The design system supports highly accessible forms with robust validation display options. Explore validation examples in action in this submission metadata mockup.
A field is a label (for example, Email address), a control (for example, input field, checkbox), optional help text, and a slot for an error, warning or info message. That order never changes. Labels sit above their controls, and related fields group by spacing rather than boxes.
Field widths are set by the type of control, on the assumption that the element already tells you about how much someone will type. If you need to override a default width, two classes are available (see Field width).
<form class="ds-form">
<div class="ds-field">
<label class="ds-label" for="f-email">Email address</label>
<input class="ds-input" id="f-email" type="email" required aria-required="true" aria-describedby="f-email-hint" value="e.vasquez@example.edu">
<p class="ds-hint" id="f-email-hint">Used only to confirm your submission.</p>
</div>
<div class="ds-field">
<label class="ds-label" for="f-cat">Primary category</label>
<select class="ds-input" id="f-cat" required aria-required="true">
<option>hep-th — High Energy Physics – Theory</option>
<option>math.CO — Combinatorics</option>
<option>physics.optics — Optics</option>
</select>
</div>
<div class="ds-field ds-field--short">
<label class="ds-label" for="f-orcid">ORCID iD<span class="label-optional">(optional)</span></label>
<input class="ds-input" id="f-orcid" type="text" placeholder="0000-0000-0000-0000">
</div>
<label class="ds-check">
<input type="checkbox" checked>
<span>Email me when the moderators respond</span>
</label>
<div class="ds-btn-group ds-btn-group--end">
<button class="ds-btn ds-btn-text" type="button">Cancel</button>
<button class="ds-btn ds-btn-primary" type="submit">Save changes</button>
</div>
</form>
<form> stacks every field.for.(optional). Required fields have no marker; they take
required and aria-required="true" on the control instead.input, select and
textarea alike.id and name it in the
control's aria-describedby. Say what good input looks like before
someone gets it wrong.<label> holding the native control and
a <span> with the text. The whole label is the target.A compact form is for a very short form, such as search: one to three controls and a button, on one row.
Two joined controls and a button
<form class="ds-form--compact" method="get" action="forms.html#compact-form" role="search" aria-label="Search arXiv">
<div class="ds-input-group">
<label class="is-sr-only" for="cf-query">Search term</label>
<input class="ds-input" id="cf-query" name="query" type="search" value="gravitational lensing">
<label class="is-sr-only" for="cf-field">Field to search</label>
<select class="ds-input" id="cf-field" name="searchtype"><option>All fields</option><option>Title</option><option>Author(s)</option><option>Abstract</option></select>
</div>
<button class="ds-btn ds-btn-primary" type="submit">Search</button>
<label class="ds-check"><input type="checkbox" name="abstracts" value="show" checked><span>Show abstracts</span></label>
<div class="ds-filter"><label for="cf-order">Sort by</label><select id="cf-order" name="order"><option>Newest announced</option><option>Relevance</option></select></div>
<div class="ds-filter"><label for="cf-size">Per page</label><select id="cf-size" name="size"><option>25</option><option selected>50</option><option>100</option></select></div>
<a href="forms.html#compact-form">Advanced search</a>
</form>
<form>. Puts the controls and the button on one row. Anything
after the button goes on the line below: a message first, then secondary options, such as
Show abstracts and the settings for the results (.ds-filter selects for order
and results per page). A link to another page, such as Advanced search, sits at the end of
that line.<label> when a heading or the button already says what the
form does. The label is read out and not shown.One control, with a visible label
<form class="ds-form--compact" method="get" action="forms.html#compact-form">
<label class="ds-label" for="cf-id">arXiv identifier</label>
<input class="ds-input" id="cf-id" name="id" type="text" value="2610.00999">
<button class="ds-btn ds-btn-primary" type="submit">Open paper</button>
</form>
With an error
<form class="ds-form--compact" method="get" action="forms.html#compact-form" role="search" aria-label="Search arXiv">
<div class="ds-input-group">
<label class="is-sr-only" for="cf-query-2">Search term</label>
<input class="ds-input is-invalid" id="cf-query-2" name="query" type="search" value="*lens" aria-invalid="true" aria-describedby="cf-query-2-err">
<label class="is-sr-only" for="cf-field-2">Field to search</label>
<select class="ds-input" id="cf-field-2" name="searchtype"><option>Title</option><option>All fields</option></select>
</div>
<button class="ds-btn ds-btn-primary" type="submit">Search</button>
<p class="field-error" id="cf-query-2-err">A search cannot start with a wildcard (? or *). Remove it and search again.</p>
<label class="ds-check"><input type="checkbox" name="abstracts" value="show" checked><span>Show abstracts</span></label>
<a href="forms.html#compact-form">Advanced search</a>
</form>
.is-sr-only only when a
heading or the button says what the form does. Never use a placeholder as the label.Fields can be in three possible states: normal, validated, or disabled. Validation styles change based on the error disposition, that is, its severity.
One sentence is enough.
Locked once a paper is announced.
Remove the HTML markup <br>.
This does not look like an ACM code. Expected form: F.2.2.
Extra spaces were removed. Please confirm it reads correctly.
<div class="ds-field ds-field--full">
<label class="ds-label" for="v3">Abstract</label>
<input class="ds-input is-invalid" id="v3" type="text" aria-invalid="true" aria-describedby="v3-m" value="<br>Our analysis introduces">
<p class="field-error" id="v3-m">Remove the HTML markup <code><br></code>.</p>
</div>
<div class="ds-field ds-field--full">
<label class="ds-label" for="v4">ACM classification<span class="label-optional">(optional)</span></label>
<input class="ds-input is-warning" id="v4" type="text" aria-describedby="v4-m" value="s 25">
<p class="field-warning" id="v4-m">This does not look like an ACM code. Expected form: <code>F.2.2</code>.</p>
</div>
<div class="ds-field ds-field--full">
<label class="ds-label" for="m1">Abstract</label>
<textarea class="ds-input is-invalid" id="m1" rows="3" aria-invalid="true" aria-describedby="m1-m">Abstract: We study the convergence of stochastic gradient methods.<br>Data at https://example.org/sgd.</textarea>
<ol class="field-messages" id="m1-m">
<li class="field-error">Do not begin the abstract with the word “Abstract”.</li>
<li class="field-error">Remove the HTML markup <code><br></code>.</li>
<li class="field-warning">Font commands are not processed and appear literally.</li>
<li class="field-warning">Add a space between a URL and the punctuation after it.</li>
</ol>
</div>
aria-invalid="true".aria-invalid: the value is
accepted.aria-invalid.id that the control's aria-describedby names. Keep it
hidden until it applies.<ol> when one field has several problems. Each
<li> takes its own tier class, and the list takes the id
the control points at. The control takes the class of the worst tier in the list.aria-disabled="true", which looks the same; on a field, pair it with
readonly, because aria-disabled alone does not stop typing. See
the accessibility essentials.Example: F.2.2; I.2.7
This does not look like an ACM code. Expected form: F.2.2.
<div class="ds-field ds-field--full">
<label class="ds-label" for="hb">ACM classification<span class="label-optional">(optional)</span></label>
<input class="ds-input is-warning" id="hb" type="text" aria-describedby="hb-h hb-m" value="s 25">
<p class="ds-hint" id="hb-h">Example: <code>F.2.2; I.2.7</code></p>
<p class="field-warning" id="hb-m">This does not look like an ACM code. Expected form: <code>F.2.2</code>.</p>
</div>
Repeat the problems below the page heading using an alert component. Errors are grouped into alerts by disposition (error, warning, info) and are not mixed. Each alert holds an ordered list of items. See Alerts for full documentation. Below is an example of using an alert specifically for validation.
2 blocking errors
Blocking errors must be corrected before you can continue. Select an item to jump to that field.
<div class="ds-alert ds-alert--error" role="alert">
<svg class="ds-alert-icon" viewBox="0 0 24 24" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" fill="none" aria-hidden="true">
<circle cx="12" cy="12" r="10"/>
<path d="m15 9-6 6"/>
<path d="m9 9 6 6"/>
</svg>
<div class="ds-alert-content">
<p class="ds-alert-title">2 blocking errors</p>
<p>Blocking errors must be corrected before you can continue. Select an item to jump to that field.</p>
<ol>
<li><a href="#m1">Abstract — do not begin with the word “Abstract”</a></li>
<li><a href="#m1">Abstract — remove the HTML markup <code><br></code></a></li>
</ol>
</div>
</div>
.ds-alert--warning, and an automatic change is a bare .ds-alert, which is the info state.
Takes role="alert" for error and warning, role="status" for
info. See Alerts for the four severities and their markup.<a> linking to its field. Rows repeat
the field name even when several name the same field: without it the reader cannot tell
whether three problems mean one field to visit or three. Derive every count and every row
from the field results; a summary written by hand can disagree with the fields it
describes, and eventually will.arXiv's general style rules all apply: no contractions, no jargon, keep it brief. See STYLE.md for full guidelines. In addition, these rules are specific to validation messages and will help users take action quickly.
| Too passive | More actionable |
|---|---|
| Invalid email. | Enter an email address, like name@university.edu. |
| This field is required. | Give a reason before saving. |
| Password does not meet requirements. | Use at least 12 characters. |
| Check this field. | You may continue, but your submission may be delayed. |
| Title was invalid. | Extra spaces were removed. Please confirm it reads correctly. |
These guidelines are specific to the severity level of each validation message:
A boolean that applies the moment it is flipped. If the change needs a Save step then use a checkbox, not a switch.
<label class="ds-switch">
<input type="checkbox" role="switch" checked>
<span class="ds-switch-track"><span class="ds-switch-thumb"></span></span>
<span class="ds-switch-label">Email digest</span>
</label>
<label> that wraps the whole control. The visible text goes
inside it, because that is what names the switch; a switch with only a heading beside it
has no accessible name..ds-internal).
The Strong Border edge and the Repository Brown thumb stay the same in both states and
both themes.An exclusive choice rendered as adjacent buttons, for a decision where
every option should stay visible. Two to four options; past that it is a <select>
or a radio group, because a row of six buttons stops being scannable and starts being a wall.
Nothing is selected until something is selected: the undecided example is the resting state of a decision nobody has made yet, and it is a real state, not a bug to hide by pre-selecting the middle option.
<fieldset class="ds-seg">
<legend class="is-sr-only">Positive example</legend>
<button aria-pressed="true" type="button" class="ds-seg-btn ds-seg-btn--positive is-active">Author</button>
<button aria-pressed="false" type="button" class="ds-seg-btn">Proxy</button>
<button aria-pressed="false" type="button" class="ds-seg-btn ds-seg-btn--negative">No</button>
</fieldset>
<fieldset> holding two to four .ds-seg-btn..is-sr-only
when the surrounding text already says it; hide it, do not omit it.<button type="button">. Buttons and not a radio
group on purpose: this is for a decision that acts when clicked. A choice that is part
of a form that gets submitted is a radio group..ds-seg-btn is informational. They take the success, info and error tokens and mean
the same thing those mean everywhere else; they are not free colours to pick from, and
they flip for dark mode with the rest of the status palette.aria-pressed="true"; every other option
has aria-pressed="false", so a screen reader says which one is selected.
Set by script when an option is clicked, and removed from the others; the stylesheet
gives the buttons no behaviour.A short explanation attached to a control, on hover and on focus. The lighter half of the popover family: no title, no close button, and never content that exists nowhere else.
Not for anything required: if the reader must have it to fill the field in, it is help text under the control, not a tooltip, which is for the person who stops and wonders.
Correct the 2 blocking errors to continue.
Hover over or tab to the question mark or the first Continue button.
<!-- Label with a tooltip -->
<div class="ds-field">
<div class="ds-label-row">
<label class="ds-label" for="f-doi">DOI</label>
<span class="ds-tooltip-host">
<button type="button" class="ds-btn ds-btn-text ds-btn-icon" aria-describedby="tip-doi">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
<circle cx="12" cy="12" r="10"/>
<path d="M9.1 9a3 3 0 0 1 5.8 1c0 2-3 3-3 3"/>
<path d="M12 17h.01"/>
</svg>
<span class="is-sr-only">What is a DOI?</span>
</button>
<span class="ds-tooltip" id="tip-doi" role="tooltip">
<span class="ds-tooltip-body">If the paper has been published, the publisher's DOI. Leave it empty otherwise; arXiv mints its own.</span>
</span>
</span>
</div>
<input class="ds-input" id="f-doi" type="text" placeholder="10.1000/example">
</div>
<!-- Disabled action, reason in a tooltip -->
<div class="ds-btn-group ds-btn-group--end">
<button type="button" class="ds-btn ds-btn-text">Cancel</button>
<span class="ds-tooltip-host">
<button type="button" class="ds-btn ds-btn-primary" aria-disabled="true" aria-describedby="tip-continue">Continue</button>
<span class="ds-tooltip ds-tooltip--end" id="tip-continue" role="tooltip">
<span class="ds-tooltip-body">Correct the 2 blocking errors to continue.</span>
</span>
</span>
</div>
<!-- Disabled action, reason as help text -->
<div class="ds-btn-group ds-btn-group--end">
<button type="button" class="ds-btn ds-btn-text">Cancel</button>
<button type="button" class="ds-btn ds-btn-primary" aria-disabled="true" aria-describedby="continue-why">Continue</button>
</div>
<p class="ds-hint" id="continue-why">Correct the 2 blocking errors to continue.</p>
<label>, never inside it: a label forwards clicks to the control it
names, so a button nested in one is a button whose clicks land somewhere else.id. The tooltip describes the
control and is never its accessible name: a name that only appears on hover is a name
most people never get, so an icon trigger has an .is-sr-only label of its
own, as above..ds-hint text instead, named in the button's
aria-describedby. Use aria-disabled, not
disabled: a disabled button cannot take focus, so a keyboard user would never
reach the reason. The script must ignore clicks while it is set.role="tooltip" and the id. It opens
downward and toward the inline end, on hover and on focus, with no JavaScript. Escape
must close it without moving focus, which CSS cannot do: a listener sets
hidden on it, and clears it when the pointer or focus arrives again.
tooltip.js does this, once per page.tooltip.js slides the bubble back onto the screen when it
would cross an edge. Both directions use logical properties, so both are correct in a
right-to-left script.<label for> on
every control, pointing at the control's id. Hide it with
.is-sr-only when there is no room for it; do not leave it out.(optional) inside the visible label of an optional field. Put
required and aria-required="true" on every required control: required
fields have no visual marker, so this pair is the only signal a screen reader gets. Never put
either word in aria-label or title..ds-hint and each message an id, and list them in the control's
aria-describedby. Without that a screen reader announces the field and nothing
else.aria-invalid for errors only. Set
aria-invalid="true" on a control in the error tier and remove it when the error
clears. A warning is not invalid, and an info message is not a problem at all.input
event so the error clears the moment it is fixed. When submission fails, move focus to the
first invalid field.role="alert" for errors and warnings, so a screen reader announces it at once,
and every row in it links to its field.aria-disabled to disabled for a control that could
explain itself. disabled leaves the tab order entirely, so a user who
cannot proceed finds nothing there and no reason why. The two must look identical.Default field widths are set to follow the expected amount of content and are an important clue for users as to what is expected in any given field. Widths will generally not need any manual adjustment but two overrides exist just in case: .ds-field--short and .ds-field--full.
<form class="ds-form">
<div class="ds-field ds-field--short">
<label class="ds-label" for="w-year">Year</label>
<input class="ds-input" id="w-year" type="text" inputmode="numeric" value="2026">
</div>
<div class="ds-field">
<label class="ds-label" for="w-date">Date</label>
<input class="ds-input" id="w-date" type="date" value="2026-09-23">
</div>
<div class="ds-field ds-field--full">
<label class="ds-label" for="w-title">Title</label>
<input class="ds-input" id="w-title" type="text" value="Heavy-tailed gradients in deep networks: a study of convergence under stochastic optimisation">
</div>
</form>
internal-tools.css: caps the control at
240px, 360px or 480px. Internal screens are dense enough to need field widths chosen
rather than inferred, so on an internal form a person picks the size instead of the
input type picking it. Use the pair above on public pages and this trio on
internal pages, and do not mix them on one form.What each tier means for the person filling the form in.
| Disposition | Can they continue? | What it means |
|---|---|---|
| Error | No | Blocking. The value is not acceptable and must be corrected. |
| Warning | Yes | Accepted, but the value may be suspect or cause issues later on. |
| Info | Yes | When used in validation it means arXiv changed the value automatically (for example, normalising whitespace). Nothing is wrong; the author is being informed. |