Accessibility
The features meet WCAG 2.2 AA in the light and the dark themes of Starlight, with the default style settings. The known exceptions are the fade of four features and the dimmed open lines of hidden lines, which the contrast section explains. Line states with their names turned off are one more. This page lists what each feature does for readers who use a keyboard or a screen reader, or who ask for reduced motion. It also says where colour is part of the meaning, and what else carries that meaning.
Rules for every feature
Section titled “Rules for every feature”These rules apply to all features, so the sections below do not repeat them:
- Every control is a native
button,a,inputorselectelement. You can reach it with the Tab key and use it with Enter or Space. - Every control shows a focus ring in
codeblocks.focusRingwhen it has keyboard focus. The ring has 3:1 contrast or more on the code background. The code area of a block, which takes focus when it scrolls, uses the same ring. - In forced colours mode, markers keep their outline and the current step keeps its highlight.
- A block with controls in its title bar has the title as its accessible name. Without a title, the name is “Code block”. The labels of the controls are not part of the name.
- A change that happens on hover over a control also happens on keyboard focus, or when the reader clicks the control.
- Escape closes an open popover or hover card.
- When the reader’s system asks for reduced motion, every transition and animation of the plugin stops, and so does the transition of the copy button. The change still happens, at once, and pages jump instead of scrolling smoothly.
- The copy button and a manual selection give the code only. Messages, markers, notes, line numbers and glyphs are not part of the copied text.
- The copy button includes hidden lines. A manual selection includes only the hidden lines that are open.
- Code stays in the page for screen readers, also when it is blurred or faded. Hidden lines and the collapsed lines of expandable blocks are the exceptions: screen readers do not read them until the reader opens them.
- Controls do not print. An expandable block prints in full, and hidden lines stay hidden.
Contrast
Section titled “Contrast”The default colours come from the Expressive Code theme. The plugin makes each one lighter or darker until it meets its target on the code background of that theme. Unit tests check the targets with the default themes of Starlight and Expressive Code. They also check Dracula, Solarized Light, One Dark Pro, Catppuccin Latte, Nord and Min Light.
| Element | Target |
|---|---|
| Text, such as messages, output, notes, line numbers and prompts | 4.5:1 |
| Code on the tint of a line or a changed word | 4.5:1 |
| Messages of line states | 5:1 |
| Bars, underlines, markers, badges and focus rings | 3:1 |
Four features fade lines or text on purpose: focus, code mentions, scrollycoding and expandable blocks. A collapsed expandable block fades its last visible lines. While it is faded, the text is below 4.5:1. This is a known exception to WCAG 1.4.3, and the plugin limits it in these ways:
- The faded text stays in the page. Screen readers read it the same as the other text, and a copy gives it in full.
- The reader can always bring the text back to full contrast. For focus, hover over the block or move keyboard focus into it. For code mentions, the fade lasts only while a mention link has hover or focus. For scrollycoding, scroll to the step, or move focus into the sticky block. For expandable blocks, click the button under the code.
- The fade does not hide information. It shows where to look.
Some tints are on known lines, such as a line state or a word-level diff. There, the plugin changes each syntax colour that falls below 4.5:1 on the tint. Expressive Code does the same for its own marked lines. A tint that any line can get, such as a permalink target, is light enough for every syntax colour of the default themes.
Hidden lines that show are dimmed to 75% opacity, so some syntax colours fall below 4.5:1, but stay at 3:1 or more.
If your site must meet 4.5:1 in every state, set codeblocksFocus.opacity, codeblocksMentions.fadeOpacity and codeblocksHiddenLines.openOpacity to 1 and codeblocksFocus.blur to 0px in the style settings. Do not use scrollycoding or expandable blocks on that site.
If you change a colour in the style settings, check its contrast in both themes.
Comment notation
Section titled “Comment notation”The plugin reads directives at build time and removes them from the page. Readers get the result of each directive, as the sections for each feature describe.
Explain code
Section titled “Explain code”Annotations
Section titled “Annotations”- Each marker is a button with the label “Annotation N”. Screen readers read the note after its marker. The number at the start of the note is hidden from screen readers, so they do not read it twice.
- The note fades in and out. With reduced motion, it opens and closes at once.
- Escape, or a click outside the markers and the notes, closes every open note.
- A hover note stays while the mouse cursor is on the marker or the note, and Escape closes it. Touch screens and the keyboard open a note only on a tap, or on Enter on the marker.
- When the page prints, the notes print as a numbered list under the block.
Footnotes
Section titled “Footnotes”- Each badge is a link with the label “Footnote N”, and its note is its accessible description. The number in front of each note is a link back to its line, with the label “Footnote N, for line L”.
- A badge moves keyboard focus to its note, and the number moves focus back to the badge.
- A badge that gets keyboard focus under a sticky list scrolls into view above the list.
- The number links are 24 by 24 pixels or more.
- A selected line and its note get a bar as well as a tint, so the highlight does not rely on colour.
- Hover highlights only. It shows nothing that a click does not show. A tap, or Enter on the keyboard, works as a click.
- Under reduced motion, the page jumps to the note or the line.
Inline callouts
Section titled “Inline callouts”- The bubble has the
noterole. Screen readers read it before its line. - A bubble has no animation and no control.
Side annotations
Section titled “Side annotations”- Keyboard focus on a note highlights its line, the same as hover. The note shows a focus ring.
- The numbers on the lines are hidden from screen readers. Screen readers read the notes as a list after the code.
Scrollycoding
Section titled “Scrollycoding”- Screen readers read the steps in order, then the code block. On a narrow screen, each step has its own copy of the block. When the code changes between steps, the step where each version starts also has that version as text for screen readers.
- The mouse cursor does not clear the focus of the sticky block. Move keyboard focus into the block to make every line sharp.
- Under reduced motion, the focus and the fade of the steps change at once.
Code walkthrough
Section titled “Code walkthrough”- The numbered steps and the Previous and Next buttons under the block are buttons. The left and right arrow keys go to the previous and the next step when a numbered step has focus.
- The row under the block also shows the step count in text, such as “Step 2 of 3”.
- Screen readers announce the number and the label of the new step. The current step has
aria-current. - The green tint on new lines is a visual extra only. It fades out, and it gives no information that the code does not give.
- Under reduced motion, the code changes without the animation, and new lines get no tint.
Draw attention
Section titled “Draw attention”- The code area takes keyboard focus when the block has focused lines. Focus in the code area makes every line clear.
- Screen readers read every line, because the blurred lines stay in the page.
- The blur and the fade are the only differences between focused lines and other lines. They show where to look, and hide no information.
Line states
Section titled “Line states”- Each state line has a tint and a bar on the left edge. A message starts with the name of the state in bold, so colour is not the only mark.
- With
lineStates.prefixoff, orlineStates.prefix=falseon a block, the names do not show on screen. The tint is then the only visible mark of the state, which is an exception to WCAG 1.4.1. Screen readers still hear the name. - Screen readers hear the name of the state before the line, for example “Error:”, and then the message after the code.
Code mentions
Section titled “Code mentions”- Keyboard focus on a mention link highlights its lines, the same as hover.
- Screen readers read the tagged lines as the description of the link.
- Under reduced motion, the fade and the scroll happen at once.
Make code easier to read
Section titled “Make code easier to read”Hidden lines
Section titled “Hidden lines”- Each marker is a button with
aria-expanded. The title bar button also hasaria-expanded, and its label changes from Show to Hide. Both havearia-controls, which names the lines they show. - The marker text gives the number of lines, such as “3 hidden lines”, so readers know what the button opens.
- Hidden lines that show are dimmed to 75% opacity. Some syntax colours then have less than 4.5:1 contrast, but at least 3:1. To turn the dimming off, set the
codeblocksHiddenLines.openOpacitystyle setting to1.
Expandable blocks
Section titled “Expandable blocks”- The button under the code says how many lines it shows, such as “Show all 24 lines”. It has
aria-expanded. - The collapsed lines use
hidden="until-found", so Find in the browser still finds text in them, and the block expands to show the match. - The last lines that show fade into the background while the block is collapsed, so they are below 4.5:1. The contrast section explains this exception.
- Screen readers do not read the collapsed lines. They read the lines that show, then the button. When the reader clicks the button, or Find in the browser opens the block, all the lines are read.
Visible whitespace
Section titled “Visible whitespace”The glyphs are hidden from screen readers, which read the same code as in a block without the attribute.
Colourised brackets
Section titled “Colourised brackets”- The colours are a reading aid. The brackets themselves stay in the code, so readers who cannot tell the colours apart lose no information.
- Hover over a bracket to outline it and its partner, which shows the pair by shape.
- Brackets cannot take keyboard focus. With caret browsing (F7 in most browsers), move the caret onto a bracket to get the same outline.
Inline code highlighting
Section titled “Inline code highlighting”The highlighted code is a normal code element, with the same characters as before. The token colours have the same contrast correction as the code blocks.
Word-level diff
Section titled “Word-level diff”- The line markers of Expressive Code show which lines are removed and which are added. The changed words get a stronger tint on top of them.
- A changed word has a bar under it, and a removed word also has a line through it. Colour is not the only mark.
- Each changed word has the
insertionor thedeletionrole. Screen readers that support these roles announce the change. All screen readers get the removed line and the added line in full, so a reader can compare the two.
Link code
Section titled “Link code”Code links
Section titled “Code links”- A link is a normal link with an underline, so it does not rely on colour. It keeps its syntax colours.
- Readers reach the link with the Tab key.
- A link with a description shows the API links card on hover and on focus. Screen readers get the same text from the link’s
aria-description.
API auto-linking
Section titled “API auto-linking”- A link has a dotted underline. On hover and on focus, the underline is solid.
- The hover card opens at once on keyboard focus. Escape closes it.
- Screen readers get the text of the card as the description of the link.
Line permalinks
Section titled “Line permalinks”- The line numbers are links. Enter highlights a line, and Shift with Enter highlights a range.
- Screen readers announce each number as “Link to line N”. Highlighted numbers have
aria-current. - A highlighted line gets a bar as well as a tint.
Adapt to the reader
Section titled “Adapt to the reader”Code tabs
Section titled “Code tabs”- The tabs are a tab list with the accessible name “Variant”. Only the selected tab is in the tab order. The arrow keys, Home and End select a tab.
- The menu is a native
selectelement with the accessible name “Variant”. - Keyboard focus moves to the same tab or the menu in the variant that shows.
- The language icon in the menu is decorative and hidden from screen readers. The menu entries name the variants.
Fill-in placeholders
Section titled “Fill-in placeholders”- Each field is a native
input. Its accessible name is its placeholder text. - A field prints as its text, without the border.
- Escape in a field clears its value.
Copy and run
Section titled “Copy and run”Smart shell copy
Section titled “Smart shell copy”- Screen readers read the prompts, the commands and the output as they are on the page.
- The Copy commands button is a native
buttonwith a visible label that says what it copies. After a copy, a live region announces “Copied”. - The copy button copies the whole block, as in every other block.
- Output lines are muted and have no syntax colours. Their position after a command also marks them as output.
- In a Python session, the
>>>and...prompts stay on the page and are read with the commands.
Open in playground
Section titled “Open in playground”- The button is a link, or a form button for playgrounds that take a post.
- Screen readers announce “opens in a new tab” after the label.
Run code
Section titled “Run code”- The Run code button keeps keyboard focus during a run, so readers can run the code again with Enter or Space. While code runs, the button has
aria-disabled. - The output panel is a polite live region. Screen readers announce the loading message and the output when they change.
- Standard error has a bar at its start and a hidden “Error:” prefix for screen readers, as well as its colour.
- The Run code button does not print.