{
"$type": "site.standard.document",
"content": {
"$type": "site.standard.content.markdown",
"text": "import ReadingOrderDemo1 from '../../components/demos/ReadingOrderDemo1.astro';\nimport ReadingOrderDemo2 from '../../components/demos/ReadingOrderDemo2.astro';\nimport PlaceholderImage from '../../components/PlaceholderImage.astro';\nimport FYIBlock from '../../components/FYIBlock.astro';\nimport CardDemo from '../../components/CardDemo.astro';\n\n## The Accessibility Gap: When Visual Order Betrays Logical Order\n\nFor a long time, developers have been able to put together the strangest of layouts that have come from the dark corners of the design team with very little pushback. Properties like `flex-direction`, `grid-template-areas` and the `order` property have given us a monumental amount of control when dealing with the visual presentation of the elements on the screen, and we can rearrange content to adapt to different screen sizes and user preference with little effort.\n\nSometimes however, that ability comes with a sidestep of accessibility and performance concerns, and that really isn't acceptable at all. We would warn that a layout *might* affect accessibility, but in the worst cases it can met with a shrug, depending on how much the team cares about providing accessible solutions in the first place. In my current position, accessibility concerns are met with a lot of discussion and push back to find a better path - so the prior 'shrug' example is not a reflection of how our projects are dealt with. The main challenge has been that while we can change how elements look on the screen, their true order in the DOM is the 'golden truth'. We'll go through a couple of examples to help illustrate this.\n\n### Example 1: Our card layout\n\nIf we have for example, a traditional card layout that has 3 elements; a date (the highest semantic priority), a title, and an image (the lowest semantic priority), and we've used CSS to reorder them differently (let's say; the image, then the date, then the title). We would have this;\n\n<style>{`\n .reading-order-card {\n display: flex;\n flex-direction: column;\n gap: 16px;\n border: 1px solid var(--color-dark);\n padding: 16px;\n max-width: fit-content;\n }\n\n @media (width <= 768px) {\n .reading-order-card {\n max-width: none;\n }\n }\n\n .reading-order-card date {\n order: 2;\n font-size: 10px;\n color: var(--color-dark);\n letter-spacing: 0.05em;\n text-transform: uppercase;\n }\n\n .reading-order-card span,\n .reading-order-card > a {\n order: 3;\n font-family: var(--font-base);\n font-weight: 300;\n line-height: 1.3;\n font-style: italic;\n color: var(--color-dark);\n background: none;\n &::before,\n &::after {\n content: none;\n }\n }\n\n .reading-order-card img {\n order: 1;\n }\n\n`}</style>\n\n```css\n.reading-order-card date {\n order: 2;\n}\n\n.reading-order-card span,\n.reading-order-card > a {\n order: 3;\n}\n\n.reading-order-card img {\n order: 1;\n}\n```\n\n<CardDemo ratio=\"16/5\" title=\"Card heading\" text=\"Laborum sint laborum nostrud est. Est exercitation et occaecat ut proident non voluptate et. Laborum non id voluptate tempor ea anim anim eu irure laborum velit labore ullamco. Non ipsum labore consequat adipisicing amet ut reprehenderit.\" />\n\nVisually, we've got our card layout looking like this; an image, then a date, then a title. Pretty generic stuff, but when a screen reader user traverses this layout, the order of the content is not what we would expect. The image is announced first, then the date, then the title. This is because the screen reader is reading the content in the order of the DOM, not the visual order.\n\n### Visual order vs DOM order: the gap your users fall into\n\nWhen CSS reorders content visually, keyboard and screen reader users still\nfollow the DOM. Use <span style=\"color: var(--color-dark)\">Tab</span> to navigate\nthe cards below and feel the difference.\n\n<ReadingOrderDemo1 />\n\n### Example 2: Our grid layout\n\nIf we have for example, a layout that is 3 elements (Article A, Article B and a CTA) in that order in the DOM, and we've used CSS to reorder them differently (let's say; the CTA, Article A and Article B). We would have this;\n\n<style>{`\n .reading-order-example-2 {\n display: grid;\n grid-template-columns: 1fr 1fr 1fr;\n grid-template-areas:\n \"cta article-a article-b\"\n \"article-c cta-2 article-d\";\n gap: 16px;\n }\n\n @media (width <= 768px) {\n .reading-order-example-2 {\n grid-template-columns: 1fr;\n grid-template-areas:\n \"cta\"\n \"article-a\"\n \"article-b\"\n \"article-c\"\n \"cta-2\"\n \"article-d\"\n }\n }\n\n .reading-order-example-2 .reading-order-card--1 {\n grid-area: article-a;\n }\n\n .reading-order-example-2 .reading-order-card--2 {\n grid-area: article-b;\n }\n\n .reading-order-example-2 .reading-order-card--3 {\n grid-area: cta;\n }\n\n .reading-order-example-2 .reading-order-card--4 {\n grid-area: article-c;\n }\n\n .reading-order-example-2 .reading-order-card--5 {\n grid-area: article-d;\n }\n\n .reading-order-example-2 .reading-order-card--6 {\n grid-area: cta-2;\n }\n`}</style>\n\n```css\n.reading-order-example-2 {\n display: grid;\n grid-template-columns: 1fr 1fr 1fr;\n grid-template-areas:\n \"cta article-a article-b\"\n \"article-c cta-2 article-d\"\n}\n\n.reading-order-example-2 .reading-order-card--1 {\n grid-area: article-a;\n}\n\n.reading-order-example-2 .reading-order-card--2 {\n grid-area: article-b;\n}\n\n.reading-order-example-2 .reading-order-card--3 {\n grid-area: cta;\n}\n\n.reading-order-example-2 .reading-order-card--4 {\n grid-area: article-c;\n}\n\n.reading-order-example-2 .reading-order-card--5 {\n grid-area: article-d;\n}\n\n.reading-order-example-2 .reading-order-card--6 {\n grid-area: cta-2;\n}\n\n```\n\n<div class=\"reading-order-example reading-order-example-2\">\n <article class=\"reading-order-card reading-order-card--1\">\n <date datetime=\"2025-10-30\">20th October 2025</date>\n <span>This is the card that is the first item in the DOM</span>\n <PlaceholderImage ratio=\"16 / 5\" />\n </article>\n\n <article class=\"reading-order-card reading-order-card--2\">\n <date datetime=\"2025-10-30\">20th October 2025</date>\n <span>This is the card that is the second item in the DOM</span>\n <PlaceholderImage ratio=\"16 / 5\" />\n </article>\n\n <a href=\"#\" class=\"item item--3 w-full\" tabindex=\"0\" aria-label=\"Read more about this article\">\n \t<PlaceholderImage ratio=\"16 / 10\" />\n </a>\n\n <article class=\"reading-order-card reading-order-card--4\">\n <date datetime=\"2025-10-30\">20th October 2025</date>\n <span>This is the card that is the first item in the DOM</span>\n <PlaceholderImage ratio=\"16 / 5\" />\n </article>\n\n <article class=\"reading-order-card reading-order-card--5\">\n <date datetime=\"2025-10-30\">20th October 2025</date>\n <span>This is the card that is the second item in the DOM</span>\n <PlaceholderImage ratio=\"16 / 5\" />\n </article>\n\n <a href=\"#\" class=\"item item--6 w-full\" tabindex=\"0\" aria-label=\"Read more about this article\">\n \t<PlaceholderImage ratio=\"16 / 10\" />\n </a>\n</div>\n\nWhat's the correct order for these examples? Visually they are correct - they've done what we want it to do in that specific order to appease our designer overlords. However, for a user navigating the site through the use of a screen reader or a keyboard, this might be a bit of a contradiction. Typically screen readers such as NVDA, JAWS or Voiceover traverse the DOM and read out the content as it is set there, disregarding any extra information that might be in place to reorder it, announcing the CTA, then the Article A, then the Article B in that order. If we use the `tab` key to traverse the DOM, it will once again follow the DOM order, rather than our re-ordered items. Try using the `tab` key on this page to navigate through the section above, and notice that the focus outline is applied to Article A before the CTA. So we can see by attempting to replicate our design and using `order` or `grid-template-areas`, that we've actually broken the accessibility of the page and left behind come accessibility debt to fix later on, and that debt increases with every time we use `order` or `grid-template-areas` and don't account for the accessibility implications. That's also just from a developer's perspective, considering the user experience is even worse - users who rely on keyboard navigation or screen readers only have the DOM as their map and our CSS decisions often discard that map entirely.\n\n### Visual order vs DOM order: the gap your users fall into\n\nWhen CSS reorders content visually, keyboard and screen reader users still\nfollow the DOM. Use <span style=\"color: var(--color-dark)\">Tab</span> to navigate\nthe cards below and feel the difference.\n\n<ReadingOrderDemo2 />\n\n## The Solution: reading-flow and reading-order\n\nSo how do we fix this? This design isn't exactly a new trend, but being able to fix it whilst also being able to maintain the visual order of the elements is a bit of a challenge. There is either;\n- an outcome where visual design flexibility is put aside in favor of accessibility leading to a less than ideal user experience/ugly design decision, or\n- we over-engineer a solution to look to sync the DOM with the visual order to aid with our accessibility efforts, leading to some complex and fragile solutions.\n\nBit of a no-win situation really. Even worse, the difference between visual order and DOM order isn't just an massive inconvenience, it's also a W3C-recognized accessibility violation, specifically the WCAG 1.3.2: Meaningful Sequence and WCAG 2.4.3: Focus Order guidelines.\n\n<aside class=\"blockquote stacked\" style=\"--gutter: var(--space-s)\">\n <div><i>\"When the sequence in which content is presented affects its meaning, a correct reading sequence can be programmatically determined.\"</i> - <a href=\"https://www.w3.org/WAI/WCAG22/Understanding/meaningful-sequence.html\">WCAG 1.3.2: Meaningful Sequence (Level A)</a></div>\n <div><i>\"If a Web page can be navigated sequentially and the navigation sequences affect meaning or operation, focusable components receive focus in an order that preserves meaning and operability.\"</i> - <a href=\"https://www.w3.org/WAI/WCAG20/Understanding/focus-order\">WCAG 2.4.3: Focus Order (Level A)</a></div>\n</aside>\n\nWith these two principles in mind, it's clear that our number one goal should be the consistency and predictability of the experience for all users. Breaking this, if it wasn't clear enough by now which hopefully it is, can lead to confusion, frustration and ultimately exclusion. By using `reading-flow` and `reading-order`, we can allow for content that is presented the way our design is put together, and also meets the accessibility principles that we outlined above. All the while avoiding the pitfalls of using `order` or `grid-template-areas`, their accessibility implications and focus on only **visually** rearranging content.\n\nIt's important to note at this stage that this isn't to replace the use of `order` or `grid-template-areas`, if anything, it allows us to use them for purely visual purposes and still maintain the accessibility of the page knowing that they won't fail WCAG guidelines.\n\n### reading-flow (on the container):\n\nSo, what is `reading-flow`? This property controls how the browser determines the default reading order of children that are with a `flex`, `grid` or block container. There are seperate applicable values that can be used on this property, depending on whether the container has one of those `display` properties set. These values are;\n\n- normal (default): Follows DOM order.\n- flex-visual: Prioritizes the visual order of flex items.\n- flex-flow: Follows the logical flow of flex items (e.g., flex-direction: row-reverse would reverse the reading order).\n- grid-rows: Reads grid items row by row.\n- grid-columns: Reads grid items column by column.\n- grid-order: Follows the explicit order values on grid items.\n- source-order: Explicitly forces DOM order, even in grid/flex.\n\n### reading-order (on the items):\n\nSo if we have `reading-flow` to do this, what does `reading-order` do differently? Well `reading-order` provides an integer value - similar to how `order` is used - that allows a manual override of the reading order of individual items that are inside a `reading-flow` container. The lower the value, the higher the priority of reading order. If `reading-flow` is not set on the parent then any `reading-order` values set are not taken into account, and is also disregarded if the `reading-flow` container is set to `normal` or `source-order`. This is where the power of `reading-order` comes in, as it directly instructs the browser's Accessibility Tree to follow the visual order, rather than the static DOM order. This means that for interactive elements, the keyboard focus order is automatically fixed, eliminating the need for fragile JavaScript solutions or manually setting tabindex values.\n\n## Best Practices and Considerations\n\nWhen using these properties, it's best to not overuse it. For accessibility, the default DOM order is always the best - referring back to our 'golden truth' comment earlier. If there was to be a recommendation, it would be to only use this when there's a justified reason for the reordering of elements and if the design absolutely couldn't be achieved without that reordering occurring - after all, it's an enhancement to solve specific layout challenges.\n\nWhen we look at the support at the top of this page, support is still sparse, but it's getting better. As of March 2026, Chrome and Edge have support for `reading-flow` and `reading-order`. The progressive enhancement story is actually straightforward: browsers without support simply fall back to DOM order, which is the baseline behaviour anyway. No special fallback code needed - just make sure your DOM order is as logical as possible, and reading-flow becomes an enhancement on top of that. It's important to communicate this to the team and the stakeholders, and to have a plan in place to test the site thoroughly with assistive technologies to ensure that the site is still accessible.\n\n## Conclusion\n\nSometimes visual design and accessibility butt heads, and they have done for years. Now, with the introduction of reading-flow and reading-order, this conflict can be significantly reduced. As developers, we no longer need to choose between a beautiful design and an accessible one. We're finally getting the right tools to do this without complex JavaScript solutions or manual tabindex management. This is a win for everyone.\n\nThat being said, this is still a work in progress, and support is more sparse than it should be given the importance of the feature. To help push it forward, developers need to advocate for it - test it, report issues, and make the case for prioritising it. The next generation of web layouts should be both beautiful and universally accessible. We have the tools now. Let's use them.\n\n## Resources\n\nThe spec is still evolving - these are the working drafts worth bookmarking if you want to track changes as browser support improves.\n\n- https://drafts.csswg.org/css-display-4/#reading-flow\n- https://drafts.csswg.org/css-display-4/#reading-order",
"version": "1.0"
},
"description": "Your CSS is lying to some of your users. Here's the property that fixes it.",
"path": "/writing/content-flow-with-reading-order",
"publishedAt": "2025-06-15T23:00:00.000Z",
"site": "https://dominickjay.com",
"tags": [
"css",
"accessibility"
],
"textContent": "import ReadingOrderDemo1 from '../../components/demos/ReadingOrderDemo1.astro';\nimport ReadingOrderDemo2 from '../../components/demos/ReadingOrderDemo2.astro';\nimport PlaceholderImage from '../../components/PlaceholderImage.astro';\nimport FYIBlock from '../../components/FYIBlock.astro';\nimport CardDemo from '../../components/CardDemo.astro';\n\nThe Accessibility Gap: When Visual Order Betrays Logical Order\n\nFor a long time, developers have been able to put together the strangest of layouts that have come from the dark corners of the design team with very little pushback. Properties like , and the property have given us a monumental amount of control when dealing with the visual presentation of the elements on the screen, and we can rearrange content to adapt to different screen sizes and user preference with little effort.\n\nSometimes however, that ability comes with a sidestep of accessibility and performance concerns, and that really isn't acceptable at all. We would warn that a layout might affect accessibility, but in the worst cases it can met with a shrug, depending on how much the team cares about providing accessible solutions in the first place. In my current position, accessibility concerns are met with a lot of discussion and push back to find a better path - so the prior 'shrug' example is not a reflection of how our projects are dealt with. The main challenge has been that while we can change how elements look on the screen, their true order in the DOM is the 'golden truth'. We'll go through a couple of examples to help illustrate this.\n\nExample 1: Our card layout\n\nIf we have for example, a traditional card layout that has 3 elements; a date (the highest semantic priority), a title, and an image (the lowest semantic priority), and we've used CSS to reorder them differently (let's say; the image, then the date, then the title). We would have this;\n\n{}\n\nVisually, we've got our card layout looking like this; an image, then a date, then a title. Pretty generic stuff, but when a screen reader user traverses this layout, the order of the content is not what we would expect. The image is announced first, then the date, then the title. This is because the screen reader is reading the content in the order of the DOM, not the visual order.\n\nVisual order vs DOM order: the gap your users fall into\n\nWhen CSS reorders content visually, keyboard and screen reader users still\nfollow the DOM. Use Tab to navigate\nthe cards below and feel the difference.\n\nExample 2: Our grid layout\n\nIf we have for example, a layout that is 3 elements (Article A, Article B and a CTA) in that order in the DOM, and we've used CSS to reorder them differently (let's say; the CTA, Article A and Article B). We would have this;\n\n{}\n\n \n 20th October 2025\n This is the card that is the first item in the DOM\n \n \n\n \n 20th October 2025\n This is the card that is the second item in the DOM\n \n \n\n \n \t\n \n\n \n 20th October 2025\n This is the card that is the first item in the DOM\n \n \n\n \n 20th October 2025\n This is the card that is the second item in the DOM\n \n \n\n \n \t\n \n\nWhat's the correct order for these examples? Visually they are correct - they've done what we want it to do in that specific order to appease our designer overlords. However, for a user navigating the site through the use of a screen reader or a keyboard, this might be a bit of a contradiction. Typically screen readers such as NVDA, JAWS or Voiceover traverse the DOM and read out the content as it is set there, disregarding any extra information that might be in place to reorder it, announcing the CTA, then the Article A, then the Article B in that order. If we use the key to traverse the DOM, it will once again follow the DOM order, rather than our re-ordered items. Try using the key on this page to navigate through the section above, and notice that the focus outline is applied to Article A before the CTA. So we can see by attempting to replicate our design and using or , that we've actually broken the accessibility of the page and left behind come accessibility debt to fix later on, and that debt increases with every time we use or and don't account for the accessibility implications. That's also just from a developer's perspective, considering the user experience is even worse - users who rely on keyboard navigation or screen readers only have the DOM as their map and our CSS decisions often discard that map entirely.\n\nVisual order vs DOM order: the gap your users fall into\n\nWhen CSS reorders content visually, keyboard and screen reader users still\nfollow the DOM. Use Tab to navigate\nthe cards below and feel the difference.\n\nThe Solution: reading-flow and reading-order\n\nSo how do we fix this? This design isn't exactly a new trend, but being able to fix it whilst also being able to maintain the visual order of the elements is a bit of a challenge. There is either;\nan outcome where visual design flexibility is put aside in favor of accessibility leading to a less than ideal user experience/ugly design decision, or\nwe over-engineer a solution to look to sync the DOM with the visual order to aid with our accessibility efforts, leading to some complex and fragile solutions.\n\nBit of a no-win situation really. Even worse, the difference between visual order and DOM order isn't just an massive inconvenience, it's also a W3C-recognized accessibility violation, specifically the WCAG 1.3.2: Meaningful Sequence and WCAG 2.4.3: Focus Order guidelines.\n\n \"When the sequence in which content is presented affects its meaning, a correct reading sequence can be programmatically determined.\" - WCAG 1.3.2: Meaningful Sequence (Level A)\n \"If a Web page can be navigated sequentially and the navigation sequences affect meaning or operation, focusable components receive focus in an order that preserves meaning and operability.\" - WCAG 2.4.3: Focus Order (Level A)\n\nWith these two principles in mind, it's clear that our number one goal should be the consistency and predictability of the experience for all users. Breaking this, if it wasn't clear enough by now which hopefully it is, can lead to confusion, frustration and ultimately exclusion. By using and , we can allow for content that is presented the way our design is put together, and also meets the accessibility principles that we outlined above. All the while avoiding the pitfalls of using or , their accessibility implications and focus on only visually rearranging content.\n\nIt's important to note at this stage that this isn't to replace the use of or , if anything, it allows us to use them for purely visual purposes and still maintain the accessibility of the page knowing that they won't fail WCAG guidelines.\n\nreading-flow (on the container):\n\nSo, what is ? This property controls how the browser determines the default reading order of children that are with a , or block container. There are seperate applicable values that can be used on this property, depending on whether the container has one of those properties set. These values are;\nnormal (default): Follows DOM order.\nflex-visual: Prioritizes the visual order of flex items.\nflex-flow: Follows the logical flow of flex items (e.g., flex-direction: row-reverse would reverse the reading order).\ngrid-rows: Reads grid items row by row.\ngrid-columns: Reads grid items column by column.\ngrid-order: Follows the explicit order values on grid items.\nsource-order: Explicitly forces DOM order, even in grid/flex.\n\nreading-order (on the items):\n\nSo if we have to do this, what does do differently? Well provides an integer value - similar to how is used - that allows a manual override of the reading order of individual items that are inside a container. The lower the value, the higher the priority of reading order. If is not set on the parent then any values set are not taken into account, and is also disregarded if the container is set to or . This is where the power of comes in, as it directly instructs the browser's Accessibility Tree to follow the visual order, rather than the static DOM order. This means that for interactive elements, the keyboard focus order is automatically fixed, eliminating the need for fragile JavaScript solutions or manually setting tabindex values.\n\nBest Practices and Considerations\n\nWhen using these properties, it's best to not overuse it. For accessibility, the default DOM order is always the best - referring back to our 'golden truth' comment earlier. If there was to be a recommendation, it would be to only use this when there's a justified reason for the reordering of elements and if the design absolutely couldn't be achieved without that reordering occurring - after all, it's an enhancement to solve specific layout challenges.\n\nWhen we look at the support at the top of this page, support is still sparse, but it's getting better. As of March 2026, Chrome and Edge have support for and . The progressive enhancement story is actually straightforward: browsers without support simply fall back to DOM order, which is the baseline behaviour anyway. No special fallback code needed - just make sure your DOM order is as logical as possible, and reading-flow becomes an enhancement on top of that. It's important to communicate this to the team and the stakeholders, and to have a plan in place to test the site thoroughly with assistive technologies to ensure that the site is still accessible.\n\nConclusion\n\nSometimes visual design and accessibility butt heads, and they have done for years. Now, with the introduction of reading-flow and reading-order, this conflict can be significantly reduced. As developers, we no longer need to choose between a beautiful design and an accessible one. We're finally getting the right tools to do this without complex JavaScript solutions or manual tabindex management. This is a win for everyone.\n\nThat being said, this is still a work in progress, and support is more sparse than it should be given the importance of the feature. To help push it forward, developers need to advocate for it - test it, report issues, and make the case for prioritising it. The next generation of web layouts should be both beautiful and universally accessible. We have the tools now. Let's use them.\n\nResources\n\nThe spec is still evolving - these are the working drafts worth bookmarking if you want to track changes as browser support improves.\nhttps://drafts.csswg.org/css-display-4/#reading-flow\nhttps://drafts.csswg.org/css-display-4/#reading-order",
"title": "Reclaiming the Flow: Mastering Visual vs. DOM Order with @reading-order"
}