Free color tools · No signup · Everything runs in your browser
Web and UI

Relative CSS Colors, light-dark(), contrast-color(), and Progressive Enhancement

Build dynamic CSS color relationships with relative color syntax, light-dark(), and contrast-color() while keeping deterministic fallbacks and honest support boundaries.

Direct answer

Use relative color syntax when a state should remain mathematically tied to a source color. Use light-dark() for values selected by the active color scheme after opting into color-scheme. Use contrast-color() as a black-or-white helper, not a substitute for a full contrast audit. Declare measured fallbacks first and isolate each feature with @supports.

At a glance

Relative syntaxDerive channels from a source
Theme functionlight-dark()
Auto textcontrast-color()
SafetyFallback first

Use relative colors for real relationships

A relative color can reuse and calculate channels from an origin color.

CSS example
:root {
  --brand: #2563eb;
  --brand-hover: #1d4ed8;
}

@supports (color: oklch(from red l c h)) {
  :root {
    --brand-hover: oklch(from var(--brand) calc(l - .07) c h);
  }
}

Opt into the light and dark color schemes

light-dark() reacts to the used color scheme.

CSS example
:root {
  color-scheme: light dark;
  --surface: #ffffff;
}

@supports (color: light-dark(white, black)) {
  :root {
    --surface: light-dark(#ffffff, #0f172a);
  }
}

Use contrast-color() with measured caution

The function returns black or white, but a mid-tone background can still be problematic.

  1. 1

    Calculate a black-or-white fallback.

  2. 2

    Record the actual contrast ratio.

  3. 3

    Use contrast-color() only after the fallback declaration.

  4. 4

    Change the background when neither black nor white meets the intended requirement.

  5. 5

    Retest small text, large text, icons, and focus indicators separately.

Gate each feature independently

Support can differ between modern functions.

  • Test color-mix() separately from relative colors.
  • Test light-dark() separately from contrast-color().
  • Do not place unrelated features in one @supports condition unless they must fail together.
  • Use CSS.supports() for current-runtime diagnostics, not universal claims.

Review user color preferences

A generated palette is not the final rendered experience for every user.

  • Test forced-colors mode.
  • Review prefers-contrast behavior when the product supports it.
  • Do not suppress browser-adjusted colors without a documented reason.
  • Keep semantic meaning independent from color alone.

Track specification maturity

CSS Color 5 is still evolving.

Put the guide into practice

Compile relative colors and theme functions

Generate fallbacks and @supports-gated modern CSS.

Open the compiler

Frequently asked questions

Can relative color syntax reference a custom property?

Yes. The origin color can be var(--token), provided the resolved value is a valid color.

Does light-dark() replace prefers-color-scheme?

It can simplify individual color values, but media queries remain useful for non-color layout, images, and broader theme behavior.

Will contrast-color() always meet 4.5:1?

Do not assume it. It returns black or white and may not solve every mid-tone background or product-specific requirement. Measure the result.

Primary references

These sources support the standards and technical explanations in this guide. Color Pick recommendations and product-specific limitations are identified separately in the article.

  1. Using relative colorsMDN Web Docs
  2. light-dark() CSS functionMDN Web Docs
  3. contrast-color() CSS functionMDN Web Docs
  4. CSS Color Module Level 5W3C
Keep it colorful

Enjoying Color Pick?

Color Pick stays free, private, and account-free. Optional donations help cover hosting, testing, and new color tools.

Donate via SociabuzzOptional. Every tool remains free.