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-card for cards and dashboard surfaces.
  • Use shadow-dropdown for menus and command palettes.
  • Use shadow-popover for floating contextual panels.
  • Use shadow-modal for dialogs and overlays.
  • Use shadow-button and shadow-button-hover for primary actions.
  • Use shadow-field-focus, shadow-field-error, and shadow-field-success for 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-lg in 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:

TokenTailwind classUse case
xsshadow-xsSubtle lift — interactive elements
smshadow-smLight elevation — small surfaces
cardshadow-cardDefault card and dashboard surface
card-hovershadow-card-hoverCard hover / raised state
dropdownshadow-dropdownDropdown menu elevation
popovershadow-popoverPopover panel elevation
modalshadow-modalModal 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:

TokenTailwind classUse case
buttonshadow-buttonResting button state
button-hovershadow-button-hoverHover button state
fieldshadow-fieldResting field state
field-hovershadow-field-hoverHover field state

State shadows

Field state shadows communicate validation without relying on color alone — they combine a coloured focus ring with elevation for accessibility:

TokenTailwind classResolved value
field-focusshadow-field-focus0 0 0 3px hsl(var(--ring) / 0.18), 0 1px 3px 0 rgb(0 0 0 / 0.07)
field-errorshadow-field-error0 0 0 3px hsl(var(--danger) / 0.15)
field-successshadow-field-success0 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:

TokenTailwind classResolved value
glow-primaryshadow-glow-primary0 0 0 4px hsl(var(--primary) / 0.18)
glow-dangershadow-glow-danger0 0 0 4px hsl(var(--danger) / 0.18)
glow-successshadow-glow-success0 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:

TokenTailwind classResolved value
field-darkshadow-field-dark0 1px 3px 0 rgb(0 0 0 / 0.3)
field-dark-hovershadow-field-dark-hover0 1px 4px 0 rgb(0 0 0 / 0.4)
field-focus-darkshadow-field-focus-dark0 0 0 3px hsl(var(--ring) / 0.25), 0 1px 4px 0 rgb(0 0 0 / 0.3)
field-error-darkshadow-field-error-dark0 0 0 3px hsl(var(--danger) / 0.2)
field-success-darkshadow-field-success-dark0 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-hover for interactive cards
  • Test shadows in light and dark mode

Don't

  • Use shadow-lg or 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-modal will 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 .dark class does not change shadow values. Use the shadow-field-*-dark variants when rendering fields on dark backgrounds.
  • Mixing raw Tailwind shadows with Byteion semantic shadows. Choose one system. Byteion tokens are themed and auditable; shadow-lg is not.

Developer tips

• Use semantic shadow names. 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.

Unlock Premium Patterns

Get access to copy-paste ready Pro blocks, SaaS templates, and advanced component patterns.

Explore Pro Components