Concepts
this site, not the packageWhat decides how an app writes CSS against this library. Everything else on this site is markup to copy.
The two tiers
Both tiers are CSS classes. There is no <DataTable>, no
<AppShell>, and there will not be either. The package ships the stylesheet, the
script, the icons, the token export, and a small C# surface for what markup cannot express: which
nav link is the current page, typed access to the browser API, and the service registration.
Cascade layers
The whole stylesheet is inside these layers, in this order. A later layer beats an earlier one regardless of specificity, which is why the reduced-motion block sits last.
| Layer | Part numbers | What lives there |
|---|---|---|
sedna.tokens | 00–04 | Tokens and the theme remap blocks |
sedna.base | 05–09 | Bare elements — html, body, a, code, headings |
sedna.frame | 10–29 | Tier 1 |
sedna.paint | 30–79 | Tier 2, then RTL, forced colours and print |
sedna.utilities | 80–89 | Single-purpose classes |
sedna.overrides | 90–99 | Density and reduced motion — the last word |
.card { padding: 12px } in your own file wins with no
!important and no compound selector — and use no !important against
the library either, because layer order inverts for important declarations and would make the
rule harder to override. If a rule here has to be overridden to make something work,
report it rather than out-specify it.
One control height
--control-height-sm (28px), --control-height (36px) and
--control-height-lg (44px) are the height of everything that can sit in a row with
another control: buttons, icon buttons, inputs, selects, the read-only value, the input group, the
stepper, the search box and the chip.
The token contract
- Every colour resolves through a token
No hex,
rgb()or colour keyword anywhere outside the token blocks. A test enforces it. - A theme is a token remap, never a selector override The light and colour-blind blocks contain values and no rules, which is why CSS load order does not affect them.
- Redefine only names the library declares
A new
--name risks colliding with a future token. Use an app-prefixed one until yours ships. - Some tokens are derived
--brand-tint, the three rings and--brand-gloware mixed from--brand, so redefining it carries all five.
Naming
| Shape | Rule |
|---|---|
.card, .btn-go | Semantic, lowercase-kebab, no app or vendor prefix |
.sedna-row, .sedna-scroll | Library-owned utilities that need a namespace |
.tab--active | A modifier is -- on the block |
go, warn, danger, info, secret | Semantic families, chosen by meaning across buttons, badges and alerts |
cyan, orange, teal | Categorical hues with no meaning at all |
.list or .menu silently gets both rule sets merged on upgrade — no
error, just a changed appearance. Every release lists the class names it adds, so grep your own
CSS before bumping.
Z-order
Shared by every app. Use one of these values for a new overlay — a test fails on any other. 0 to 3 are for local stacking inside a component and are not part of the scale.
| Layer | z-index |
|---|---|
| Topbar, floating action button | 60 |
| User widget | 200 |
| Collapsed-rail flyout | 400 |
| Drawer scrim | 480 |
| Drawer panel | 490 |
| Modal backdrop | 500 |
| Spotlight | 510 |
| Popover, dropdown menu, search panel | 550 |
| Toast | 600 |
| Hover hints, reconnect banner | 1000 |
.topbar and
.user-widget establish stacking contexts, so a panel nested in either is ordered
within it and cannot be lifted above the modal backdrop by z-index alone. The top
layer — anything promoted by popover or dialog.showModal() — ignores
z-index entirely and orders by promotion. And the drawer sits below the modal backdrop on
purpose, because a modal opened from inside a drawer has to cover it.
Direction and density
Every directional property in the library is a logical one, so the whole layout mirrors from
dir="rtl" on <html>. Use the switch in the header to check any page
on this site.
margin-inline-start, border-inline-end,
inset-inline and text-align: start and your own pages mirror for free;
only transform: translateX() and a background-position percentage have
no logical form, and both are handled here. data-density="compact" tightens table
cells, card padding and list rows by remapping --cell-pad-*,
--card-pad-* and --list-pad-*;
a component of your own that reads those tokens tightens with them.