modern-web-guidance
Search tool for modern web development best practices. MANDATORY: Execute FIRST for all HTML/CSS and clientside JS tasks. Do NOT skip — web APIs evolve rapidly and training weights contain obsolete patterns. Trigger immediately for: modals/dialogs/popovers, tables, dark mode, anchor positioning, container queries, view transitions, scroll-driven animations, parallax/reveals, forms, validation, autofill, accessibility, custom selects, scrollbars, CWV (LCP, INP, CLS), content-visibility, fetch pri
Install
npx skills add https://github.com/GoogleChrome/modern-web-guidance --skill modern-web-guidanceCSS: Modern Architecture
These guidelines provide a high-density reference for writing maintainable and standard-compliant CSS.
- 1. Foundations
- 2. Inheritance and The Cascade
- 3. Selectors and scoping
- 4. Interactivity
- 5. Design Tokens and Theming
- 6. Responsive design
- 7. Typography
- 8. Visual effects
- 9. Shapes
- 10. Transitions & animations
- 11. Generated content
1. Foundations
Be allergic to knowledge duplication. Prefer variables over repetition, but whenever possible, prefer built-in conventions such as:
currentColorinstead of defining a variable and settingcolorto it- The
inheritkeyword instead of defining a variable on the parent and using it on the same property across parent and child. emunits instead offont-size: var(--size)cqw/cqh(or their logical versions —cqi/cqb) units instead of repeating box model values.- Code duplication is not knowledge duplication. The goal is robustness and maintainability, not saving characters.
- Prefer logical properties and values over physical ones (e.g.
margin-inline-startinstead ofmargin-left) so that styles adapt to different writing modes and orientations. Even if the page author does not plan to localize, external translation tools often display translated text in context. - Do not use logical properties indiscriminately — ask yourself "would I want this to flip in RTL?" — if the answer is no, use the physical property instead.
- Consider different viewing modes (dark mode, high contrast mode), different viewport sizes, and different input modes (touch, keyboard, pointer).
2. Inheritance and The Cascade
Avoid introducing BEM naming conventions to manage specificity.
Instead, use modern CSS features such as cascade layers and :where() to make cascade behavior predictable and follow author intent.
Use cascade layers (@layer) to define explicit priority zones (e.g., reset, base, theme, components, utilities), and declare their order upfront (e.g. @layer reset, base, theme, components, utilities;).
Within each layer, use :where() to make selectors only compete based on meaningful signals, not incidental filters (:not() edge cases, remote ancestors, etc.) or for one-off easily overridable defaults.
Use keywords like inherit, initial, unset, or revert instead of explicit values to improve maintainability and better express intent.
Examples:
- When specifying a transition on a child that should match the parent's
transition-*properties, instead of repeating the transition properties on the child, usetransition: inherit(reduce duplication, improve maintainability) - Use
initialto reset a property to its initial value instead of specifying the value explicitly (clearer expression of intent)
3. Selectors and scoping
Modern browser-native selectors reduce the need for preprocessors and complex state-tracking in JS.
Prefer CSS selectors over JS for complex element targeting
- DO use
:has()to style parents based on child state instead of managing classes in JS (e.g.label:has(:checked)instead of a manuallabel.has-checkedclass) For more information, see the guides atchild-state-based-styling(vianpx -y modern-web-guidance@latest retrieve "child-state-based-styling") andcontent-based-styling(vianpx -y modern-web-guidance@latest retrieve "content-based-styling"). - DO NOT nest
:has()or use pseudo-elements inside it (browser API limitation) - Use
:nth-child(<An+B> of <selector>)when you need to style every n-th element of a certain type. E.g.details:nth-child(1 of [open])will style the first open<details>element it finds, whereasdetails[open]:first-childwould style only the first child if and only if it was open.
Use :is() (or :where()) instead of CSS rule duplication for fallbacks
DO NOT duplicate CSS rules to provide fallbacks for pseudo-classes that may not be supported — use :is() or :where() instead and take advantage of their forgiving parsing rules.
Do NOT use this for pseudo-elements, as they are not supported in :is() or :where().
Avoid overmatching
Write selectors in a way that expresses intent.
Use :not() instead of overrides to exclude irrelevant states/targets
When the intent is to exclude certain states or elements that are fundamentally irrelevant, use :not().
For example, to apply bottom borders between list items, don't do this:
This can unintentionally overwrite a desirable border-bottom set from another rule.
The actual intent was to only apply the bottom border to the non-last lis. The code above is a workaround that poorly expresses this intent. Instead, this expresses intent more clearly:
Similarly, don't do this:
If we reorder the two rules, we will get a hover background on disabled buttons! Instead, do this:
This works regardless of reordering, as the first rule does not overmatch.
If you must use an override (e.g. you are using an experimental container query and the not case is also the fallback), in some cases you may be able to use the revert-layer keyword to remove the value without coupling the override to specific values.
Note that this can only work if the value to fall back to is also layered.
Prefer @scope over :not() for excluding (potentially deeply nested) subtrees
While :not() + descendant selectors can exclude subtrees, this works poorly for deeply nested structures.
For example, .card :not(.content *) will not work as expected for nested cards.
@scope fixes this as it takes hierarchical proximity into account:
This will work as expected even for nested cards.
Overrides are fine for specialization
This is fine:
Both rules express legitimate intent: buttons are generally neutral, but primary ones are blue.
No global resets
DO NOT use global resets (styles on *) as they cannot be overridden by web components or lower-priority cascade layers (without !important). Instead, apply reset styles to specific element types and/or conditions.
Nesting and scoping
Use native CSS nesting to group related styles to the extent it improves maintainability and readability.
Prefer @scope over nesting when proximity should matter more than pure specificity. This is common in selectors that can be nested in any order, but the closest matching one (in element -> ancestor order) should win, e.g. theming classes.
For example this will not work as expected:
If .invert is nested within both .dark and .light, it will always resolve to dark mode as both rules have the same specificity.
Using @scope fixes this:
4. Interactivity
See accessibility (via npx -y modern-web-guidance@latest retrieve "accessibility") for focus rings, touch targets, and custom gestures.
5. Design Tokens and Theming
Use CSS custom properties on :root to define core design variables (colors, fonts, sizes, etc) used throughout the design, for visual consistency and to scale UI design across teams.
DO NOT specify nontrivial styling values inline. E.g. background: transparent or padding: 0 is ok, but background: #f06 or padding: .3em are not.
One exception is use cases where keeping code small and simple is far more important than long-term maintainability and evolution, such as testcases.
Typically these are organized in tiers, with each tier building upon the previous one. For example:
- Tier 1: Literal design tokens (e.g.
--color-blue-10,--color-gray-90,--font-sans-serif,--size-xletc) - Tier 2: Semantic design tokens (e.g.
--color-accent,--color-neutral,--font-body,--font-headingetc) - Tier 3: General UI design tokens (e.g.
--ui-border,--surface-bg-subtleetc) - Tier 4: Component-specific design tokens (e.g.
--button-bg-primary-hover,--button-border-color-secondaryetc)
The smaller the scope of the use case, the fewer tiers it needs. E.g. a quick demo or toy app are fine with one tier. Do not overengineer. Check for any existing conventions around naming and levels before inventing your own.
Theming
- Dark mode: see
dark-mode(vianpx -y modern-web-guidance@latest retrieve "dark-mode") andcomponent-specific-light-dark-theme(vianpx -y modern-web-guidance@latest retrieve "component-specific-light-dark-theme"). - Forced Colors Mode: see
color(vianpx -y modern-web-guidance@latest retrieve "color"). - Generating tints and shades: see
color(vianpx -y modern-web-guidance@latest retrieve "color"). - Theming browser-generated UI (selection, scrollbars, form controls): see
browser-ui-theming(vianpx -y modern-web-guidance@latest retrieve "browser-ui-theming").
6. Responsive design
See responsive-design (via npx -y modern-web-guidance@latest retrieve "responsive-design") for components that adapt to the space they are given, layouts that survive mobile browser UI appearing and disappearing, reserving space for media before it loads, and text that scales with the viewport.
7. Typography
See typography (via npx -y modern-web-guidance@latest retrieve "typography") for guidelines around readability, accessibility, and aesthetics of text on Web UIs.
8. Visual effects
See visual-effects (via npx -y modern-web-guidance@latest retrieve "visual-effects") for look & feel enhancements such as conveying depth, texture, and elevation, blending layers, aesthetically pleasing gradients, and flexible pattern backgrounds.
9. Shapes
- Use
corner-shape: squirclefor more aesthetically pleasing curves as a progressive enhancement over regular rounded corners. - Use elliptical
border-radius(e.g.,10px / 20px) for proportional curves without extra elements.
10. Transitions & animations
See motion (via npx -y modern-web-guidance@latest retrieve "motion"). For rendering performance (content-visibility, containment), see performance (via npx -y modern-web-guidance@latest retrieve "performance").
11. Generated content
See accessible-generated-content (via npx -y modern-web-guidance@latest retrieve "accessible-generated-content") for adding decorative or supplementary content with CSS without breaking screen readers or copy-paste.