Accessibility
An asset download is a list of asset links — each row a single native <a> naming the file it points at — so it has no dedicated WAI-ARIA interaction pattern; it leans on native link and list semantics, and meets WCAG 2.1 AA. Accessibility hinges on two things: the link taking its accessible name from its visible text, and that name saying which of the two things the row does. One row opens the asset in a new tab; the next saves it. Same markup, same single tab stop, different outcome — and the trailing glyph that separates them for a sighted user is decorative, so a screen-reader user has only the name to go on.
For designers
Section titled “For designers”- The label, file size, and the trailing glyph — including their hover and focus states — must maintain accessible contrast on every supported surface. The file size uses a subtler foreground than the label, so check it specifically rather than assuming the label’s pass carries over. Source: WCAG 1.4.3 Contrast (Minimum).
- Never rely on colour to say what a row does — the visible label already names the action in text; keep it that way. The label is underlined, so its link affordance doesn’t rest on colour either. The two glyphs differ in shape, not colour (they take the link’s colour, which is the same on both rows), and the file extension in the label says the same thing in text — so the preview-versus-save distinction never rests on colour alone. Draw the two shapes distinctly enough that the difference survives a small glyph. Source: WCAG 1.4.1 Use of Color.
- Focus styles must stay visible, meet contrast requirements on every supported surface, and be clearly distinguishable from hover. The focus ring wraps the filename and its trailing glyph together, since both are inside the link — leave room around the row for it to draw. Source: WCAG 2.4.7 Focus Visible.
- One row is one target. The link — the filename plus its trailing glyph — is a single hit area, which clears the minimum target size comfortably at every size; there is no second control to keep clear of. Source: WCAG 2.5.8 Target Size (Minimum).
- Use semantic colour tokens (e.g.
foreground/neutral/default) rather than fixed values, so the component adapts correctly across themes and surfaces.
For developers
Section titled “For developers”Render the assets as a native list — a <ul>, or an <ol> when the order is meaningful — of rows, each row a single <a>. The browser’s native link and download handling carries keyboard activation, Open in New Tab, and Save Link As for free, so don’t intercept clicks with bespoke JavaScript download logic that would break them. Keep the label text self-describing — the filename with its extension — rather than reaching for an aria-label.
The file type picks the element, the element has to match what actually happens, and the glyph has to match both:
- Opens in a new tab (a PDF the browser renders) —
<a target="_blank" rel="noopener noreferrer">closing withicon-external-link. The new tab is a context change, so the link must announce it. Append a.sr-onlyspan to the label —<span class="sr-only">(opens in a new tab)</span>— rather than overwriting the whole name with anaria-label. - Saves the file (every other format) —
<a download>closing withicon-download. The file saves and the tab doesn’t move, so there is nothing to announce beyond the filename, and nothing to append.
Never ship the announcement without the behaviour, or the behaviour without the announcement. A row that says “opens in a new tab” and then downloads is worse than one that says nothing. The glyph is under the same rule for sighted users — it is the only thing on screen that distinguishes the two rows, so a mismatched shape is a lie told in pictures.
The glyph is decorative — mark it aria-hidden="true". It should never get an <a> of its own: a glyph that leads where the label leads adds a tab stop and a second announcement of a destination the user has already been offered, and nesting one link inside another is invalid HTML besides. One row, one link, one tab stop — and the row order is the tab order, whichever behaviour each row carries.
Where the glyph sits in the DOM is a visual-design decision, not an accessibility one, since it is neither focusable nor named: it is the Link’s own trailing icon, which is what makes it take the link’s colour and states. Don’t wrap the row — link and size — in an outer <a> to “make it all clickable”; that would pull the file size into the accessible name.
Don’t wrap the <ul> / <ol> in a landmark unless the surrounding page genuinely needs one; the list is content, not navigation. The per-element roles, names, and states are in the Labelling elements section below.
Labelling elements
Section titled “Labelling elements”Give every element the role, name, and state assistive technology needs.
Row link
A native <a> that takes its accessible name from its visible text — the filename with its extension, so a screen reader announces “Download brochure.pdf”. Wrap that text in a <span>; the trailing glyph inside the link is hidden and adds nothing to the name. The file size sits outside the link and isn’t part of it either, so put the size in the label only if it genuinely needs announcing. Only add an aria-label when a surface needs a different announcement, and never leave the name to a glyph alone.
On a row that opens a new tab, the name carries one thing more: the new tab. Append a .sr-only span rather than overwriting the whole name with an aria-label.
List
The wrapping <ul> (or <ol>) carries role="list" explicitly, so the count still announces when list-style is removed — Safari with VoiceOver drops the implicit list role otherwise. It needs no aria-label: the rows are self-describing, so a redundant list label only adds noise.
Trailing glyph
aria-hidden="true" on both shapes: it is a visual cue, not a control. It is the sighted user’s read on what the row does — external-link for a new tab, download for a save — and the visible label already names the action in text.
Hiding it does not mean the cue is dropped for everyone else. On a new-tab row the warning belongs to the link’s name instead, where assistive tech will actually reach it; the .sr-only text is a sibling of the glyph, not a label for it. Never mark the glyph aria-hidden="false" or give it a title to compensate; that announces a decoration rather than the link. And never let the glyph be the only place the behaviour is stated — a hidden shape says nothing to a screen reader.
Source: WCAG 4.1.2 Name, Role, Value.