CSS Coding Standards
The full reference for writing maintainable CSS: scope, specificity, naming, design tokens, units, layout, responsiveness, theming, accessibility, performance, and animation. Without discipline, CSS turns into piles of overrides and fights over !important. With it, the UI stays consistent, themeable, and easy to change. Pairs with the HTML & Markup, React, and Blazor standards and with Accessibility.
CSS's main hazards are global scope and cascading specificity. A rule meant for one place affects another, and "fixing" it with more specific selectors and !important makes the next change harder. The fix is discipline: scope styles to components, keep specificity flat, drive values from shared tokens, and build responsive and accessible by default.
We prefer component-scoped styles (CSS modules in React, scoped .razor.css in Blazor) plus a small set of global tokens. Whatever method a project uses, apply it consistently. Prettier and Stylelint enforce the mechanics.
Architecture & scope
- DoScope styles to their component (CSS modules, scoped
.razor.css, or a strict convention like BEM) so a change cannot leak and break something elsewhere. - DoKeep global CSS to resets and normalisation, design tokens, and base typography only. Everything else lives with its component.
- DoKeep component styles next to the component, and keep stylesheets small and focused.
- AvoidLarge global stylesheets where any rule can affect anything. That is how specificity fights start.
Specificity & selectors
- DoKeep specificity low and flat. Prefer single class selectors. Avoid deep descendant chains and styling by element or ID.
- Avoid
!importantexcept as a genuine, documented last resort. It shows specificity has gone wrong and makes future overrides worse. - AvoidOver-qualified selectors (
div.card span.title) and styling on IDs. Both are fragile and hard to override cleanly. - DoUse modern selectors (
:is(),:where()for zero specificity,:has()) on purpose and sparingly.
#sidebar div.menu ul li a.item { color: #3b82f6 !important; }
.item { padding: 13px; } /* magic number, overridden anyway */
A deep, ID-based selector plus !important means the next person can only override it with something even worse. The magic padding and hard-coded colour are not tokenised. This is how CSS becomes unmaintainable.
.menu-item { /* single class, scoped to component */
color: var(--color-link);
padding: var(--space-2);
}
.menu-item:focus-visible { outline: 2px solid var(--color-focus); }
Low specificity (easy to override), values from tokens (themeable and consistent), and a visible focus style for accessibility. Maintainable and correct.
Naming & methodology
- DoName classes for purpose or role, not appearance (
.btn-primary, not.blue-button), so the styling can change without renaming. - DoUse one consistent method across a project (BEM, utility classes, or CSS modules). Do not mix approaches without reason.
- DoKeep class names readable and consistent in case and format (kebab-case).
Design tokens & values
- DoUse design tokens (CSS custom properties) for colours, spacing, typography, radii, shadows, and z-index. One source of truth, themeable, with no hex codes scattered around (see Frontend Architecture).
- DoUse a consistent spacing and sizing scale instead of arbitrary pixel values everywhere.
- AvoidMagic numbers and one-off colours or values. If a value really is a one-off, add a comment saying why.
Units & sizing
- DoUse relative units (
rem,em) for typography and spacing that should scale with user settings. Use%,fr, and viewport units for layout where they fit. - DoAvoid fixed heights and widths that break with content or translation. Let content flow, and reserve space to prevent layout shift (CLS).
- DoRespect the user's font-size preferences. Do not fix everything in
px; this is an accessibility concern (see Accessibility).
Layout & responsiveness
- DoUse modern layout (Flexbox and Grid) instead of floats and hacks. Let the browser do the layout work.
- DoDesign mobile-first: base styles for small screens, then add to them with
min-widthmedia queries. Test real breakpoints (see Frontend Performance). - ConsiderContainer queries for components that must adapt to their context, and logical properties for right-to-left and internationalised layouts (see Internationalization).
Theming, typography & dark mode
- DoDrive themes (including dark mode) from tokens, switching variable values instead of duplicating rules. Respect
prefers-color-schemewhere relevant. - DoSet a sensible type scale and line-height for readability. Do not use heading tags for size; use CSS (see HTML & Markup Standards).
Accessibility & performance
- AlwaysMeet the minimum colour-contrast levels, and never convey meaning by colour alone. Pair colour with text, an icon, or a shape (see Accessibility).
- DoKeep focus styles visible. Never remove outlines without a clear replacement. Respect
prefers-reduced-motion. - DoKeep CSS lean: remove dead styles, avoid large unused frameworks, and watch for expensive selectors and animations on large pages (see Frontend Performance).
- DoAnimate
transformandopacity(the compositor handles these) instead of properties that affect layout (width, height, top). Let a linter enforce style (see CI/CD & Deployment). - AvoidLarge blocking CSS that delays first render, and animations that ignore reduced-motion preferences.
Self-review checklist
- AskIs this scoped so it cannot leak, with low specificity (no !important, no deep chains or IDs)?
- AskAm I using tokens and a consistent scale, not scattering magic colours and values?
- AskIs it responsive (mobile-first) and free of fixed sizes that break with content or translation?
- AskDoes it meet contrast, keep focus visible, avoid colour-only meaning, and respect reduced motion?
!important, and magic numbers make the UI fragile and slow to change, and careless CSS quietly breaks accessibility. Scoped, token-driven, low-specificity, accessible-by-default CSS keeps the interface consistent, themeable, and maintainable as the product and team grow.