# Reading order that doesn't match the layout

Source: https://easeweb.dev/learn/reading-order
Topics: Page structure > Reading order matches the visual order (WCAG 1.3.2)
The fix: Make the order in the code carry the meaning. Either write the source in the order it is read, each item's name before its amount with the row laid out by flexbox (justify-content: space-between) instead of a float, and each delivery option in one row of a table, or, where the markup can't move, add visually hidden words that tie each value to what it belongs to.
Test with: screen reader, styles off
References: https://www.w3.org/WAI/WCAG22/Understanding/meaningful-sequence.html https://www.w3.org/WAI/WCAG22/Techniques/general/G57 https://www.w3.org/WAI/WCAG22/Techniques/css/C27 https://www.w3.org/WAI/WCAG22/Techniques/failures/F1 https://www.w3.org/WAI/WCAG22/Techniques/failures/F49 https://accessibility.psu.edu/css/readorderhtml/ https://www.w3.org/TR/css-display-3/#order-accessibility https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Properties/reading-flow

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.

## What happens

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.

## Why it fails

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.

## Who is affected

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.

## What automation and AI miss

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.

## Before: amounts floated right, written before their items

```html
<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>
```

```css
.amount {
  float: right;
}
```

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.

## Before: delivery options written column by column

```html
<div class="delivery">
  <div class="col">
    <p>Standard</p>
    <p>Express</p>
  </div>
  <div class="col">
    <p>$4</p>
    <p>$9</p>
  </div>
  <div class="col">
    <p>3–5 days</p>
    <p>Next day</p>
  </div>
</div>
```

```css
.delivery {
  display: flex;
  gap: 24px;
}
```

The columns line up, so the box looks like two rows. The markup holds no rows, only columns, so it is read as two names, then two prices, then two times.

## After: 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>
```

```css
.summary li {
  display: flex;
  justify-content: space-between;
  gap: 16px;
}
```

```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>
```

```css
.delivery th {
  text-align: start;
}
```

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.

## After: hidden words that tie each value to its owner

```html
<li><span class="amount"><span class="visually-hidden">Canvas tote: </span>$28</span> Canvas tote</li>
<li><span class="amount"><span class="visually-hidden">Linen tote: </span>$35</span> Linen tote</li>
<li><span class="amount"><span class="visually-hidden">Delivery: </span>$4</span> Delivery</li>
<li class="total"><span class="amount"><span class="visually-hidden">Total: </span>$67</span> Total</li>
```

```html
<div class="col">
  <p><span class="visually-hidden">Standard: </span>$4</p>
  <p><span class="visually-hidden">Express: </span>$9</p>
</div>
<div class="col">
  <p><span class="visually-hidden">Standard: </span>3–5 days</p>
  <p><span class="visually-hidden">Express: </span>Next day</p>
</div>
```

```css
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}
```

The order is unchanged, but each value now says what it belongs to: "Canvas tote: $28", "Standard: $4". Once no value depends on its place to be understood, the sequence no longer carries the meaning, and 1.3.2 is met. It is the smallest change when a theme or a platform fixes the markup. It holds only while every value keeps its label: a new item, a third delivery option or a translated name needs its hidden words written too. The listener still hears each name twice and each amount before its name, and the read-aloud highlight still jumps from one end of the row to the other.

## Which option to use

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.

## Implementation decisions

`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` (or `inline-end`) on an element written before the text beside it, the pattern in this guide.
- `flex-direction: row-reverse` and `column-reverse`, or `order`, which flip or reorder a row or column; a float moved to flexbox often lands here.
- Grid placement with `grid-template-areas`, `grid-row` or `grid-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](/learn/focus-order).

## Verify the fix

1. With a screen reader, read the area from the top with the down arrow, without jumping: every amount, value or step comes after the name it belongs to.
2. Turn page styles off (in Firefox: View, Page Style, No Style) or open a reader view, and check that the content still reads in an order that makes sense.
3. Search the CSS for `float: right`, `order`, `row-reverse`, `column-reverse`, grid placement and absolute positioning near text, at every breakpoint, and confirm none of them draws a value on the other side of the name or label it belongs to.
4. Find data drawn in rows and columns, and confirm it is written row by row, as a table or one element per row, or that each value names its row.

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.

---

Get this guide in your coding agent: MCP https://easeweb.dev/mcp (setup at https://easeweb.dev/services/developers)
