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-guidanceChild State Based Styling
Historically, CSS selectors could only traverse downwards—you could style a child based on its parent, but not a parent based on its child. The :has() pseudo-class changes this, allowing you to conditionally style a container element depending on the presence or state of its descendants.
By combining :has() with state pseudo-classes (like :checked, :focus, :valid, :invalid, or :not()), you can build complex, interactive UI components entirely in CSS, without needing JavaScript to toggle "modifier" classes (like .is-active or .has-error) on parent elements.
This is particularly useful for components that need to respond to internal interactions, such as a localized theme toggle reacting to a checkbox (:checked), a form group highlighting an error (:invalid), or a card elevating when a child link is focused (:focus-within).
Implementing state-based container styling
MANDATORY: You must use the :has() selector on the container element to detect the specific state (e.g., :checked, :focus, :invalid) of its interactive child element.
To build a component that changes its styling based on a child's state:
- Define the default styling: Set CSS variables on the container to define its base state.
- Apply state-based overrides: Target the container with
:has([child-selector]:[state])and redefine the CSS variables for the active or alternate state.
Example: A component that changes theme based on a child toggle.
Performance tip: When using :has(), scope the selector to the most specific container possible (like .theme-card). Avoid anchoring it to very high-level elements like body:has(...) if the styling changes are localized, as broad :has() queries can trigger more layout recalculations.
Fallback strategies
Baseline status for :has(): Widely available. It's been Baseline since 2023-12-19. Supported by: Chrome 105 (Sep 2022), Edge 105 (Sep 2022), Firefox 121 (Dec 2023), and Safari 15.4 (Mar 2022).
If the state-based styling is critical to the user experience or page layout, you must provide a fallback for browsers that do not support the :has() selector. For purely decorative effects, :has() can be used as a progressive enhancement without a fallback.
MANDATORY: When implementing a fallback for critical features, you must use @supports not selector(:has(*)) in your CSS to define a traditional class-based fallback. If the critical state change relies on user interaction, you must also use a small inline script with CSS.supports() to toggle that class based on the equivalent JavaScript event (e.g., change, focus, blur) representing the state change.