A nocturnal, high-density console for tabletop RPG — pink-neon command energy over deep ink, built to feel native, not webby.
This page is the migration contract for the panel: tokens first, shared primitives second, local styling only when a domain surface needs a real exception.
src/styles/theme.ts
ui · dashboard · media
canvas · charts · VTT · Discord
Deep ink for structure, a single hot pink for command. The arcane violet appears only inside glows and gradient meshes — never as a fill.
The ink ramp every layer is built on
surface-0App background, the deepest layer
surface-1Sidebars, top bars, sunken regions
surface-2Default card / panel surface
surface-3Nested panels, popovers, menus
surface-4Inputs, the highest interactive layer
surface-hoverHover/focus state for elevated controls
Brand pink — used with intent, never as wallpaper
accentPrimary actions, active state, focus
accent-hoverHover on primary surfaces
accent-strongPressed state, dense fills
arcaneSecondary glow — gradients & auras only
Reserved meanings — don't repurpose
successConfirmations, positive deltas
warningCautions, pending states
dangerDestructive actions, errors
infoNeutral highlights, hints
Edges and dividers
line-subtleLow-contrast separators on dark fills
lineStandard panel and control borders
line-strongEmphasised edges, focused fields
Foreground hierarchy
inkPrimary text and headings
ink-mutedSecondary text, descriptions
ink-subtleCaptions, placeholders, labels
Brand mimicry — only on surfaces that echo Discord's UI
discordPrimary Discord action
discord-hoverPressed/hover on Discord actions
discord-greenOnline / positive in Discord chrome
discord-redDestructive in Discord chrome
discord-linkLink blue inside Discord surfaces
discord-surfaceDefault Discord panel
discord-surface-strongDeepest Discord chrome
discord-surface-deepNear-black Discord fill
discord-textPrimary Discord text
discord-text-mutedMuted Discord text
Space Grotesk gives titles a technical, slightly arcane voice. Hanken Grotesk keeps dense UI legible. Space Mono carries every number — dice, codes, counters.
Ag
Space Grotesk
Ag
Hanken Grotesk
Ag
Space Mono
Roll for initiative
Space Grotesk · hero headlines & brand voiceThe Arcane Console
Space Grotesk · page & section titlesEncounter overview
Hanken Grotesk · card and block headingsA complete platform for game masters and players.
Hanken Grotesk · paragraphs and UI copyCharacter sheet
Hanken Grotesk · labels & eyebrows2d20 + 17
Space Mono · dice, codes & live numbersShared primitives that consume the tokens directly — so the system stays consistent by construction, not by discipline alone.
Lifts toward the accent on hover. Use for anything clickable.
The default resting surface for content blocks.
A quieter panel for secondary content.
Inputs need a shared contract: label, hint, value constraints, validation feedback and character counters where content can overflow.
Keep titles short enough for cards and modal headers. 37 characters left.
Use textarea for long copy only; single-line fields should stay single-line. 113 characters left.
Use ToolTextarea for two-pane conversion tools; keep generic form textareas in app forms.
Show one actionable error below the field.
Names, titles and short strings
Input + maxLength + counterFiltering local lists and tables
Input with icon + clear actionBounded values, amounts and units
NumberInput + min/max/stepDescriptions and long copy
Textarea + maxLength + counterFinite choices
Select with searchable content when list is longBoolean settings
Switch with explicit label and hintThese already exist in the app and should be the default for color variation, visual tuning and bounded values.
Accent color
Use for custom visual settings. Brand/system colors should still come from tokens.
Accent gradient
Use for custom gradients. The closed control mirrors ColorPicker and keeps editing in the popover.
Use for bounded visual values; pair with the current value when precision matters.
The default answer for common UI jobs. Start here before creating a new class cluster or local component.
Actions and commands
Forms and settings
Status, labels, compact counts
Panels, repeated objects
Dialogs and focused workflows
Image, video, audio and uploads
Use disclosure for optional detail, advanced settings and grouped controls. Keep primary actions visible; don't hide the next required step inside an accordion.
Optional detail, dense advanced controls, FAQ-like help and expandable table rows.
Primary CTAs, required form fields, critical errors or anything the user must see before continuing.
A small, fixed set of corner radii and narrative shadows. The accent glow is reserved for focus and primary intent.
surface-0App background
surface-1Navigation, shell chrome
surface-2Default panels and cards
surface-3Nested panels, menus
surface-4Inputs and high controls
rounded-sm10pxrounded-md14pxrounded-lg18pxrounded-xl24pxrounded-2xl30pxrounded-3xl36pxrounded-4xl44pxrounded-full999pxshadow-panelResting panels and cards
shadow-raisedHovered / lifted surfaces
shadow-popoverMenus, popovers, modals
shadow-glowThe signature accent glow — focus & primary
Modal, media and empty/loading states should start from these primitives instead of rebuilding the chrome locally.
Compact dashboard modal chrome.
Use the shared shell tokens before adding one-off overlay, radius or shadow classes.
Clickable preview surface with empty, hover and loading states.
No results
Use EmptyState for empty lists, filtered tables and quiet dashboard panels.
Use these for admin and panel screens before composing local toolbar, filter, card and state chrome.
128
DashboardStatCard keeps metrics consistent.
Use for empty dashboard regions before custom empty cards.
Operational screens need dense, predictable layouts: a compact toolbar, visible active filters, and rows that scan before they decorate.
| Campaign | Owner | Status | Updated | Actions |
|---|---|---|---|---|
| Fenda Rubra | GM | Active | 2h ago | |
| Vault 13 | Player | Draft | Yesterday | |
| Ordem Delta | GM | Archived | May 18 |
Use semantic tones for meaning, not decoration. Keep copy short and pair destructive or blocking feedback with a clear action.
Settings were saved and synced.
A new publisher review is available.
Storage is close to the plan limit.
Use for transient confirmation. For blocking errors, use an inline alert inside the affected panel.
Destructive flows need a danger tone, explicit copy and a secondary escape action.
The source list for migration. If a screen needs one of these jobs, reach here before writing new local chrome.
Button, Badge, Card, Input, Textarea, NumberInput, Select, Switch, Tabs, Pressable
ColorPicker, GradientPicker, Slider, DatePicker, Pagination
Spinner, Skeleton, EmptyState, Toaster, AlertDialog, ConfirmDialog
FileUploadField, FilePreviewSurface, MediaPreviewPicker, AssetSelectorButton
DashboardSurface, DashboardToolbar, SearchField, FilterSelect, SegmentedTabs
ToolTextarea, ToolKeyInput, ToolPanel, ToolPanelHeader and shared toolTokens
UserSelect, UserMultiSelect; document here, render only inside admin flows
Small compositions for the surfaces we build most often. Copy the structure, then swap the content.
Header, metric and primary action.
Active
12
Shared
04
One row, one label, one control. No custom switch chrome.
Use Select for bounded choices.
Focus, high contrast and reduced motion are token-driven global states. Components should inherit them instead of repainting local exceptions.
Keyboard focus resolves to the accent ring globally.
Semantic text and line tokens lift together.
Use `useArcaneMotion()` for generic UI movement.
The shortest checklist for keeping the app visually consistent over time.
Import shared primitives and token classes before adding local chrome.
<Badge tone="success" size="sm">Online</Badge>Recreate color, radius, border and spacing by hand for a one-off pill.
rounded-full border px-2 text-xs ...Navigation
Sidebar, tabs, breadcrumbs and local page rails
Filters
Search, select, segmented/tabs and active filter chips
Data
Tables, dense lists, stats and empty/loading rows
Disclosure
Accordions, expandable rows and optional advanced settings
Feedback
Inline alerts, banners, toasts and destructive confirmations
Media
Preview-first upload and asset selection surfaces
Forms
Label, helper text, validation and footer actions
Overlays
Compact, fullbleed, split and sheet modal shells
Micro type
Use text-2xs .. text-5xs
Motion
Use useArcaneMotion() for generic UI
Chips
Use Badge tone + size
Modals
Start from modalTokens
Media
Start from components/media
Clickable rows
Use Pressable or native controls
Four rules that keep the system from drifting back into 200 one-off hexes.
The accent marks the single most important action or live state on a screen — focus, primary, active. If everything glows, nothing does.
Depth comes from the five ordered surfaces, not from inventing #0b0f1d next to #101225. Stack surface-1 → surface-4 to build elevation.
Every color, radius and shadow has a name. Reach for the token utility or import from theme.ts — raw values are how palettes drift.
This is a tool people work in for hours. Favour compact, scannable layouts with deliberate rhythm over marketing-site emptiness.