Skip to content

Browser Support

Hydrateless targets the modern Baseline: the current and previous major versions of Chrome, Edge, Firefox, and Safari (desktop and iOS). Instead of reimplementing overlays, focus trapping, and light dismiss in script, the components lean on platform features that are now interoperable, and the enhancers fill the few remaining gaps.

Platform features by component

FeatureUsed byWithout it
Popover API (popover, popovertarget)Dropdown, menu submenus, popover, tooltip, comboboxRequired. Surfaces render inline (not in the top layer) and don't light-dismiss.
<dialog> with showModal() and closedbyModal, drawer, command palette host<dialog> is required. Without closedby, the enhancer adds backdrop dismissal; the no-JS baseline still supports Escape.
Invoker Commands (command/commandfor)Buttons that open dialogs and popovers with no JSRequired for declarative invokers. Where missing, call showModal() or the enhancer API from your own trigger.
CSS anchor positioning (anchor-name, position-area)Dropdown, menu, popover, tooltip, comboboxProgressive. supportsAnchorPositioning() is false and a small JS positioner runs (position: true).
light-dark() and color-schemeEvery color tokenRequired for automatic dark mode. See Dark Mode.
@layerThe whole stylesheet (reset, tokens, theme, components)Required. Layers are shipped natively, not flattened.
@starting-styleEntry transitions on overlays and toastsProgressive. Surfaces appear without the fade.
:has()Checkbox, switch, radio group, segmented control, tabs baselineRequired for the CSS-only selection styling.
:dir()Logical mirroring in drawer, slider, switch, disclosureProgressive. [dir="rtl"] selectors cover the same cases.
field-sizing: contentTextarea auto-growProgressive. The textarea keeps its rows.
MutationObserver, IntersectionObserverLive collections, the auto-loader, TOC scroll spyRequired for enhancement. The CSS baseline is unaffected.

The CSS build runs through postcss-preset-env (stage 1) and Autoprefixer, but deliberately leaves the features above native rather than polyfilling them, and disables the cascade-layers transform so the documented layer order survives.

Detection helpers

@hydrateless/enhancers exports the same checks the enhancers use:

ts
import { supportsPopover, supportsAnchorPositioning } from '@hydrateless/enhancers';

if (!supportsAnchorPositioning()) {
  // the JS fallback is positioning floating surfaces
}

What "no JavaScript" means here

Every component renders and is operable before any script runs, and the CSS baselines (tab panels by radio, menu submenus on hover and :focus-within, combobox list on :focus-within, <details> accordions, <dialog> and popover invokers) are what a user sees if JavaScript fails, is blocked, or is still loading. When an enhancer takes over it marks the root data-hl-ready and the baseline stands down. The tests in packages/e2e run every fixture twice, with JavaScript disabled and enabled, in Chromium, Firefox, and WebKit.

Testing matrix

EnginePlaywright projectNotes
ChromiumchromiumReference engine; anchor positioning and invokers both native.
FirefoxfirefoxExercises the JS positioning fallback where anchor positioning is absent.
WebKitwebkitExercises the Popover API and <dialog> paths on the Safari engine.

Tests that depend on a feature an engine hasn't shipped (for example Invoker Commands) call test.skip with the reason, so a run is never green because a path was silently skipped without saying so.

Not supported

  • Internet Explorer and legacy Edge (EdgeHTML).
  • Browsers without the Popover API. There is no polyfill path; the CSS renders, but dropdowns, tooltips, and popovers won't behave as overlays.
  • Server environments are supported only in the sense that every enhancer and the auto-loader are safe no-ops without a DOM; see Server-Side Rendering.

Released under the MIT License.