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 three tiers
One question decides a tier: who writes the markup on screen?
Tiers 1 and 2 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.btn--solid | 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, list rows, the page's rhythm and the control heights by remapping
--cell-pad-*, --card-pad-*, --list-pad-*,
--page-rhythm, --page-pad and --control-height*;
data-density="touch" loosens the same tokens and puts the default control at
44px. A component of your own that reads those tokens moves with them.