Concepts
this site, not the packageSix things that decide 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 six layers, in this order.
| Layer | Part numbers | What lives there |
|---|---|---|
dr.tokens | 00–04 | Tokens and the theme remap blocks |
dr.base | 05–09 | Bare elements — html, body, a, code, headings |
dr.frame | 10–29 | Tier 1 |
dr.paint | 30–79 | Tier 2, then RTL, forced colours and print |
dr.utilities | 80–89 | Single-purpose classes |
dr.overrides | 90–99 | Density and reduced motion — the last word |
.card { padding: 12px } in your own file wins against anything, with no
!important and no compound selector. If a rule here has to be overridden to make
something work, that is a design problem worth reporting rather than a specificity puzzle.
A later layer beats an earlier one regardless of specificity. That is what makes
.text-end at (0,1,0) win against .table td at (0,1,1), and it is why the
reduced-motion block sits in the last layer — motion has to be switchable off whatever declared it.
!important, and you should not use one against it.
Inside a layer an !important declaration becomes harder to override rather
than easier, because layer order inverts for important declarations.
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 |
.dr-row, .dr-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 a consuming app can grep
its 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 and 1 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 |
| Dropdown menu, palette, popover | 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. And the top
layer — anything promoted by popover or dialog.showModal() — ignores
z-index entirely and orders by promotion, which is why the palette and the confirmation dialog
carry no rung of their own.
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.
transform: translateX() has no logical form, and a
background-position percentage is always measured from the physical left edge.
Anything else you write with margin-inline-start,
border-inline-end, inset-inline and text-align: start
mirrors for free. data-density="compact" tightens table and cell padding; tighten
your own page-specific components alongside it.