modern-css

Installation
SKILL.md

Modern CSS

Modern CSS rules for creating robust, responsive and accessible UIs.

The rules work best when you apply a progressive enhancement approach. The CSS features are within Baseline Newly Available. Thanks to Interop, most are within Widely Available.

The rules, constraints, and examples are not intended to be prescriptive. Use your best judgment and consider the specific needs of the project when applying them. However, if you use a rule, apply it consistently.

Rules

Architecture

Organizing styles (@layer)

  • Rule: Use @layer to organize groups of styles.
  • Constraint: Avoid global styles outside of layers.
  • Rationale: Prevents style conflicts through managing specificity.
  • References: @layer on MDN.
  • Example:
@layer elements, components;

@layer elements {
  a {
    text-decoration-skip-ink: auto;
  }
}

Encapsulating styles (@scope)

  • Rule: Use @scope to encapsulate styles.
  • Constraint: Avoid custom element and components styles that are outside of a scope.
  • Rationale: Prevents styles from bleeding into other components because selectors are scoped to the component.
  • References: @scope on MDN.
  • Example:
@scope (.card) {
  h2 {
    font-size: var(--large);
  }
}

Nesting rules and at-rules (&)

  • Rule: Use & for nesting rules and at-rules.
  • Constraint: Avoid unnested selectors (e.g. a {} a:hover {}).
  • Rationale: Clarifies the relationship between the nested selector and the parent selector.
  • References: & nesting selector on MDN.
  • Example:
a {
  color: var(--blue);

  &:hover {
    text-decoration: underline;
  }

  @container (width > 20em ) {
    place-self: center;
  }
}

Relational styles (:has())

  • Rule: Use :has() for relational styles.
  • Constraint: Avoid .has--like class names (e.g. .has-img {}).
  • Rationale: Creates "smart" components that adapt to relationships.
  • References: :has() on MDN.
  • Example:
.card {
  &:has(img) {
    grid-template-rows: auto 1fr;
  }
}

Additive properties (:not() and 20em < width <= 40em ))

  • Rule: Use :not() and ranged queries (e.g. @media (20em < width <= 40em)) to create additive styles.
  • Constraint: Avoid overriding styles (e.g. div { margin: 1rem; &:first-child { margin-block-start: 0; } })
  • Rationale: Simplifies the mental model because you don't have to keep track of which styles are being overridden.
  • References: :not() on MDN, media query range syntax on MDN.
  • Example:
.card {
  /* Apply to all conditions */
  color: red;

  /* Apply based on the selector */
  &:not(:first-child) {
    margin-block-start: var(--medium);
  }

  /* Apply based on non-overlapping container conditions */
  @container example (width <= 20em) {
    background-color: var(--primary);
  }

  @container example (20em < width <= 40em ) {
    background-color: var(--secondary);
  }

  @container example (width > 40em ) {
    background-color: var(--tertiary);
  }
}

Typography

Fluid type sizes (clamp())

  • Rule: Use clamp() for font sizes to create harmonious rhythmic scales that are appropriate to the screen size, e.g. Major Second (1.125) on narrow viewports and Major Third (1.25) wide ones.
  • Constraint: Avoid fixed font sizes (e.g., px, rem) and central values without a rem addition (e.g. clamp(1.75rem, 5cqi, 2.25rem)).
  • Rationale: Ensures text is appropriately sized across different viewport sizes and can be zoomed for accessibility.
  • References: clamp() on MDN, Responsive design: seams & edges. and Designing with fluid type scales .
  • Example:
:root {
  --medium: clamp(1.3125rem, 1.1821rem + 0.6522cqi, 1.6875rem);
  --large: clamp(1.75rem, 1.5761rem + 0.8696cqi, 2.25rem);
}

Widow and orphan words (text-wrap)

  • Rule: Use text-wrap with pretty or balance to avoid widow and orphan words.
  • Constraint: Avoid default wrapping outside of inputs and text areas.
  • Rationale: Improves the readability and aesthetics of text blocks.
  • References: text-wrap on MDN.
  • Example:
h1,
h2,
h3 {
  text-wrap: balance;
}

p {
  text-wrap: pretty;
}

Colors

Perceptual uniform lightness (oklch())

  • Rule: Use oklch() for all colors.
  • Constraint: Avoid hex, rgb(), hsl() and other color formats.
  • Rationale: Easier to maintain perceptual uniform lightness to ensure text is accessible regardless of background color.
  • References: oklch() on MDN.
  • Example:
:root {
  --success: oklch(40% 0.15 150deg);
  --danger: oklch(40% 0.2 25deg);
}

Respecting color preferences (color-scheme)

  • Rule: Use color-scheme and light-dark() to support color schemes.
  • Constraint: Avoid hardcoding colors that don't adapt to light and dark modes.
  • Rationale: Improves accessibility by respecting a person's preference for light or dark mode.
  • References: color-scheme on MDN, light-dark() on MDN.
  • Example:
body {
  color-scheme: light dark;
  background-color: light-dark(oklch(98% 0.03 250deg), oklch(14% 0 0deg));
}

Relative color functions (oklch(from /* .. */) & color-mix() )

  • Rule: Use relative color syntax (e.g. oklch(from var(--primary) l + 10%)) and functions (e.g. color-mix()) to create color relationships.
  • Constraint: Avoid hardcoding colors that relate to other colors.
  • Rationale: Creates a cohesive color palette that is easier to maintain and adjust.
  • References: relative colors on MDN, color-mix() on MDN.
  • Example:
button {
  background-color: var(--primary);

  &:hover {
    background-color: oklch(from var(--primary) l c calc(h - 10deg));
  }
}

Layout

Container queries and units (@container, cqi etc.)

.card {
  container: card / inline-size;
  padding: 2cqi;

  p {
    @container card (width > 30cqi) {
      place-self: center;
    }
  }
}

Intrinsic sizing (*-content)

  • Rule: Use intrinsic sizing (e.g. max-inline-size: fit-content, block-size: max-content).
  • Constraint: Avoid fixed sizes (e.g. width: 300px, height: 200px) for content elements.
  • Rationale: Improves flexibility and prevents overflow issues as content determines its own size.
  • References: fit-content on MDN, max-content on MDN.
  • Example:
nav {
  max-inline-size: fit-content;
}

Motion

Respecting motion preferences (prefers-reduced-motion)

  • Rule: Use prefers-reduced-motion: no-preference when applying large animations and transitions.
  • Constraint: Avoid prefers-reduced-motion: reduce.
  • Rationale: Respects a person's preferences for motion to prevent motion sickness and improve accessibility.
  • References: prefers-reduced-motion on MDN.
  • Example:
@media (prefers-reduced-motion: no-preference) {
  .hero {
    animation: bounce-in 0.5s ease;
  }
}

Example in practice

This site follows the rules. Study the implementation on GitHub for concrete examples of each:

Installs
10
GitHub Stars
2
First Seen
Mar 13, 2026