The earlier pages taught CSS as a language. This page asks what that language does to people: the accessibility work that is CSS's job, performance as usability, styling for other languages, the developers who maintain a style sheet, and where frameworks fit.
1. CSS as a humanistic medium
CSS is easy to dismiss as decoration applied after the real work. The opposite is closer to the truth. HTML decides what a page means; CSS decides whether a particular person can perceive and use it. Light gray text on white, a removed focus ring, an animation that cannot be stopped, a layout that falls apart at 200 percent zoom: each is a CSS decision, and each shuts somebody out. A style sheet has more audiences than its author usually pictures.
| Who | What they need from the CSS |
|---|---|
| A sighted reader on a fast connection | A page that looks designed and appears quickly: the reader most authors test as. |
| A reader with low vision or a color-vision difference | Enough contrast, text that scales, and meaning that never depends on color alone. |
| A keyboard, switch or voice user | A visible focus indicator, a sensible focus order, and targets big enough to hit. |
| A reader with vestibular sensitivity or attention limits | Motion that respects prefers-reduced-motion, and a calm, predictable layout. |
| A reader on a slow or metered connection | A page that does not wait on, or pay for, styles it never uses. |
| A reader in another language or script | A layout that survives longer words, right-to-left text and vertical writing. |
| The developer who maintains it, including you in two years | CSS that reads as clearly as it writes, and changes that stay local. |
Serving all of them comes down to four qualities: the style sheet is accessible, fast, translatable and maintainable. Sections 2 to 5 take them in turn.
Start from what the platform already gives
The browser arrives with humane behavior built in: user-agent focus rings, form controls in the reader's language, relative units that follow the reader's font size, media queries that report preferences, and a cascade that lets the reader's own style sheet win when it must. So the first discipline is not to add accessibility on top of CSS but to avoid breaking what is already there. Nearly every CSS accessibility failure is an override that removed something and put nothing in its place: outline: none, text in fixed units, a fixed height that clips enlarged text. The Accessibility quality page traces this idea through every layer of a web client; this page covers the part that lives in the style sheet.
2. Accessibility: what CSS can give and take away
CSS cannot make bad HTML accessible. A div styled to look like a button is a picture of a button: not focusable, no role, deaf to Enter and Space. Everything below assumes real button, a href, label and landmark elements, and asks what CSS then does well or badly.
Focus you can see
For a keyboard user the focus indicator is the only way to know where they are; removing it is like hiding the mouse pointer. The most common accessibility bug in CSS is one line, often added because someone disliked the ring after a mouse click:
The :focus-visible pseudo-class is the proper answer. It matches only when the browser judges an indicator useful: after keyboard navigation or in a text field, but usually not after a mouse click on a button. Browsers' user-agent styles already draw their default ring with it. If the default does not suit the design, restyle it instead of deleting it:
Use outline rather than box-shadow: an outline takes no layout space, follows rounded corners, and survives forced-colors mode, which removes box shadows. Measure it in rem so it grows with zoom, and check its contrast: WCAG 2.2 requires focus to be visible at level AA (success criterion 2.4.7), asks that a visual indicator of state meet 3:1 against its surroundings (1.4.11, non-text contrast), and sets a stricter size and contrast target for focus at level AAA (2.4.13).
Contrast, and never color alone
Contrast is the most measurable accessibility decision in a style sheet. WCAG's AA level, the usual legal and professional target, asks for:
- 4.5:1 between normal text and its background;
- 3:1 for large text (roughly 24 CSS pixels, or 18.66 bold) and for the parts of controls and graphics a person needs to see, such as input borders, icons and focus rings.
DevTools shows the ratio in its color picker. Separately, color must never be the only carrier of meaning (criterion 1.4.1): an error state needs a message or an icon as well as red, and a link in a paragraph needs an underline or another cue besides its hue. The Color section treats both in depth: Color for people computes contrast and shows what color alone fails to carry, and How color works covers color spaces.
Readers can also ask for more contrast. The prefers-contrast media feature reports that request, and a token-based style sheet can answer it in a few lines:
Motion
Parallax, large slides and zooms can cause dizziness and nausea for people with vestibular disorders. Operating systems offer a "reduce motion" setting, and prefers-reduced-motion reports it to CSS. The responsive page introduces preference queries; here, treat stillness as the baseline and motion as the enhancement:
Many sites use the opposite approach, a global switch that cuts every animation short. It is a safety net for animation you do not control, but it relies on !important rather than design:
Reduced motion does not mean no change: a fade still signals that something happened. The Animation topic (coming) covers motion in full.
Zoom, text size and reflow
Low-vision readers zoom the page or raise the browser's default font size, and a style sheet must survive both. Three WCAG criteria at level AA say what that means:
| Criterion | Requirement | What breaks it in CSS |
|---|---|---|
| 1.4.4 Resize text | Text can be enlarged to 200 percent without loss of content or function. | Text in px, which ignores the font-size setting; boxes with a fixed height that clip enlarged text. |
| 1.4.10 Reflow | Content works at a width of 320 CSS pixels (a 1280-pixel window at 400 percent zoom) without scrolling in two directions. | Fixed-width layouts and columns that never stack; wide content that is not allowed to scroll on its own. |
| 1.4.12 Text spacing | Nothing is lost when the reader raises line height to 1.5, paragraph spacing to 2em, letter spacing to 0.12em and word spacing to 0.16em. | Fixed heights and widths around text, and overflow: hidden on text containers. |
The 320-pixel figure is a fact about the standard, not a unit to design in. A layout built from relative units, fluid tracks and normal flow meets all three without special effort, because it never assumed a size in the first place:
The min() inside minmax() lets a track shrink below 18rem on a narrow viewport, which is exactly the 400 percent case. min-block-size sets a floor without a ceiling, so a card grows with its text. Code and wide tables scroll inside their own box, which WCAG allows. To test, zoom to 400 percent, then separately set the browser's default font size to its largest value.
Forced colors
Windows contrast themes apply forced colors: the browser replaces the page's colors with a small palette the reader chose, such as yellow text on black. Backgrounds, borders and text all take the reader's colors, and box shadows are dropped. CSS can detect the mode with @media (forced-colors: active) and can name the reader's palette with system color keywords such as Canvas, CanvasText, LinkText, ButtonText and Highlight.
Well-built pages need almost nothing. Failures come from meaning drawn only with a background or a shadow, which forced colors erase:
The transparent border is invisible normally, but forced colors redraw it in the reader's color, so the button keeps its edge. The query restores a cue the design carried in color alone and points icons at the reader's text color. The property forced-color-adjust: none opts an element out of forced colors entirely; keep it for the rare element, such as a color swatch, whose color is the content.
Hiding content properly
"Hidden" means different things to different readers. Choose by asking who should still perceive the content:
| Technique | Seen | Read by a screen reader | Focusable | Takes space |
|---|---|---|---|---|
display: none, or the hidden attribute | No | No | No | No |
visibility: hidden | No | No | No | Yes |
opacity: 0 | No | Yes | Yes | Yes |
| The visually hidden pattern | No | Yes | Yes | No |
aria-hidden="true" (HTML, not CSS) | Yes | No | Yes, which is a trap | Yes |
Use display: none or hidden for content hidden from everyone, such as a closed panel; the Properties page covers both. Use the visually hidden pattern for text a screen reader should announce but the design does not show, such as the label of an icon-only button. opacity: 0 alone is usually a mistake: a keyboard user can still tab to the invisible element and land on nothing.
Here a class earns its place: the markup cannot say "hide this visually" on its own. The second rule styles the first link in the body, by convention a "skip to main content" link, which stays off-screen until a keyboard user reaches it.
Source order and visual order
Flexbox order, grid placement, row-reverse and absolute positioning can all show boxes in a different order from the HTML. The eye follows the visual order; the keyboard and screen readers follow the source. When they disagree, focus jumps around the screen.
WCAG names the requirement: when the sequence affects meaning, it must survive in the source (1.3.2, meaningful sequence), and focus must move in an order that preserves meaning and operability (2.4.3, focus order). The fix is almost never more CSS. Put the HTML in the order a reader should meet it, then use layout to arrange that order on the screen:
The HTML lists nav, main, aside and footer in the order a reader needs. A narrow screen stacks them so; a wide one puts the aside beside main, a small and sensible difference. Rearranging is fine when visual and reading order agree, such as a wrapping card grid, and a bug when they disagree. A newer property, reading-flow, lets focus follow the visual order of flex and grid items, but it has shipped only in Chromium-based browsers and is not Baseline, so source order remains the fix.
For type in particular (weight and contrast together, all capitals, forced colors and readers who replace a site's fonts), see Type for people.
3. Performance as usability
A page that takes eight seconds to appear is unusable for the person who waited, however well it was designed. Speed is part of usability, and it matters most to readers on slow phones and metered data. The Performance quality page sets out the course's numbers and the whole delivery picture. This section covers what is specific to CSS.
CSS blocks rendering, on purpose
A browser will not paint a page until it has the style sheets that apply to it. That is deliberate: painting unstyled content and restyling it a moment later is worse than a short wait. The cost is that every style sheet in the head sits on the critical path to the first paint.
Two more facts follow. A classic script in the head waits for style sheets above it, because the script might ask for computed styles, so a slow style sheet can also delay JavaScript. And a style sheet that pulls in another with @import cannot reveal the second file until the first has arrived, which stretches the critical path; Basics covers that case. A sheet limited with media="print", or to any media query that does not currently match, is still downloaded but does not block rendering.
Critical CSS, and the simpler answer
The classic optimization is critical CSS: extract the rules the first screenful needs, inline them in a style element in the head so the first paint needs no extra request, and load the rest of the style sheet without blocking. Tools such as Critical and Beasties automate the extraction.
It is a trade-off. Inlined CSS is downloaded again with every page because it cannot be cached separately, and the extraction can drift as the design changes. For small CSS, as this section's style encourages, one compact, compressed, cached style sheet usually wins. The fastest CSS is CSS never sent: open the DevTools Coverage panel, use the site, and delete the rules that never matched.
Selector cost is rarely the problem
Older advice warned against "expensive" selectors such as the universal selector or descendant combinators. Engines have long since optimized matching, and on real pages it is small next to network, layout and script time. Choose selectors for readability. If the Performance panel shows long "Recalculate Style" work, the usual cause is a very large DOM or a script repeatedly changing classes high in the tree, not any one selector.
Where a change enters the pipeline
After the first paint, the browser keeps rendering as things change. A change runs through some or all of four stages: style (work out each element's computed values), layout (work out sizes and positions), paint (draw pixels into layers) and composite (combine the layers on screen). How much of that work a change causes depends on which property changed.
An animation produces a frame about sixty times a second. Animating width, margin or inset-inline-start forces layout on every frame, often spreading to neighboring boxes, and on a slow phone the motion stutters. Moving the box with translate and fading it with opacity leaves layout alone, and the browser can usually hand the animation to the compositor:
The same pipeline explains layout thrashing in scripts: writing a style and then reading a size, in a loop, forces a fresh layout on every read.
Skip work for content off screen
On a long page, most content starts out of view. content-visibility: auto lets the browser skip layout and paint for an element while it is far off screen, and do the work just before it scrolls into view. The content stays in the DOM and the accessibility tree, and find-in-page still reaches it. contain-intrinsic-size gives skipped sections a placeholder size so the scrollbar does not jump; the auto keyword makes the browser remember the real size once it has rendered the section.
content-visibility is Baseline newly available, and a browser without it simply renders everything, as before. It pays off on long pages of independent sections, not on short ones.
Layout shift
The most hostile failure is content that moves after the reader starts using it: an image arrives, pushes a button down, and the tap lands elsewhere. Core Web Vitals measure it as Cumulative Layout Shift. CSS prevents most of it by reserving space: aspect-ratio, or width and height attributes in the HTML, for images and video; min-block-size for regions filled late; and translate rather than geometry for anything that moves. Web fonts that swap in with different metrics are the other big cause; loading fonts belongs to the Fonts topic (coming).
4. Internationalization
Most style sheets carry English assumptions: text runs left to right, words are as long as their English versions, quotation marks look like these ones. Arabic, Hebrew, Persian and Urdu run right to left. Japanese, Chinese and Mongolian can be set vertically. German often runs a third longer than English, and browser translation can rewrite any page after it loads.
Logical properties as the default
Basics introduced the logical directions: inline is the direction lines run, block is the direction they stack, and each has a start and an end. Logical properties are named for those directions, so they follow the writing mode and the direction of the content instead of the screen:
| Physical | Logical |
|---|---|
margin-left, margin-right | margin-inline-start, margin-inline-end, or margin-inline for both |
padding-top, padding-bottom | padding-block-start, padding-block-end, or padding-block |
border-left | border-inline-start |
width, max-width, height | inline-size, max-inline-size, block-size |
top, left | inset-block-start, inset-inline-start |
text-align: left | text-align: start |
Logical properties are Baseline widely available and cost nothing to adopt from the start; converting a physical style sheet when an Arabic edition arrives costs a great deal. Keep physical properties for things that are truly physical, such as a shadow cast by light from above.
Direction and writing mode
Direction belongs in the HTML, because it is a property of the content. The dir attribute sets it for a document or a passage:
With dir="rtl", every logical property flips with no further work. The few things that must mirror explicitly, such as a "forward" arrow icon, can use the :dir() pseudo-class, which matches the direction an element actually has.
Vertical scripts change the axes themselves. With writing-mode: vertical-rl, lines run top to bottom and stack right to left. Logical properties keep their meaning; physical ones now point the wrong way.
Language-aware typography: :lang(), quotes and hyphens
The :lang() pseudo-class matches an element by the language it inherits from lang, so CSS can adjust typography per language without a class. Quotation marks are the classic case. The q element generates its marks from the quotes property; its initial value, auto, asks the browser to choose marks suited to the language, and :lang() sets them explicitly where a design needs control or a browser's choice falls short:
German gets low opening quotes, French gets guillemets with no-break spaces, and Japanese gets corner brackets, all from the same <q> markup. hyphens: auto breaks long words at syllable boundaries, which keeps narrow columns of German from leaving ragged gaps. It works only when the content declares its language, because the browser must pick a hyphenation dictionary, and not every language has one. One more reason every page starts with <html lang="…">.
Room for longer words
A "Save" button becomes "Speichern" in German and "保存" in Chinese; a box sized for the English word overflows in one and gapes in the other. The cure is the same as for zoom: min-inline-size instead of fixed widths, ch and em for measures tied to text, and flexbox or grid to absorb the variation. A pseudo-translation that stretches every string by a third catches most failures early.
Type is where many of these needs meet. The Fonts section's Type for people shows what joined scripts, tall marks and Han unification ask of fonts and CSS, with a sampler of eight scripts.
5. Developer experience
The audience most often forgotten is the maintainer. CSS is read far more often than it is written, and a style sheet that frightens its readers gets worse over time: afraid to change a rule, they add a more specific one on top, and eventually nobody understands the whole.
Four qualities make CSS pleasant to maintain:
- Readable. A person can tell what a rule does by reading it. Selectors name things the way the HTML does, and token names say what a value is for, such as
--spaceor--measure, not what it happens to be. - Local. A person can find the rules that affect an element, and a change to one component does not leak into another.
- Predictable. A person can tell which rule wins without trial and error: specificity stays low, layers are declared in one place, and
!importantis rare. - Small. Dead rules are deleted. Less CSS is easier to read, faster to load, and has fewer ways to surprise anyone.
Every habit in this section's house style serves that reader. Compare a selector that describes a path through the DOM with one that describes a thing:
The first rule breaks whenever the markup changes and can only be overridden by something more specific. The second set states the cascade's order in its first line, gives shared rules zero specificity with :where(), and finds the current page's link by the attribute that already tells assistive technology which page it is. The cascade page explains layers and :where() in full, and the selectors page explains specificity.
6. Frameworks and the platform
Faced with all of this, many teams reach for a CSS framework to make the complexity go away. Each major framework solved a real problem of its time, and deserves a fair account.
Bootstrap: consistency when browsers disagreed
Bootstrap came out of Twitter in 2011, when browsers differed in their defaults, layout meant floats, and every team rebuilt the same grid, buttons and forms. It offered a normalized base, a twelve-column responsive grid, and ready-made components such as btn btn-primary. Teams got consistent, responsive interfaces in days, and a generation of sites looked alike. Later versions followed the platform: Bootstrap 4 moved its grid to flexbox, and Bootstrap 5 dropped jQuery and exposed its values as custom properties.
Tailwind: naming nothing
Tailwind, first released in 2017, took the opposite approach. Instead of named components it offers small single-purpose utility classes, and an element's look is composed in the markup:
The case for it is real. Naming is hard, and many one-off styles have no meaningful name. Utilities skip naming, keep styling beside the element, leave no dead CSS when markup is deleted, and keep the output small because only used classes are emitted. Specificity fights mostly disappear. Its current version is itself built on modern CSS, including cascade layers and custom properties.
The costs are real too. A maintainer must combine ten classes in their head to see the button, and the intent, "the primary action", appears nowhere. The HTML gets heavier, and styling is tied to a build tool and a vocabulary learned on top of CSS. A team that knows px-4 but not padding-inline is stuck the moment it needs something the framework lacks.
| Approach | Optimizes for | Costs | Fits best |
|---|---|---|---|
| Utility classes | Writing speed, no naming, small output, local edits | Reading effort, heavier HTML, intent hidden, a tool to learn | Prototypes, and teams fluent in the vocabulary |
| Named components | Reading speed, visible intent, a coherent design system | Naming effort, cascade discipline, CSS to prune | Long-lived code maintained by many people |
| Structure-first platform CSS (this course) | Semantic HTML, no build step, low specificity, longevity | Requires knowing CSS well; good markup is a prerequisite | Content sites, and anywhere the HTML is in your control |
What native CSS now does
Much of what frameworks and preprocessors such as Sass were adopted for is now part of the language, and Baseline widely available:
| The need | The framework or preprocessor answer | The platform answer |
|---|---|---|
| Consistent starting styles | Normalize, Reboot, Preflight | Consistent user-agent styles and a small reset layer |
| Design tokens | Sass variables, a theme configuration | Custom properties, which also change at run time and per element |
| Nesting | Sass and Less | Native nesting |
| Controlling who wins | Naming conventions, single-class utilities, !important | @layer and :where() |
| A grid system | Twelve-column classes | Grid and subgrid |
| Components that adapt to their space | Breakpoint classes such as md: | Container queries |
| Color variations | Sass darken(), fixed palettes | color-mix() and the color functions |
The structure-first equivalent of the two buttons above needs no class at all, because the HTML already says which button is the primary action of a form:
The course's position
This course is platform first. That is not a judgment that frameworks are bad; you will use them in jobs. It is a judgment about order. Abstractions do not remove complexity, they move it into a build step, a vocabulary or a dependency, and when the abstraction leaks only someone who knows the layer underneath can fix it. Learn the platform first and a framework becomes a convenience you can evaluate.
Longevity points the same way. Frameworks change conventions every few years, while CSS grows by addition: plain, semantic CSS written in 2010 still works in 2026. And the defaults, preference queries, logical properties and :focus-visible exist because standards authors argued on users' behalf. Work with those decisions and your pages inherit them.
7. Summary
| Concept | Key points |
|---|---|
| A humanistic medium | CSS decides whether people can perceive and use a page. Its audiences include disabled readers, slow connections, other languages and future maintainers. Start from the platform's defaults and avoid breaking them. |
| Focus | Never remove outlines without a replacement. Style :focus-visible with an outline in relative units, with at least 3:1 contrast. |
| Contrast and color | WCAG AA: 4.5:1 for normal text, 3:1 for large text and interface parts. Never let color alone carry meaning. Answer prefers-contrast. |
| Motion | Make stillness the baseline and add motion inside prefers-reduced-motion: no-preference. |
| Zoom and reflow | Relative units, fluid tracks and minimum sizes satisfy resize text, reflow at 400 percent zoom, and text spacing. No fixed heights around text. |
| Forced colors | Colors are replaced and shadows dropped. Use transparent borders and outlines, and system colors where needed. |
| Hiding content | display: none hides from everyone; the visually hidden pattern hides only from the eye; opacity: 0 alone leaves a focusable ghost. |
| Source and visual order | Keyboard and screen readers follow the source. Put HTML in reading order and let grid place it; do not reorder against meaning. |
| Performance | CSS blocks first paint. Ship less of it, cache it, inline critical CSS only when it pays. Selector cost rarely matters; animate transform and opacity, not geometry; use content-visibility on long pages; reserve space to prevent layout shift. |
| Internationalization | Logical properties by default; direction in HTML with dir; writing-mode for vertical scripts; :lang() for quotes; hyphens needs lang; leave room for longer words. |
| Developer experience | Readable, local, predictable and small. Declare layers once, keep specificity low with :where(), and delete dead rules. |
| Frameworks | Bootstrap gave consistency and a grid; Tailwind removes naming at a cost to reading. Custom properties, layers, nesting and container queries now cover much of their ground. Learn the platform first. |