Browser support
bedrock's floor is Chrome 135, Firefox 144 and Safari 26.2, the releases where
each engine shipped invoker commands. A bedrock trigger is commandfor with no
JavaScript standing behind it, so under that floor a trigger is a button that
does nothing.
The last of the three landed in December 2025. Drag back from there and watch the library switch off.
Reading the grid
The track runs a little past today. Those last stops are arithmetic, thirty months from the day the last engine shipped, and each one says so when you land on it.
One markup, five stylesheets
<Tabs.Root className="t-stack" defaultValue="editor">
<Tabs.List className="t-tabs">
<Tabs.Trigger className="t-tab" value="editor">Editor</Tabs.Trigger>
<Tabs.Trigger className="t-tab" value="preview">Preview</Tabs.Trigger>
</Tabs.List>
<Tabs.Content className="t-tab-body" value="editor">Saved just now.</Tabs.Content>
</Tabs.Root>
Nothing in that snippet changes between the five renderings. Nothing in the grid above changes either, at any stop on the track: fifteen components, one set of class names, and a stylesheet swapping underneath. That is what unstyled buys you. Not the absence of CSS, but a redesign in 2029 that never opens a component.
What you are not betting on
The floor is a bet on the platform. Two things keep the bet off your code:
| No platform feature name appears in a public prop | Tooltip.Root takes delayDuration. Today that becomes interest-show-delay; if interestfor never standardises it becomes a timer. Your code does not change either way. |
Every non-Baseline CSS block sits behind @supports |
Feature detection lives in one module. Nothing throws on a browser that lacks a feature; it renders a documented downgrade. |
What "degrades" actually means
Three rows are load-bearing. Everything else is polish:
| tier | features | what happens without them |
|---|---|---|
| Required | <dialog>, invoker commands, Popover API |
Triggers do nothing, or overlays get clipped by ancestor overflow. There is no JavaScript fallback, by design. |
| Visual | @starting-style, allow-discrete, transition: overlay, interpolate-size, calc-size() |
Transitions are skipped or jump. Nothing becomes unusable or inaccessible. |
| Behavioural | anchor positioning, position-area, position-try, closedby, popover=hint, <details name> |
Placement, light dismiss, or single-open exclusivity are lost. The component still opens, closes, traps focus and is announced correctly. |
| Replaceable | interestfor |
A JavaScript timer fallback ships instead. Same behaviour, bigger bundle. |
Accessibility never lands in the degradation column. Focus trapping, dismissal,
naming and keyboard operation come from <dialog>, the popover stack and native
buttons, which shipped everywhere years ago. They are the last group in the
table below.
The matrix
The Here column is not a claim. It is measured in the browser you are reading this in, as you read it, and it is the one part of this page that works with JavaScript switched off, on a browser too old to run the grid above.
Dialog and AlertDialog
The floor lives here. Without these two, a trigger is an inert button. Every other group on this page is degradation; this one is absence.
| Feature | Here | Chrome | Firefox | Safari | If missing |
|---|---|---|---|---|---|
<dialog>.showModal() | · | 37 | 98 | 15.4 | Nothing renders as a dialog, and there is no fallback. It shipped everywhere in 2022, so in practice the invoker row below is what decides support. |
Invoker commands (commandfor) | · | 135 | 144 | 26.2 | Triggers do nothing at all. bedrock ships no click handler to stand in for them, by design, so this row is the real support floor. |
::backdrop | · | 37 | 47 | 15.4 | No dimmed overlay. The dialog still opens, traps focus and closes. |
<dialog closedby> | · | 134 | 141 | preview | Clicking the backdrop will not close the dialog even if you ask for it. Escape and the Close button are unaffected. |
Popover, Tooltip, HoverCard, Select and menus
Everything that opens next to the thing that opened it. Missing anchor positioning costs placement, not behaviour.
| Feature | Here | Chrome | Firefox | Safari | If missing |
|---|---|---|---|---|---|
| Popover API | · | 114 | 125 | 17 | No top-layer promotion, so overlays are clipped by ancestor overflow and fight z-index, the exact class of bug this library exists to remove. |
popover=hint | · | 151 | 153 | — | Tooltips ask for the ordinary popover stack instead, so opening a tooltip closes an open menu rather than layering above it. Asking is the whole fallback: the attribute's invalid-value default is manual, so an unasked-for hint would be a tooltip that light dismiss and Escape never reach. |
Anchor positioning (anchor-name) | · | 125 | 147 | 26 | Overlays render centred in the viewport instead of beside their trigger. Readable, usable, wrong-looking. |
position-area | · | 129 | 147 | 26 | side and align are ignored; default placement applies. |
Position fallbacks (position-try) | · | 128 | 147 | 26 | No automatic flip near a viewport edge, so an overlay can open partly offscreen instead of flipping to the other side. |
interestfor | · | 142 | — | — | Hover, focus and touch long-press intent fall back to JavaScript timers. Behaviour is equivalent; the bundle is bigger and the delay is not tunable from CSS. |
appearance: base-select | · | 135 | — | 27 | Select renders as a native OS dropdown: fully accessible, entirely unstyleable. Options with icons or two-line labels will not render as designed. |
Transitions and animation
Entry and exit only. Every row here fails to a jump cut; none of them fails to a broken component.
| Feature | Here | Chrome | Firefox | Safari | If missing |
|---|---|---|---|---|---|
@starting-style | · | 117 | 129 | 17.5 | Enter transitions are skipped, so things appear instantly. Exit transitions still play. |
transition-behavior: allow-discrete | · | 117 | 129 | 17.4 | Exit transitions are skipped: the element is removed from display immediately, so a fade-out never plays. |
transition: overlay | · | 117 | — | — | The element leaves the top layer the moment it closes, so its exit transition plays underneath other content instead of above it. |
interpolate-size | · | 129 | — | — | Height cannot animate to auto, so open/close is an instant jump instead of a slide. No measuring effect is added to compensate. |
calc-size() | · | 129 | — | — | Same as above, for sizes derived from the intrinsic one. |
State selectors and disclosure
How your CSS asks whether something is open, and how an accordion keeps one panel at a time.
| Feature | Here | Chrome | Firefox | Safari | If missing |
|---|---|---|---|---|---|
:open selector | · | 133 | 136 | 26.5 | Open-state styling on dialog, details and select silently does nothing. bedrock emits no data-state, so there is no fallback selector. |
:popover-open selector | · | 114 | 125 | 17 | Open-state styling on popovers silently does nothing. Note this is a separate selector from :open, which does not match popovers. |
<details name> | · | 120 | 130 | 17.2 | Accordion loses single-open exclusivity: every item can be open at once, and type="single" is not honoured. |
::details-content | · | 131 | 143 | 18.4 | The content wrapper cannot be styled or animated as one box. |
The parts that were already there
None of these are news, which is the point: they carry half the library and they carried it a decade ago. They are in the table because the timeline needs them. A component whose only requirement shipped in 2011 was never waiting for anything.
| Feature | Here | Chrome | Firefox | Safari | If missing |
|---|---|---|---|---|---|
<details> and <summary> | · | 12 | 49 | 6 | Nothing to degrade to: this is the disclosure widget itself, keyboard behaviour and all, and it has been in every engine since 2016. |
<select> | · | 1 | 1 | 1 | Older than CSS. Select is the one overlay in the library that has never had a moment when it did not work. |
<input type="range"> | · | 4 | 23 | 3.1 | The input falls back to a text field. Ancient everywhere that matters; listed because Slider rests on it entirely. |
<input type="checkbox"> | · | 1 | 1 | 1 | Nothing to degrade to. Switch is this element with role="switch", which is what the ARIA pattern asks for. |
<input type="radio"> | · | 1 | 1 | 1 | Nothing to degrade to. Grouping, arrow-key roving and single selection are the browser’s, from the shared name. |
<progress> | · | 6 | 6 | 6 | The element renders as an inline box with no bar. Announced as a progress bar regardless, because the role is the element. |
aspect-ratio | · | 88 | 89 | 15 | The box takes its content height. Radix ships a padding-bottom wrapper for exactly this case; bedrock does not, because the property has been everywhere since 2021. |
prefers-reduced-motion | · | 74 | 63 | 10.1 | Animations play for readers who asked the OS for stillness. The only row here whose absence is an accessibility problem rather than a cosmetic one. |
Testing
The Playwright suite runs Chrome only, deliberately. A cross-browser matrix today would report failures that are the documented state of the world rather than regressions in this library. It widens when the live table says the features have landed.