Shadows
20+ elevation and emphasis shadow tokens for every component state.
Overview
Byteion defines 25 shadow tokens covering elevation, component states, field validation, and glow effects. Each token is a CSS variable, so light mode, dark mode, and brand themes stay consistent without touching component files.
Shadows control perceived elevation across SaaS dashboards, admin panels, ERP and POS screens, ecommerce back offices, forms, cards, dropdowns, popovers, and modals. Tokenised shadows ensure a card on one page feels the same as a card on any other page.
When to use shadows
- Use
shadow-cardfor cards and dashboard surfaces. - Use
shadow-dropdownfor menus and command palettes. - Use
shadow-popoverfor floating contextual panels. - Use
shadow-modalfor dialogs and overlays. - Use
shadow-buttonandshadow-button-hoverfor primary actions. - Use
shadow-field-focus,shadow-field-error, andshadow-field-successfor accessible form states.
When not to use shadows
- Do not use shadows as the only layout separator — prefer
border-border,bg-card,bg-muted, and spacing for structure. - Do not use raw Tailwind shadows like
shadow-lgin shared Byteion components. - Do not stack multiple glow effects on the same element.
- Do not use intense decorative shadows in dense admin interfaces — keep elevation subtle.
Elevation shadows
Elevation tokens create visual depth. They are ordered from subtle to prominent:
| Token | Tailwind class | Use case |
|---|---|---|
| xs | shadow-xs | Subtle lift — interactive elements |
| sm | shadow-sm | Light elevation — small surfaces |
| card | shadow-card | Default card and dashboard surface |
| card-hover | shadow-card-hover | Card hover / raised state |
| dropdown | shadow-dropdown | Dropdown menu elevation |
| popover | shadow-popover | Popover panel elevation |
| modal | shadow-modal | Modal overlay elevation (highest) |
The shadow-inner-sm token (inset 0 1px 2px 0 rgb(0 0 0 / 0.06)) is available for inset / recessed effects.
Component shadows
Buttons and fields have dedicated shadow tokens for resting and hover states:
| Token | Tailwind class | Use case |
|---|---|---|
| button | shadow-button | Resting button state |
| button-hover | shadow-button-hover | Hover button state |
| field | shadow-field | Resting field state |
| field-hover | shadow-field-hover | Hover field state |
State shadows
Field state shadows communicate validation without relying on color alone — they combine a coloured focus ring with elevation for accessibility:
| Token | Tailwind class | Resolved value |
|---|---|---|
| field-focus | shadow-field-focus | 0 0 0 3px hsl(var(--ring) / 0.18), 0 1px 3px 0 rgb(0 0 0 / 0.07) |
| field-error | shadow-field-error | 0 0 0 3px hsl(var(--danger) / 0.15) |
| field-success | shadow-field-success | 0 0 0 3px hsl(var(--success) / 0.15) |
Glow shadows
Glow shadows add emphasis around active or important elements. Use sparingly — they are designed for focus accents, not decoration:
| Token | Tailwind class | Resolved value |
|---|---|---|
| glow-primary | shadow-glow-primary | 0 0 0 4px hsl(var(--primary) / 0.18) |
| glow-danger | shadow-glow-danger | 0 0 0 4px hsl(var(--danger) / 0.18) |
| glow-success | shadow-glow-success | 0 0 0 4px hsl(var(--success) / 0.18) |
Dark mode
Dark mode in Byteion is controlled by the .dark class, which overrides surface and text colour variables. Shadow values in the :root block remain the same — the .dark block does not redeclare --shadow-* variables.
For field states that need stronger contrast on dark backgrounds, Byteion provides dedicated dark variant tokens as separate classes:
| Token | Tailwind class | Resolved value |
|---|---|---|
| field-dark | shadow-field-dark | 0 1px 3px 0 rgb(0 0 0 / 0.3) |
| field-dark-hover | shadow-field-dark-hover | 0 1px 4px 0 rgb(0 0 0 / 0.4) |
| field-focus-dark | shadow-field-focus-dark | 0 0 0 3px hsl(var(--ring) / 0.25), 0 1px 4px 0 rgb(0 0 0 / 0.3) |
| field-error-dark | shadow-field-error-dark | 0 0 0 3px hsl(var(--danger) / 0.2) |
| field-success-dark | shadow-field-success-dark | 0 0 0 3px hsl(var(--success) / 0.2) |
Apply these classes explicitly when rendering fields on dark backgrounds. They do not toggle automatically — the .dark class by itself does not change shadow values.
Practical examples
HTML — Card with surface shadow:
<div class="rounded-card border border-border bg-card text-foreground shadow-card hover:shadow-card-hover transition-shadow">
<div class="p-surface-card">
<h3 class="text-lg font-semibold">Monthly Revenue</h3>
<p class="text-3xl font-bold text-primary">$48,200</p>
</div>
</div>
HTML — Dropdown with elevation:
<div class="rounded-button border border-border bg-card shadow-dropdown">
<a href="#" class="block px-inset-md py-inset-sm text-sm text-foreground hover:bg-muted">Profile</a>
<a href="#" class="block px-inset-md py-inset-sm text-sm text-foreground hover:bg-muted">Settings</a>
</div>
HTML — Form field states:
<!-- Focus state -->
<input class="rounded-input border border-border bg-background px-inset-md py-inset-sm text-foreground shadow-field-focus" />
<!-- Error state -->
<input class="rounded-input border border-danger bg-background px-inset-md py-inset-sm text-foreground shadow-field-error" aria-invalid="true" />
<!-- Success state -->
<input class="rounded-input border border-success bg-background px-inset-md py-inset-sm text-foreground shadow-field-success" />
React:
import { Card, Button, Input } from '@byteion/react';
export default function SettingsPanel() {
return (
<Card>
<Input label="Email" />
<Button>Save</Button>
</Card>
);
}
Vue:
<script setup>
import { Card, Button, Input } from '@byteion/vue';
</script>
<template>
<Card>
<Input label="Email" />
<Button>Save</Button>
</Card>
</template>
Customization
All shadow tokens are backed by CSS variables declared in packages/core/theme/variables.css. Override any token at runtime without editing component files:
:root {
--shadow-card: 0 2px 6px 0 rgb(0 0 0 / 0.05);
--shadow-dropdown: 0 6px 28px -6px rgb(0 0 0 / 0.1);
--shadow-modal: 0 28px 56px -16px rgb(0 0 0 / 0.2);
}
- Keep shadows subtle for dashboard UIs. Overly large shadows add visual noise to data-dense screens.
- Change tokens globally instead of editing every component — one CSS variable update propagates everywhere.
- Test both light and dark mode after changing shadow values. A shadow that looks correct on white may feel muddy on a dark surface.
Do and Don't
Do
- Use semantic shadow tokens —
shadow-card,shadow-dropdown,shadow-modal - Use elevation consistently within a screen
- Use state shadows alongside labels, helper text, and
aria-invalid - Apply
hover:shadow-card-hoverfor interactive cards - Test shadows in light and dark mode
Don't
- Use
shadow-lgor other raw Tailwind shadows in shared components - Use glow shadows as decoration everywhere — they are for emphasis
- Make every card look like a modal — use the correct elevation token
- Use shadow alone to communicate validation state — always pair with text
- Assume dark mode automatically adjusts shadow contrast
Common mistakes
- Too many elevation levels on one page. Stick to 2-3 distinct elevation levels (card, dropdown/popover, modal) to keep the visual hierarchy clear.
- Using modal-level shadows for cards. A card with
shadow-modalwill appear to float above other content, confusing the depth hierarchy. - Relying only on colour or shadow for validation. Always pair state shadows with helper text,
aria-invalid, and border colour changes for accessibility. - Forgetting dark mode contrast. The
.darkclass does not change shadow values. Use theshadow-field-*-darkvariants when rendering fields on dark backgrounds. - Mixing raw Tailwind shadows with Byteion semantic shadows. Choose one system. Byteion tokens are themed and auditable;
shadow-lgis not.
Developer tips
shadow-card tells you the purpose; shadow-lg only tells you the intensity.
• Hover shadows are subtle. The card hover shadow increases blur and spread but keeps the color neutral — hover:shadow-card-hover.
• Error shadows replace color alone. The focus ring on error fields uses both colour (--danger) and shadow for accessibility.
• Apply transition-shadow when using hover:shadow-card-hover or focus state shadows to avoid abrupt changes.
• Use dark variant tokens (shadow-field-focus-dark, shadow-field-error-dark) when building components that render directly on dark backgrounds.
• Audit shadow drift. Search for shadow-lg, shadow-md, shadow-xl, and shadow-2xl in component files to catch raw Tailwind shadow usage that should be replaced with Byteion tokens.Related
- Design Principles — Why tokens matter
- Design Tokens — Source of shadow values
- Button — Button elevation
- Input — Field focus and validation shadows
- Card — Surface elevation
- Dropdown — Floating menu elevation
- Modal — Overlay elevation
- Theming Guide — Customising CSS variables
Unlock Premium Patterns
Get access to copy-paste ready Pro blocks, SaaS templates, and advanced component patterns.
Explore Pro Components