Data attributes
The shared state-attribute vocabulary. Where it comes from, which attributes are Base UI's and which are opsinjs's, and why it is a versioned contract.
The short version
NOT IMPLEMENTED. This component does not exist in any released version of opsinjs. There is no package to install, no module to import and no props interface to generate code against. Everything on this page is a specification of intended behaviour and may change without notice. Do not write code against it.
This component is not built yet
PlannedRoadmapWhat “planned” means
Every stateful part will publish its state to the DOM as a data-* attribute,
so styling a state needs no JavaScript:
[data-open] { /* the popup is open */ }
[data-status="urgent"] { /* the value's clinical status */ }
[data-category="sleep"] { /* which metric family this belongs to */ }Two families of attributes exist and they come from different places. Base UI publishes the interaction state: open, closed, disabled, and the animation lifecycle. opsinjs publishes the semantic state: clinical status and category. The Reference → Data attributes table has no extracted rows yet, so the vocabulary you can rely on is the one on this page.
These attributes are covered by semver. Renaming one is a breaking change, exactly like renaming a prop. That is a deliberate commitment; see Versioning policy.
How it works
Base UI's attributes
opsinjs is built on Base UI, so its state contract passes straight through. The ones you will use constantly:
| Attribute | Present when |
|---|---|
data-open | The part is open |
data-closed | The part is closed |
data-disabled | The part is disabled |
data-starting-style | For one frame as the part enters |
data-ending-style | While the part is leaving, before it unmounts |
data-starting-style and data-ending-style are what make enter and exit
transitions work without an animation library. You style the from-state and the
to-state, and the element stays mounted long enough for the exit to run. See
Motion in practice.
opsinjs's attributes
These carry meaning rather than interaction, and they are the reason this page matters more here than it would in a general-purpose library.
| Attribute | Values | What it means |
|---|---|---|
data-slot | present, naming the part | On every part of every component. This is the stable styling and testing handle, the one attribute you can rely on being everywhere. |
data-status | steady · watch · attention · urgent | The clinical status of the value this part represents |
data-category | the metric family, e.g. heart · sleep | Which family the surface belongs to |
data-opsinjs-value | the unrounded magnitude | The machine-readable number behind a Value. A magnitude is not a reading. Nothing beside it names the unit, so read the unit from wherever the caller set it, and never export or print this attribute on its own. |
The component contract is closed at four: data-slot, data-status,
data-opsinjs-value and data-category. That is the same four the component
pages name, and no component adds a fifth. In particular there is no
data-opsin-shape. Card, Callout and Dialog draw the squircle with the CSS
corner-shape property, [corner-shape:var(--opsin-corner-shape)], and the
product theme does not stamp an attribute for it either, so the shape is a
token-driven switch you cannot select on.
data-opsinjs-not-implemented is not part of that contract. This documentation
site stamps it on its own not-built-yet markers, NotBuiltYet and StubNotice
in components/docs/stub.tsx, and on ComponentInstall in
components/docs/source.tsx when the component it installs is unbuilt, so
tooling can tell a specification from a shipped component without parsing prose.
No component emits it, and a consumer's installed DOM will never carry it.
The five data states that Data states defines
have no attribute today. There is no data-state in the DOM, so a surface's state
is legible only from the words it shows. Do not write CSS or a test against a
data-state selector: it would match nothing and fail silently.
data-status and data-category never appear on the same element with
overlapping visual effect. That is the two-axis
rule expressed in the DOM, and it is what the
stylelint plugin checks.
Do this
- Style states with attribute selectors rather than toggling classes. The component's DOM is already the source of truth.
- Use
data-statusto drive presentation, never to compute one. The status is assigned upstream by whoever owns the thresholds; the attribute reports it. - Assert on data attributes in tests.
[data-open]is a stable, documented contract; a generated class name is not. See Testing. - Read
data-opsinjs-not-implementedif you are writing tooling. It is the machine-readable way to ask "does this exist yet" without parsing prose.
Not this
- Do not set
data-statusyourself to force a colour. If you want a surface to look urgent without a value being urgent, you want a different component. Forcing the attribute puts a clinical claim in the DOM that nothing backs. - Do not invent attributes in the
data-opsinjs-*namespace. It is versioned; your additions will collide. Use your own prefix. - Do not rely on attribute order or on the absence of one you have not
read about.
data-closedbeing absent is not a promise thatdata-openis present. - Do not use
data-categoryfor theming an unrelated surface. A settings page tinted with the sleep category colour teaches the reader that the colour means nothing.
Gotchas
- Boolean-style attributes are present or absent, not
"true"or"false".[data-open]matches;[data-open="true"]will not. Base UI renders these as empty attributes. data-starting-stylelasts one frame. You cannot inspect it comfortably in devtools, which is why enter transitions look "broken" when they are actually working. See Motion in practice.data-ending-stylerequires the element to still be mounted. If you unmount on close yourself, the exit transition never runs and no error is reported.- Attribute selectors and Tailwind arbitrary variants disagree about
escaping.
data-[status=urgent]:bg-…works; quoting inside the arbitrary variant frequently does not. - Server-rendered markup carries these attributes too, which is what makes them safe to style with pure CSS. It also means, though, that an incorrect status is visible in view-source. Do not put anything in an attribute you would not put on screen.
Related
- Styling has the four hooks, of which this is the second.
- Reference → Data attributes will hold the generated list once the extractor reads the component sources. Its table is empty for now, so this page is the vocabulary.
- Clinical status semantics says what
each
data-statusvalue is allowed to mean. - Data states has the five states the design contract names and what each obliges the surface to show. They are a design contract, not a DOM attribute today.
Styling
className, data attributes, CSS variables and the style prop are the four override hooks, listed in their fixed order of preference, with the rule for choosing.
Composition and render
Merging an opsinjs part into your own element with the render prop. How props and refs are combined, and the three ways it goes wrong.