Elevation
A shadow shows how high a surface floats. A z-index layer decides what sits on top. They are separate tools.
<div style="box-shadow: var(--shadow-1); background: var(--surface-raised); padding: var(--space-24); border-radius: var(--radius-12)">shadow-1</div> <div style="box-shadow: var(--shadow-2); background: var(--surface-raised); padding: var(--space-24); border-radius: var(--radius-16)">shadow-2</div> <div style="box-shadow: var(--shadow-3); background: var(--surface-raised); padding: var(--space-24); border-radius: var(--radius-16)">shadow-3</div>
Shadow
Three steps, plus shadow-none. The higher the number, the higher the surface floats.
| Token | Use for | In Thykes |
|---|---|---|
shadow-none |
flat; clears an inherited shadow | — |
shadow-1 |
a card, a small lifted part | the selected segment, the switch thumb |
shadow-2 |
a panel floating over the page | menu, popover, tooltip, toast, date pickers, floating sidebar |
shadow-3 |
a modal surface | dialog, sheet |
Components set their own shadow; use the tokens for surfaces you build yourself.
Anyone with the link can view this file.
<tk-popover label="Share"> <tk-button slot="trigger">Share</tk-button> <p>Anyone with the link can view this file.</p> </tk-popover>
- Each step has a 1px rim in light and dark: a thin edge that keeps a raised surface crisp against a background of the same tone. The rim is black in light and white in dark.
- Dark theme: the shadow is stronger, and
surface/raisedis one step lighter than the page. The step is small, so the white rim draws most of the edge. - High contrast: the shadow is one faint layer with no rim. Floating panels draw a border instead.
Borders and shadows
The rim lands just outside the element, on top of any border it draws. An element with both a border and a shadow shows two edges.
- A floating panel you build: let the shadow draw the edge in light and dark, and add a border only in high contrast. Thykes panels work this way.
- A border that must stay visible in every theme, such as a control's 3:1 edge: keep the border and drop the shadow.
.my-panel { box-shadow: var(--shadow-2); }
[data-theme="high-contrast"] .my-panel { border: 1px solid var(--border-default); }
@media (prefers-contrast: more) {
:root:not([data-theme]) .my-panel { border: 1px solid var(--border-default); }
}The rim follows the theme, not the surface. On a dark surface in the light theme, such as a tooltip or a toast, it's invisible, so it never defines a dark surface's edge.
Stacking order
- Thykes overlays need no z-index. Dialogs, sheets, menus, popovers, tooltips, and the panels of selects and date pickers open in the browser's top layer, above everything on your page.
- Use a
z-*layer for your own elements that float over the page: a sticky header takesz-sticky, a custom dropdownz-dropdown. Toasts sit onz-toast. - Inside a component, stack locally. Set
isolation: isolateon the component's root and usez-index: 1,2,3inside it. The local values can't leak out and compete with the page's layers. - Never write
z-index: 9999. Pick the layer by its role.
The layers are on the
Accessibility
- A shadow is never the only boundary. It is soft and depends on the background, so it can't meet the 3:1 non-text minimum. If an element's edge matters, such as a control's or a field's, give it a border.
- Elevation doesn't replace focus. A lifted surface still needs a visible focus
indicator; see
Focus .
Do / Don't
| ✅ Do | ❌ Don't |
|---|---|
Take shadows from shadow-* |
Write a box-shadow by eye |
Use shadow-1 for cards, shadow-2 for floating panels, shadow-3 for modals |
Give a modal the same shadow as a card |
| Give an element a border when its edge must be seen | Rely on a shadow alone as a boundary |
| Border a floating panel in high contrast only | Give it a border and a shadow in every theme |
| Show hover by changing the surface or border | Add a stronger shadow step for hover |
Use z-* layers, and isolation: isolate inside a component |
Write z-index: 9999 |
Shadow
Three shadow steps, set per theme: the same names with different values in light, dark and high contrast.
| Token | CSS variable | Use for |
|---|---|---|
shadow-none |
--shadow-none |
no shadow |
shadow-1 |
--shadow-1 |
a card, a small lifted part |
shadow-2 |
--shadow-2 |
a floating panel: menu, popover, tooltip, toast |
shadow-3 |
--shadow-3 |
a modal surface: dialog, sheet |
In light and dark each step has three layers: two soft layers in a tinted dark gray
(gray-950 with alpha) and a 1px rim. High contrast has one faint layer and no rim.
Light
| Token | Value |
|---|---|
shadow-none |
none |
shadow-1 |
0 1px 3px 0 #1a1a1d0f, 0 1px 2px 0 #1a1a1d0a, 0 0 1px 0 #0000001a |
shadow-2 |
0 4px 12px -2px #1a1a1d1a, 0 2px 6px -1px #1a1a1d14, 0 0 1px 0 #0000001a |
shadow-3 |
0 12px 28px -6px #1a1a1d1f, 0 6px 16px -4px #1a1a1d1a, 0 0 1px 0 #0000001a |
Dark
| Token | Value |
|---|---|
shadow-none |
none |
shadow-1 |
0 1px 3px 0 #1a1a1d40, 0 1px 2px 0 #1a1a1d29, 0 0 1px 0 #ffffff26 |
shadow-2 |
0 4px 12px -2px #1a1a1d4d, 0 2px 6px -1px #1a1a1d3d, 0 0 1px 0 #ffffff26 |
shadow-3 |
0 12px 28px -6px #1a1a1d59, 0 6px 16px -4px #1a1a1d4d, 0 0 1px 0 #ffffff26 |
High contrast
| Token | Value |
|---|---|
shadow-none |
none |
shadow-1 |
0 1px 2px 0 #1a1a1d1f |
shadow-2 |
0 2px 4px 0 #1a1a1d29 |
shadow-3 |
0 4px 8px 0 #1a1a1d33 |
A shadow, rim included, is not a boundary: it can't meet the 3:1 non-text minimum. Give an
element a border when its edge must be seen. More in
Z-index
Eight stacking layers, named by role and spaced by 100. The same in every theme.
| Token | CSS variable | Value | Use for |
|---|---|---|---|
z-deep |
--z-deep |
-100 | decorative backgrounds behind the interface |
z-base |
--z-base |
0 | ordinary page content |
z-sticky |
--z-sticky |
100 | sticky headers, toolbars, sidebars |
z-dropdown |
--z-dropdown |
200 | menus, popovers, autocomplete |
z-overlay |
--z-overlay |
300 | the dimmed backdrop under a modal |
z-modal |
--z-modal |
400 | a modal or drawer, above its backdrop |
z-toast |
--z-toast |
500 | toasts |
z-tooltip |
--z-tooltip |
600 | tooltips, above everything |
Use these layers only for elements that float over the page. Inside a component, set
isolation: isolate on its root and stack with local 1, 2, 3. More in
Blur
One blur amount, for the optional frosted backdrop behind a dialog or sheet.
| Token | CSS variable | Value | Use for |
|---|---|---|---|
blur-backdrop |
--blur-backdrop |
8px | the backdrop blur behind a modal |
<tk-dialog heading="Frosted backdrop" backdrop-blur> The page behind this dialog is dimmed and blurred. </tk-dialog>
- Off by default. Turn it on with
backdrop-blurontk-dialog,tk-confirm-dialogortk-sheet(Dialog ,Sheet ). A live blur costs rendering time. - The dim does the work. The blur adds to the dimmed backdrop; where a browser can't blur, the dim stays.