Search topics
Reading order that doesn't match the layout
An order summary writes each amount before its item and floats it to the right, and a delivery box is written column by column, so a screen reader pairs the wrong words.
<li><span class="amount">$28</span> Canvas tote</li> The problem
Screen reader and read-aloud users hear each amount before its item and the delivery options as loose columns, so they pair prices with the wrong items.
A bag shop's checkout shows an order summary. Each row has a name on the left and an amount on the right: Canvas tote, $28; Linen tote, $35; Delivery, $4; and in bold, Total, $67. A screen reader user reads the summary from the top and hears "$28. Canvas tote. $35. Linen tote. $4. Delivery. $67. Total." Every amount comes before its name. Someone who expects a name and then its price pairs each name with the amount after it: the canvas tote seems to cost $35, delivery seems to cost $67, and the total has no amount at all.
Above the summary, a small box offers two kinds of delivery. On screen it reads as two rows: Standard, $4, 3–5 days, and Express, $9, next day. The screen reader reads "Standard. Express. $4. $9. 3–5 days. Next day." Which price goes with which service is left to memory.
WCAG 1.3.2 asks that when the order of content affects its meaning, a correct reading order can be determined from the code. Assistive technology reads the markup, not the pixels. In the summary, each amount is written first and pushed to the right end of its row with float: right, so the screen shows name then amount and the markup holds amount then name. The delivery box is written as three columns, names, then prices, then times, set side by side with flexbox. On screen the eye reads across the rows; in the markup there are no rows to read across. In both, the pairs that give the content its meaning, a name and its amount, exist only in the layout.
Reader views, the browser's own text-to-speech, and pages copied or printed with styles off all take the markup's order too, so the problem reaches more people than screen reader users.
3 of 10 groups affected
- No vision (affected)
- Low vision (affected)
- Colour vision (not affected)
- No hearing (not affected)
- Hard of hearing (not affected)
- No speech (not affected)
- Motor (not affected)
- Reach, strength (not affected)
- Cognitive (affected)
- Seizures (not affected)
- Without vision A screen reader reads the markup from top to bottom, so each amount arrives before its item's name and runs on from the row before it. Braille displays show the same order.
- Limited language, cognitive and learning abilities People who follow along with a read-aloud tool hear the same jumbled order, and watch its highlight jump from the amount at the right end of a row back to the name at its start.
- Limited vision Screen magnifier users who turn on speech hear the wrong pairs, and people who read in a reader view or with their own styles see each amount before its item, in the markup's order.
Every element is valid: the list has items, the amounts are spans, the box is a set of divs. No axe rule compares where text is drawn with where it sits in the markup, so a scan reports nothing. Whether a difference matters is a question about meaning: a sidebar drawn beside the main text can come before it or after it, but an amount can't come before the item it prices. A model asked to modernise the summary will often swap the float for flex-direction: row-reverse, which keeps the look and the wrong order. EaseWeb's audit flags short values floated right ahead of their text, blocks drawn in another order than their markup, and rows built from side-by-side columns, as items for a person to check.
Try it
Read the order summary the way a screen reader does, and see which amount follows which name.
The panel shows what a screen reader reads from the top. Turn on “Show reading order” to number each piece in that order. Does every amount come after its own name? Broken 2 is the delivery box, written column by column.
Broken Can you fix this?
- $28 Canvas tote
- $35 Linen tote
- $4 Delivery
- $67 Total
Reading from the top, a screen reader says:
- Order summary
- $28
- Canvas tote
- $35
- Linen tote
- $4
- Delivery
- $67
- Total
The fix
Write the source in the order it is read, or tie each value that is out of place to its owner in text.
- 1Where order carries meaning, the markup carries it: written in that order, or with each item naming what it belongs to.
- 2Data drawn in rows and columns is written row by row, as a table, or each value names its row.
- 3Check every breakpoint and read with styles off, because layouts reorder at some widths.
Amounts floated right, written before their items
<ul class="summary"> <li><span class="amount">$28</span> Canvas tote</li> <li><span class="amount">$35</span> Linen tote</li> <li><span class="amount">$4</span> Delivery</li> <li class="total"><span class="amount">$67</span> Total</li></ul>Source written in the order it is read
html
<ul class="summary"> <li><span>Canvas tote</span> <span class="amount">$28</span></li> <li><span>Linen tote</span> <span class="amount">$35</span></li> <li><span>Delivery</span> <span class="amount">$4</span></li> <li class="total"><span>Total</span> <span class="amount">$67</span></li></ul>html
<table class="delivery"> <tr> <th scope="row">Standard</th> <td>$4</td> <td>3–5 days</td> </tr> <tr> <th scope="row">Express</th> <td>$9</td> <td>Next day</td> </tr></table>Why this fix: Write the source in the order it is read
The code is written this way because of the float. A float can't sit higher than content written before it, so to keep the amount on the first line of its row whatever the name holds, the habit is to write it first; Bootstrap 3's own documentation wrote its list badges like this, and themes from that time still do. The row looks right, and a screen reader reads each amount before its name, straight after the row above. Rewriting the row with flexbox and flex-direction: row-reverse keeps the same look and the same wrong order.
Each name now comes before its amount in the markup, and flexbox pushes the amount to the right end of the row without moving it, so the summary reads "Canvas tote $28" and looks as it did. A description list (dl, with each name a dt and each amount a dd) or a table works as well. The delivery options are a table, one row per option, so they are read row by row, and a screen reader's table commands can say which service a price belongs to.
Both pass. Writing the source in reading order is recommended: it fixes the order itself, so screen readers, read-aloud tools, reader views and pages with styles off all get the same sequence the screen shows, and there is nothing extra to keep in step. It costs an edit to the summary template, swapping the float for flexbox, and turns the delivery box into a table. The hidden labels keep the markup as it is, which helps when a theme or a third-party widget can't be changed, but they repeat each name for screen reader users and leave the reading order jumbled for everyone else who follows the markup.
float: right is one of several ways to draw content away from its place in the markup. These do the same, and are worth searching for wherever a value belongs to a name or a label.
float: right(orinline-end) on an element written before the text beside it, the pattern in this guide.flex-direction: row-reverseandcolumn-reverse, ororder, which flip or reorder a row or column; a float moved to flexbox often lands here.- Grid placement with
grid-template-areas,grid-roworgrid-column, which can put any child anywhere. - Absolute positioning, which lifts a badge or a price out of the flow, often to a corner of its box.
Layouts often reorder only at one width. A row that becomes a column-reverse stack on a phone, with the amount above the name, is wrong at that width only. Check each breakpoint.
Not every difference fails. A floated photo can come before or after the paragraph it sits in, two independent cards can come in either order, and a sidebar can be read before or after the main text. What fails is a difference that changes what something means: an amount before its item, a step before the one it depends on, a label after its value. The newer reading-flow CSS property makes some browsers read and tab a flex or grid container in the order it is drawn, but it does nothing for floats and support is not yet universal, so fix the source.
Older ways of laying out columns cause the same problem. A layout table whose cells read in the wrong order when linearised fails in the same way, and so does text lined up into columns with spaces, tabs or line breaks, which reads as one jumbled line. Put real rows and columns in a table, and let CSS place everything else.
When the reordered items can take focus, as links or buttons, the same mismatch also moves Tab against the layout. That side of it is covered in focus order that jumps around the page.
Fixing this with an AI coding assistant? Get this guide as Markdown
Verify the fix
4 checks, no mouse.
Test with: Screen reader, Styles off
Record the OS, browser and assistive-technology versions, the build, the date, and the actual result of each step. Repeat after the layout or a component's styles change.
Limits
This is a starting pattern, not a guarantee for every application. It covers a short order summary and a small box of options. Drag-to-reorder lists, data grids and carousels have their own patterns and need their own checks. Whether an order changes the meaning is a judgement about the content, so read your real pages with a screen reader and with styles off.
This is a recreated teaching example, not a finding about a named client. The code is a starting pattern: test it in your own product. No completed assistive-technology test is claimed here.
References
- WCAG Understanding: meaningful-sequence (opens in a new tab)
- WCAG Technique G57 (opens in a new tab)
- WCAG Technique C27 (opens in a new tab)
- WCAG Technique F1 (opens in a new tab)
- WCAG Technique F49 (opens in a new tab)
- accessibility.psu.edu: readorderhtml (opens in a new tab)
- w3.org: css-display-3 (opens in a new tab)
- developer.mozilla.org: reading-flow (opens in a new tab)
Keep this fixed as your code changes.
Turn guides like this into rules your coding agents and CI follow, then have the result retested with a keyboard and screen readers.