Skip to content

Browser support ​

These components solve styling and layering with what browsers ship, not with JavaScript patches. The upside is small code, good accessibility, and no extra dependency. The cost is a floor: older browsers lose pieces.

The floor first.

BrowserMinimum version
Chrome, Edge123
Safari (including iOS)17.5
Firefox120

light-dark() sets that floor, because nearly every color variable is written as light-dark(light value, dark value). An older browser does not understand the function, drops the whole declaration, and the page is left without its colors.

What we actually use ​

Every row below appears in packages/core/styles; none of it is copied from the upstream checklist.

FeatureWhere it is usedChromeSafariFirefox
light-dark()Every theme color variable12317.5120
@layerFirst line of every stylesheet, declaring the five astryx.* layers9915.497
:has()Form state, lists, chat, and tabs, where a parent style depends on a child10515.4121
color-mix()Hover tints, sliders, code blocks, and other derived colors11116.2113
@propertyRegisters the custom property behind the press animation so it can animate as a number8516.4128
Popover APIMenus, tooltips, the command palette, and the toast viewport use native popover and popovertarget11417125
<dialog> and showModal()Dialogs, bottom sheets, and the lightbox3715.498
@starting-styleEntry animation start values for overlays, sheets, and toasts11717.5129
Container queries @containerPagination, steppers, and chat adapting to their own width10516110
text-wrap: balance / prettyBalanced headings, and body text that avoids a lone last word11417.5121
:dir()Direction checks in a few places, such as the slider12016.449
Logical propertiesPadding, margin, radius, and offsets written as padding-inline-start and friends8714.166

@property and @starting-style are "missing is fine": without them the press animation and the entry animation simply do not play, and the interface still works. light-dark() is what actually sets the floor.

The versions follow MDN and caniuse as of when this page was written. When in doubt, search caniuse for the feature name.

Below the floor ​

  • Colors drop out. light-dark() is not understood, --astryx-color-* resolves to nothing, and text may fall back to default black.
  • Overlays open, but may not sit next to the trigger. Without the native popover API, menus and tooltips still open; CSS fallback positioning is less exact.
  • Dialogs may not open at all. There is no fallback path when showModal() is unavailable.
  • Animations stop. Entry and press motion disappear; content still shows.

Nothing goes to a blank page. The visuals just stop matching the design, which is why older browsers are not supported.

Supporting older browsers anyway ​

We do not ship a fallback build for old browsers, and there is no bundled polyfill. Three options.

1. If your audience is old, hold the floor. Internal tools can usually push a browser update. That is the cheapest path.

2. Detect with @supports and supply your own colors. Check for light-dark() at the app entry and write fixed light values when it is missing:

css
@supports not (color: light-dark(#fff, #000)) {
  :root {
    --astryx-color-text-primary: #000000;
    --astryx-color-background-surface: #ffffff;
  }
}

That restores colors only. Overlay positioning and animation do not improve.

3. Needing full function on an old browser means changing code. That covers a popover fallback, a @starting-style fallback, or rewriting the affected overlays in your project. That work is outside this library.

Compatibility details already handled ​

  • iOS zooms the page on focus. On iOS, focusing an input whose font size is below 16px zooms the page. Every input component lifts its font size to at least 1rem on touch devices, using @supports (-webkit-touch-callout: none) so the rule only applies on iOS.
  • Reduced motion. One global prefers-reduced-motion: reduce rule flattens the distance, scale, and opacity deltas of entry and exit motion, and components add their own opt-outs. With the system setting on, animation does not play.
  • Scrollbars. Scrollbar styling lives in one file, scrollbars.css, implemented with the overlayscrollbars runtime dependency. Colors are derived from the text color with color-mix(), so dark mode follows.

Check your own browser ​

Paste this in the console to see which pieces your browser lacks.

js
const checks = {
  'light-dark()': CSS.supports('color', 'light-dark(#fff, #000)'),
  '@layer': CSS.supports('at-rule', '@layer a, b;'),
  ':has()': CSS.supports('selector(:has(*))'),
  'color-mix()': CSS.supports('color', 'color-mix(in oklab, red, blue)'),
  'Popover API': typeof HTMLElement.prototype.showPopover === 'function',
  '@container': CSS.supports('container-type', 'inline-size'),
}
console.table(checks)

Any false maps to a row in the table above. When light-dark() is false, page colors are effectively unusable.