Testing

CSS Variables: Browser Support, Syntax, Fallbacks

CSS Variables let you reuse CSS values via custom properties. Learn the syntax, browser support across Chrome 49+, Firefox 31+, Safari 9.1+, and IE fallbacks.

Author

Prince Dewani

May 6, 2026

CSS Variables are a W3C feature that lets you store values like colors, sizes, or fonts in custom properties prefixed with two dashes and reuse them through the var() function. They work in Chrome 49+, Edge 16+, Firefox 31+, Safari 9.1+ on macOS and iOS, Opera 36+, and Samsung Internet 5+, while Internet Explorer 5.5 to 11 never added support.

This guide covers what CSS Variables are, browser support, the syntax, fallbacks, how to check support, and known issues.

What is CSS Variables?

CSS Variables are a W3C CSS Custom Properties for Cascading Variables Module Level 1 feature. They define reusable custom properties on any selector, prefixed with two dashes such as --main-color: blue, and read them back through the var() function. Variables cascade and inherit like any other CSS property.

Which browsers does CSS Variables support?

CSS Variables work in every modern desktop and mobile browser, with Chrome shipping support in Chrome 49, Firefox in Firefox 31, Safari in Safari 9.1, and Edge in Edge 16. Internet Explorer 5.5 to 11 never added support, and Opera Mini still does not support custom properties.

Loading browser compatibility data...

CSS Variables compatibility in Chrome

Chrome supports CSS Variables from Chrome 49 on Windows, macOS, Linux, ChromeOS, and Android. Chrome 48 had CSS Variables disabled by default behind the Experimental Web Platform features flag, and Chrome 4 to 47 did not support custom properties at all. Chrome for Android picks up the same baseline from Chrome 49 on Android.

CSS Variables compatibility in Edge

Microsoft Edge supports CSS Variables from Edge 16 on Windows 10, with Edge 15 offering partial support that ignored a handful of edge cases around invalid values. Modern Chromium-based Edge 79+ inherits full Chromium support across Windows, macOS, and Linux. Legacy Edge 12 to 14 did not support custom properties.

CSS Variables compatibility in Firefox

Firefox supports CSS Variables from Firefox 31 on Windows, macOS, Linux, and Android, and Firefox for Android picks up the same baseline. Firefox 1 to 30 did not implement custom properties or the var() function. Firefox shipped support earliest among the major browsers, so Firefox-only test runs catch regressions before other engines.

CSS Variables compatibility in Safari

Safari supports CSS Variables from Safari 9.1 on macOS El Capitan, and from Safari 10 on iOS for iPhone and iPad. Safari 9.3 on iOS offered partial support before Safari 10 shipped full coverage. Safari 3.1 to 9 on macOS and iOS 3 to 9.2 did not support custom properties, and several calc() interactions were patched up through Safari 14.

CSS Variables compatibility in Opera

Opera supports CSS Variables from Opera 36 on desktop, with Opera 35 offering it disabled by default behind the Chromium experimental flag. Opera Mobile supports custom properties from Opera Mobile 80 on Android. Opera Mini does not support CSS Variables, since the Presto-based proxy renderer pre-processes pages on Opera servers and strips var() calls.

CSS Variables compatibility in Samsung Internet

Samsung Internet supports CSS Variables from Samsung Internet 5 on Galaxy phones and tablets. Older Samsung Internet 4 builds did not support custom properties, so visitors on stale Galaxy devices fall back to whatever the cascade resolves to before the var() declaration.

CSS Variables compatibility in Android Browser

Chrome for Android supports CSS Variables from Chrome for Android 49 on Android 5 and later, matching desktop Chrome. The legacy stock Android Browser, frozen at WebKit 4.4, does not support custom properties. Firefox for Android supports them from Firefox for Android 31, so most modern Android phones render CSS Variables natively.

CSS Variables compatibility in Internet Explorer

Internet Explorer does not support CSS Variables in any version. IE 5.5 to 11 predate the W3C Custom Properties for Cascading Variables Module spec, and Microsoft has retired Internet Explorer in favor of Chromium Edge. Sites that still serve IE traffic must compile variables to literal values with PostCSS or Sass before deploying.

Note

Note: CSS Variables fail silently on Internet Explorer and older Safari, iOS, and Android WebView builds. Test them on real browsers and OS with TestMu AI. Try TestMu AI free!

How does CSS Variables syntax work?

A CSS Variable is declared by writing a custom property prefixed with two dashes inside any selector, then read with the var() function. The browser resolves the value at runtime and inherits it down the DOM tree, so a value declared on :root is available to every descendant.

The example below declares three variables on :root, reads them inside .button, overrides one on .card.dark, and uses a variable inside calc().

/* Declare CSS Variables on :root for global scope */
:root {
  --main-color: #2962ff;
  --spacing: 1.5rem;
  --base-font: "Inter", system-ui, sans-serif;
}

/* Read them with var() and provide a fallback value */
.button {
  color: var(--main-color);
  padding: var(--spacing, 1rem);
  font-family: var(--base-font);
}

/* Override the variable on a child element */
.card.dark {
  --main-color: #ffffff;
}

/* Variables resolve through calc() and at-rules */
.grid {
  width: calc(100% - var(--spacing) * 2);
}

A few rules apply across every browser that ships CSS Variables:

  • Names must start with two dashes: A property like --main-color or --space-2 is a valid custom property; anything without the leading -- is treated as a regular CSS property and ignored.
  • Names are case-sensitive: --mainColor and --maincolor are two different variables, unlike standard CSS property names. A typo in casing returns an empty string from getComputedStyle, not a fallback.
  • :root sets global scope: Declarations on the :root pseudo-class apply to the html element, so every descendant reads the same value. Scoped overrides on a child element shadow the global value down its subtree.
  • var() takes an optional fallback: Write var(--space, 1rem) so the rule still resolves when the variable is missing or invalid in a supporting browser. The fallback is only used inside the var() function call.
  • Variables work inside calc(), clamp(), and at-rules: Compose values like calc(100% - var(--gutter) * 2) or clamp(1rem, var(--fluid), 3rem). Variables also cascade through @media and @supports query bodies normally.
  • JavaScript can read and write variables: Use element.style.setProperty('--name', value) to write and getComputedStyle(element).getPropertyValue('--name') to read, which makes runtime theme switching trivial.

How do you provide CSS Variables fallbacks for older browsers?

Pair every var() reference with either a literal fallback inside the function or a duplicate property declared above the var() line. The duplicate pattern is the only one that rescues Internet Explorer, since IE drops every declaration that contains a var() call, including the literal inside the function call itself.

  • Use the var() second argument for missing variables: Write color: var(--accent, #2962ff) so the rule still resolves to a literal value when --accent is undeclared at the call site. This rescues only modern browsers, not Internet Explorer.
  • Declare a duplicate property above var() for IE 11: Stack two declarations like color: #2962ff; followed by color: var(--accent, #2962ff);. IE 11 reads the first line and ignores the second; modern browsers apply the second and override the first.
  • Wrap blocks in @supports queries: Guard a rule that depends on custom properties with @supports (--probe: 0) so older browsers skip the block. Pair it with a flat fallback ruleset in the same stylesheet.
  • Run PostCSS Custom Properties at build time: The postcss-custom-properties plugin replaces every var() call with the matching :root literal, so the deployed stylesheet works in IE while the source file stays clean.
  • Compile to flat values with Sass or Less: Sites locked to IE traffic can move design tokens to Sass variables, compile to static values, and ship a separate stylesheet for that audience while modern browsers receive the var() build.
  • Detect support before applying advanced features: Use window.CSS.supports inside JavaScript to choose between a class-based theme switch and a custom-property theme switch. The class-based path keeps IE consistent with modern browsers.
...

How do you check if a browser supports CSS Variables?

Run a quick CSS.supports probe in DevTools, or wrap a block in @supports inside your stylesheet. Both paths give a one-line yes or no without changing visible styling.

  • Open DevTools: In Chrome, Edge, or Firefox, press F12 (or Cmd-Option-I on macOS) and switch to the Console tab.
  • Probe with CSS.supports: Paste window.CSS.supports("(--probe: 0)") and press Enter. A return value of true means the engine implements custom properties.
  • Inspect computed style: In the Elements panel, select the html element and confirm declared variables appear in the Computed pane with their resolved values.
  • Add an @supports block in your stylesheet: Wrap modern rules in @supports (--probe: 0) so older browsers skip them and use the flat fallback above instead.
  • Cross-check on real devices: Run the same console probe on iOS Safari, Android Chrome, and Samsung Internet, since older mobile builds and embedded WebViews lag desktop releases.

The console snippet below packages the probe and prints a one-line yes or no, then reads and writes a variable from JavaScript:

// Run in the browser DevTools Console
const ok = window.CSS && window.CSS.supports("(--probe: 0)");
console.log("CSS Variables:", ok ? "yes" : "no");

// Read or write a variable from JavaScript
const root = document.documentElement;
const main = getComputedStyle(root).getPropertyValue("--main-color");
console.log("Current --main-color:", main.trim());
root.style.setProperty("--main-color", "#ff5722");

If the probe returns false, fall back to a precompiled flat stylesheet built with PostCSS, Sass, or Less so the page still renders correctly.

What are the known issues with CSS Variables?

CSS Variables are well established now, but a few rough edges still trip developers up. The biggest hits are the no-fallback drop in Internet Explorer, animation limits without @property registration, and quirks around media query feature values.

  • IE 11 drops the entire declaration: Internet Explorer ignores any property whose value contains a var() call, even when the call has a literal fallback. The browser does not fall back to the second argument; it skips the declaration and uses the previous cascade value instead.
  • Plain custom properties cannot be animated: Animating var(--accent) between two colors with @keyframes does not interpolate, because the engine treats the property as a string. Register the variable through @property with a syntax type of color before animating.
  • Media query features cannot read variables: @media (max-width: var(--bp)) is invalid because the media feature syntax is parsed before custom properties resolve. Use literal lengths or the @custom-media draft for theme-driven breakpoints.
  • Names are case-sensitive: A typo like --MainColor and --maincolor produces silent failures. Standardize on lowercase kebab-case across the design system to avoid invisible bugs.
  • Older Safari calc() quirks: Safari 9.1 to 11 mishandle some calc() expressions that mix a custom property with units, returning the wrong pixel value or failing the calc entirely. Targeting Safari 12+ avoids the bulk of these.
  • WebView-based apps lag system browsers: Hybrid mobile apps shipped on top of an old Android System WebView or an old iOS WKWebView inherit the same gaps as the embedded engine, so CSS Variables may fail in production builds long after the system browser has updated.
  • Computed style returns trimmed strings: getComputedStyle(el).getPropertyValue('--name') returns a string with leading whitespace from the source declaration. Trim the return value before comparing or parsing it.

In my experience, the most surprising failure happens with Internet Explorer 11. A page renders in what looks like default browser styling because IE drops every declaration that contains a var() call, including the literal fallback inside the function. The fix is to declare a duplicate property above the var() line so IE picks up the duplicate while modern browsers apply the variable on the line below.

...

Citations

All CSS Variables version numbers and platform notes in this guide come from these primary sources:

Author

Prince Dewani is a Community Contributor at TestMu AI, where he manages content strategies around software testing, QA, and test automation. He is certified in Selenium, Cypress, Playwright, Appium, Automation Testing, and KaneAI. Prince has also presented academic research at the international conference PBCON-01. He further specializes in on-page SEO, bridging marketing with core testing technologies. On LinkedIn, he is followed by 4,300+ QA engineers, developers, DevOps experts, tech leaders, and AI-focused practitioners in the global testing community.

Open in ChatGPT Icon

Open in ChatGPT

Open in Claude Icon

Open in Claude

Open in Perplexity Icon

Open in Perplexity

Open in Grok Icon

Open in Grok

Open in Gemini AI Icon

Open in Gemini AI

Copied to Clipboard!
...

3000+ Browsers. One Platform.

See exactly how your site performs everywhere.

Try it free
...

Write Tests in Plain English with KaneAI

Create, debug, and evolve tests using natural language.

Try for free

Frequently asked questions

Did you find this page helpful?

More Related Hubs

TestMu AI forEnterprise

Get access to solutions built on Enterprise
grade security, privacy, & compliance

  • Advanced access controls
  • Advanced data retention rules
  • Advanced Local Testing
  • Premium Support options
  • Early access to beta features
  • Private Slack Channel
  • Unlimited Manual Accessibility DevTools Tests