Elevation
Elevation tokens are the shadows that lift a surface above the page, the focus effects on controls, and the overlays that dim the page behind a modal. Use them to show what floats and what has focus.
Beta. All shadows except one are Figma effect styles; regular-shadow-small is interim because Figma only has it in a remote library (TD-14, FC-16). The focus effects measure about 1.1:1 against white and need an approved accessible ring before Stable (C5). The overlay colours are Figma variables.
Overview
Nuton is a flat, light interface. Most surfaces sit on the page with a 1px Stroke/Soft-200 border; a shadow says that something floats above the content, such as a menu, a popover or the command search. The higher the surface, the softer and wider its shadow.
Regular shadows: one soft layer
x-smallsmallmediumCustom shadows: layered, with a hairline ring
x-smallsmallmediumlargetooltipchatRegular shadows are a single soft drop. Custom shadows stack several drops with a 1px ring and an inner bottom edge, so a surface reads as raised even without a border. Today components use the regular set; the custom set has no component yet.
Tokens
Every value below is generated from @nuton/tokens. Shadows are Figma effect styles under components/; the group prefix is dropped in the name (custom-shadows/large becomes --nu-shadow-custom-shadows-large).
Regular shadows
| Preview | Token | Value | Source |
|---|---|---|---|
--nu-shadow-regular-shadow-x-small | 0px 1px 2px 0px #0a0d1408 | FigmaEffectStyle/components/regular-shadow/x-small | |
--nu-shadow-regular-shadow-small | 0px 2px 4px 0px #1b1c1d0a | Interim | |
--nu-shadow-regular-shadow-medium | 0px 16px 32px -12px #0e121b1a | FigmaEffectStyle/components/regular-shadow/medium |
regular-shadow-small is interim: Chart legend dots and Avatar status badges use the remote style regular-shadow/small, which has no local Figma effect style yet (TD-14, FC-16).
Custom shadows
| Preview | Token | Value | Source |
|---|---|---|---|
--nu-shadow-custom-shadows-x-small | 0px 1px 2px 0px #3333330a, 0px 2px 4px 0px #3333330a, 0px 4px 8px -2px #3333330f, 0px 0px 0px 1px #3333330a, inset 0px -1px 1px -0.5px #3333330f | FigmaEffectStyle/components/custom-shadows/x-small | |
--nu-shadow-custom-shadows-small | 0px 1px 3px -1.5px #33333329, 0px 5px 5px -2.5px #33333314, 0px 12px 6px -6px #33333305, 0px 16px 8px -8px #33333303, 0px 0px 0px 1px #3333330a, inset 0px -0.5px 0.5px 0px #33333314 | FigmaEffectStyle/components/custom-shadows/small | |
--nu-shadow-custom-shadows-medium | 0px 48px 48px -24px #3333330a, 0px 24px 24px -12px #3333330a, 0px 12px 12px -6px #3333330a, 0px 6px 6px -3px #3333330a, 0px 3px 3px -1.5px #33333305, 0px 1px 1px 0.5px #3333330a, 0px 0px 0px 1px #3333330a, inset 0px -1px 1px -0.5px #3333330f | FigmaEffectStyle/components/custom-shadows/medium | |
--nu-shadow-custom-shadows-large | 0px 96px 96px -32px #3333330f, 0px 48px 48px -24px #3333330a, 0px 24px 24px -12px #3333330a, 0px 12px 12px -6px #3333330a, 0px 6px 6px -3px #3333330a, 0px 3px 3px -1.5px #33333305, 0px 1px 1px 0.5px #3333330a, 0px 0px 0px 1px #3333330a, inset 0px -1px 1px -0.5px #3333330f | FigmaEffectStyle/components/custom-shadows/large | |
--nu-shadow-custom-shadows-tooltip | inset 0px -1px 1px -0.5px #1717170f, 0px 0px 0px 1px #ebebebff, 0px 48px 48px -24px #1717170a, 0px 24px 24px -12px #1717170a, 0px 12px 12px -6px #1717170a, 0px 6px 6px -3px #1717170a, 0px 3px 3px -1.5px #17171705, 0px 1px 1px 0.5px #1717170a | FigmaEffectStyle/components/custom-shadows/Tooltip | |
--nu-shadow-custom-shadows-chat | 0px 0px 0px 1px #17171705, 0px 1px 1px -0.5px #17171705, 0px 3px 3px -1.5px #17171705, 0px 4px 6px -10px #17171705 | FigmaEffectStyle/components/custom-shadows/Chat |
Tooltip and Chat keep Figma's capitalised style names (custom-shadows/Tooltip, custom-shadows/Chat); the casing clean-up is part of TD-09.
Focus effects
| Preview | Token | Value | Source |
|---|---|---|---|
--nu-shadow-buttons-primary-focus | 0px 0px 0px 4px #476cff1a, 0px 0px 0px 2px #ffffffff | FigmaEffectStyle/components/buttons/primary-focus | |
--nu-shadow-buttons-important-focus | 0px 0px 0px 4px #99a0ae29, 0px 0px 0px 2px #ffffffff | FigmaEffectStyle/components/buttons/important-focus | |
--nu-shadow-buttons-error-focus | 0px 0px 0px 4px #fb37481a, 0px 0px 0px 2px #ffffffff | FigmaEffectStyle/components/buttons/error-focus |
Each is a 2px white gap plus a 4px tinted halo. On white the halo is barely visible (about 1.1:1, C5); see Accessibility.
Component shadows
| Preview | Token | Value | Source |
|---|---|---|---|
--nu-shadow-fancy-buttons-neutral | 0px 0px 0px 1px #242628ff, 0px 1px 2px 0px #1b1c1d7a | FigmaEffectStyle/components/fancy-buttons/neutral | |
--nu-shadow-fancy-buttons-primary | 0px 0px 0px 1px #1daf61ff, 0px 1px 2px 0px #0e121b3d | FigmaEffectStyle/components/fancy-buttons/primary | |
--nu-shadow-fancy-buttons-error | 0px 0px 0px 1px #e93544ff, 0px 1px 2px 0px #0e121b3d | FigmaEffectStyle/components/fancy-buttons/error | |
--nu-shadow-fancy-buttons-stroke | 0px 0px 0px 1px #ebebebff, 0px 1px 3px 0px #0e121b1f | FigmaEffectStyle/components/fancy-buttons/stroke | |
--nu-shadow-toggle-switch | 0px 2px 4px 0px #0e121b08, 0px 6px 10px 0px #0e121b0f | FigmaEffectStyle/components/toggle/switch |
Fancy-button shadows come from Figma but no @nuton/ui component uses them yet. toggle-switch lifts the switch thumb.
Overlays
| Preview | Token | Value | Source |
|---|---|---|---|
--nu-overlay-gray | #3333333d | FigmaMapped/overlay/overlay-gray | |
--nu-overlay-slate | #2b303b3d | FigmaMapped/overlay/overlay-slate |
Figma names these overlay/overlay-gray and overlay/overlay-slate; the repeated prefix is collapsed in code (TD-05, rename queued in TD-09). Overlays sit on --nu-z-overlay, under the dialog on --nu-z-modal (code-only, see Layout).
Usage
/* A floating panel: border + shadow, like the Nuton menus */
.lesson-popover {
border: 1px solid var(--nu-stroke-soft-200);
border-radius: var(--nu-radius-16);
background: var(--nu-bg-white);
box-shadow: var(--nu-shadow-regular-shadow-medium);
}
/* The dimmed page behind a modal */
.lesson-dialog__overlay {
position: fixed;
inset: 0;
background: var(--nu-overlay-gray);
z-index: var(--nu-z-overlay);
}/* app.css */
@import "tailwindcss";
@import "@nuton/tokens/tokens.css";
@import "@nuton/tokens/tailwind-v4.css";
<div className="rounded-16 border border-stroke-soft-200 bg-bg-white shadow-regular-shadow-medium">…</div>
<div className="fixed inset-0 bg-overlay-gray" />// tailwind.config.js
module.exports = { presets: [require('@nuton/tokens/tailwind-v3-preset')] }
<div className="rounded-16 border border-stroke-soft-200 bg-bg-white shadow-regular-shadow-medium">…</div>
<div className="fixed inset-0 z-overlay bg-overlay-gray" />Every shadow is a shadow-* utility in both Tailwind outputs (for example shadow-custom-shadows-tooltip, shadow-toggle-switch). Overlays are colours: bg-overlay-gray, bg-overlay-slate. The v3 preset also has z-overlay; the v4 theme has no z-index utilities, so use var(--nu-z-overlay) there.
Choosing a token
- Resting controls (text areas, digit inputs, key icons, avatar groups):
regular-shadow-x-small, always with a border. - Floating panels (dropdown menus, combobox lists, command search):
regular-shadow-mediumwith a 1px Stroke/Soft-200 border. - Tiny marks on busy backgrounds (chart legend dots, status badges):
regular-shadow-small(interim). - Focus: the
buttons/*-focuseffect that matches the control's intent (important for neutral, primary, error). Don't use a focus effect for elevation. - Behind a modal:
overlay-gray, the one in use.overlay-slateis a cooler alternative with no documented use yet.
In use
Real @nuton/ui components, each reading its shadow from a token.
Floating menu
Open the menu. The panel uses regular-shadow-medium and a border; the switch thumb uses toggle-switch.
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button appearance="stroke" size="sm" trailingIcon={<RiArrowDownSLine />}>Lesson actions</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="start">
<DropdownMenuItem leading={<RiEditLine />} onSelect={edit}>Edit lesson</DropdownMenuItem>
<DropdownMenuItem leading={<RiShareLine />} onSelect={share}>Share with space</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuSwitchItem leading={<RiBellLine />} checked={notify} onCheckedChange={setNotify}>
Notify students
</DropdownMenuSwitchItem>
</DropdownMenuContent>
</DropdownMenu>Overlay behind a modal
Open the search. overlay-gray dims the page and the panel floats on regular-shadow-medium. Press Escape to close; focus returns to the button.
<Button appearance="stroke" size="sm" leadingIcon={<RiSearch2Line />} onClick={() => setOpen(true)}>
Search lessons
</Button>
<CommandMenu open={open} onOpenChange={setOpen} label="Search Nuton" placeholder="Search lessons and spaces…">
<CommandMenuEmpty>No results</CommandMenuEmpty>
<CommandMenuGroup heading="Actions">
<CommandMenuItem leading={<RiAddLine />} onSelect={createLesson}>Create lesson</CommandMenuItem>
</CommandMenuGroup>
<CommandMenuGroup heading="Lessons">
<CommandMenuItem leading={<RiBookOpenLine />} onSelect={openLesson}>Photosynthesis</CommandMenuItem>
<CommandMenuItem leading={<RiFolderLine />} description="12 lessons" onSelect={openSpace}>Biology 101</CommandMenuItem>
</CommandMenuGroup>
</CommandMenu>Resting fields and focus
The text area rests on regular-shadow-x-small. Tab into either field to see the buttons/important-focus effect.
Shown on the lesson card.
<Field label="Lesson title">
<Input size="sm" defaultValue="Photosynthesis" />
</Field>
<Field label="Summary" hint="Shown on the lesson card.">
<Textarea rows={3} defaultValue="How plants turn light into energy." />
</Field>Focus effects by intent
Tab through the buttons. Each intent has its own focus effect: important (neutral), primary and error.
<Button appearance="stroke">Save draft</Button>
<Button intent="primary" appearance="stroke">Invite students</Button>
<Button intent="error" appearance="stroke">Remove lesson</Button>Small surfaces
<KeyIcon icon={<RiBookOpenLine />} label="Lessons" /> // regular-shadow-x-small
<ChartLegend color="blue">Completed</ChartLegend> // regular-shadow-small (interim)
<ChartLegend color="orange">In progress</ChartLegend>
<SwitchIndicator checked /> // toggle-switchWhere components use each token
| Token | Used by |
|---|---|
--nu-shadow-regular-shadow-x-small | Textarea, Digit input, inline Input when invalid, Key icon (stroke), compact Avatar group, menu media frames |
--nu-shadow-regular-shadow-small | Chart legend dot; Avatar status badges (see the warning below) |
--nu-shadow-regular-shadow-medium | Dropdown menu, Combobox list, Command menu panel |
--nu-shadow-buttons-important-focus | Button (neutral), Input, Textarea, Digit input |
--nu-shadow-buttons-primary-focus | Button (primary) |
--nu-shadow-buttons-error-focus | Button (error); invalid Input, Textarea and Digit input |
--nu-shadow-toggle-switch | Switch thumb in menu switch items |
--nu-overlay-gray | Command menu backdrop |
Custom shadows, fancy buttons, --nu-overlay-slate | No @nuton/ui component yet |
Fixed: Avatar status badge shadow
Avatar used to apply regular-shadow-small through filter: drop-shadow(). That function doesn't accept the token's spread value, so browsers dropped it. The badge's white ring now uses box-shadow, which renders the token as designed.
Accessibility
Focus visibility
The buttons/*-focus effects measure about 1.1:1 against white, below the 3:1 Stable gate (C5). Components use them as Figma specifies. An accessible ring is ready as --nu-focus-ring-color (State/Primary/Base, 5.59:1) but isn't applied until it's approved.
Shadows never carry meaning alone
Shadows are faint and vanish in Windows High Contrast (forced-colors). Floating panels also have a 1px border, and Button, Input and menu items switch to a system Highlight outline there, so focus and edges stay visible.
Overlays and modals
overlay-gray is decoration; it doesn't make a page inert. The Command menu traps focus, closes on Escape and returns focus to its opener. Any custom modal on an overlay must do the same.
You must: pair a floating surface's shadow with a border; keep the component's focus effect (don't remove it with box-shadow: none); manage focus in anything shown on an overlay.
Guidelines and related
Use a shadow for
- Surfaces that float over content: menus, popovers, the command search.
- A light lift on resting controls that need to read as tappable, like text areas and key icons.
- Focus, through the component's own focus effect.
Don't use a shadow for
- Grouping content on the page. Use a border or a background such as Bg/Weak-50.
- Showing state such as selected or invalid. Use colour and text.
- Emphasis. A heavier shadow doesn't make an action more important; Button appearance does.
Do and don't
x-smallmediumlargelarge