Design System
Consistent CSS classes for buttons and badges across the web UI.
Buttons
All buttons use the .btn base class plus a variant and optional size modifier.
<button className="btn btn-primary">Save</button>
<button className="btn btn-secondary btn-sm">Cancel</button>
<button className="btn btn-danger btn-sm">Delete</button>
<button className="btn btn-primary btn-lg">Submit Form</button>
Base (.btn)
Shared properties applied to all buttons:
| Property | Value |
|---|---|
| Padding | 0.4rem 0.75rem |
| Border radius | var(--radius) (8px) |
| Font size | 0.8rem |
| Font family | var(--font) |
| Font weight | 500 |
| Display | inline-flex (centered) |
| Transition | opacity 0.15s, background 0.15s, border-color 0.15s |
Variants
| Class | Use for | Background | Color | Border |
|---|---|---|---|---|
.btn-primary |
Main actions (save, submit, confirm) | var(--accent) green |
#000 |
none |
.btn-secondary |
Secondary actions (cancel, back, filter) | var(--bg-card) |
var(--text-muted) |
var(--border) |
.btn-danger |
Destructive actions (delete, clear) | #7f1d1d |
#fca5a5 |
#ef4444 |
.btn-ghost |
Tertiary/minimal (menu items, dismiss) | transparent |
var(--text-muted) |
var(--border) |
Sizes
| Class | Padding | Font size | Width |
|---|---|---|---|
.btn-sm |
0.25rem 0.5rem |
0.7rem |
auto |
| (default) | 0.4rem 0.75rem |
0.8rem |
auto |
.btn-lg |
0.625rem 1rem |
0.85rem |
100% |
States
All handled by CSS — no JS needed:
| State | Effect |
|---|---|
:hover |
Variant-specific (opacity, background change) |
:disabled |
opacity: 0.45, cursor: not-allowed |
:active |
transform: scale(0.98) |
Usage Guidelines
- Forms: Use
btn btn-primary btn-lgfor submit buttons (full width) - Tables: Use
btn btn-secondary btn-smfor action buttons,btn btn-danger btn-smfor delete - Modals: Cancel =
btn btn-secondary btn-sm, Confirm =btn btn-primary, Destructive =btn btn-danger btn-sm - Navigation: Use
btn btn-secondaryfor prev/next,btn btn-ghostfor menu items - Tabs: Active =
btn btn-primary, Inactive =btn btn-secondary - For dynamic color buttons (e.g., vote buttons), use
btn btn-lgand setbackground,color,bordervia style prop
Badges
All badges use the .badge base class plus a variant.
<span className="badge badge-online">active</span>
<span className="badge badge-muted">viewer</span>
<span className="badge badge-accent">Platform Admin</span>
<span className="badge badge-rare">RARE x3</span>
Base (.badge)
| Property | Value |
|---|---|
| Padding | 0.1rem 0.4rem |
| Border radius | 9999px (pill) |
| Font size | 0.65rem |
| Font weight | 500 |
| Display | inline-block |
Variants
| Class | Use for | Background | Color | Border |
|---|---|---|---|---|
.badge-online |
Active, online, confirmed | var(--accent-dim) |
var(--accent) green |
— |
.badge-offline |
Blocked, offline, error | #7f1d1d |
var(--danger) red |
— |
.badge-degraded |
Warning, degraded | #78350f |
var(--warning) yellow |
— |
.badge-muted |
Role labels, neutral info | var(--bg) |
var(--text-muted) |
var(--border) |
.badge-accent |
Platform admin, special | var(--accent-dim) |
var(--accent) |
var(--accent) |
.badge-rare |
Rare species | #1e1b4b |
#8b5cf6 purple |
— |
.badge-danger |
Rejected, errors | #7f1d1d |
#ef4444 |
— |
Usage Guidelines
- Status:
badge-online(active),badge-offline(blocked),badge-degraded(warning) - Roles:
badge-mutedfor tenant roles (viewer, member, admin),badge-accentfor platform admin - Species:
badge-rarefor rare detections,badge-onlinefor confirmed,badge-dangerfor rejected - Webhooks/workers:
badge-onlinefor active,badge-mutedfor disabled
Page Layout
Every page follows a consistent fixed-header / scrollable-content pattern:
┌──────────────────────────────────────────────┐
│ .page-header — Title + subtitle + actions │ fixed
├──────────────────────────────────────────────┤
│ .page-toolbar — Filters, sort, toggles, ⋮ │ fixed
├──────────────────────────────────────────────┤
│ .page-scroll — Content (cards, tables...) │ scrolls
├──────────────────────────────────────────────┤
│ .page-footer — Pagination controls │ fixed
└──────────────────────────────────────────────┘
Classes
| Class | Purpose | Behavior |
|---|---|---|
.page-header |
Page title, subtitle, primary actions | Fixed at top, flex-shrink: 0 |
.page-toolbar |
Filters, sort, toggles, overflow menu | Fixed below header. On mobile (< 768px), scrolls with content |
.page-scroll |
Main content area | flex: 1, overflow-y: auto |
.page-footer |
Pagination (prev/next) | Fixed at bottom, border-top separator |
Principles
- page-header is always present. Title + optional subtitle + optional action buttons (e.g. "Register satellite")
- page-toolbar is optional — only rendered if the page has filters or controls. Uses flexbox wrap for responsive layout. Styled inputs/selects are automatic via child selectors.
- page-scroll wraps all scrollable content. Only this area scrolls.
- page-footer is optional — only for pages with pagination (Detections, Timeline, Audit Log)
- Pages use React fragments (
<>...</>) as their root element so the layout classes sit directly inside.main-content's flex column
Overflow Menu
For pages with many secondary actions (e.g. export formats), use the OverflowMenu component:
import { OverflowMenu } from "../components/OverflowMenu.js";
<OverflowMenu items={[
{ label: "Export CSV", href: "/api/export?format=csv", download: true },
{ label: "Export JSON", href: "/api/export?format=json", download: true },
]} />
Uses CSS classes .overflow-menu, .overflow-menu-trigger (⋮ button), .overflow-menu-dropdown (positioned dropdown).
Mobile Behavior
On screens < 768px:
.page-toolbarloses its fixed position and scrolls with content (viaflex-shrink: unset).page-footerstays fixed — pagination is always accessible- Toolbar inputs expand to full width
Scrollbars
Global scrollbar styling — thin (6px), transparent track, faint thumb:
- Chromium:
::-webkit-scrollbarrules - Firefox:
scrollbar-width: thin; scrollbar-color: rgba(255,255,255,0.1) transparent
Color Palette
| Variable | Value | Use |
|---|---|---|
--bg |
#0f1117 |
Page background |
--bg-card |
#1a1d27 |
Card/panel background |
--bg-hover |
#252833 |
Hover states |
--border |
#2a2d3a |
Borders, dividers |
--text |
#e4e4e7 |
Primary text |
--text-muted |
#9ca3af |
Secondary text, labels |
--accent |
#22c55e |
Green primary accent |
--accent-dim |
#166534 |
Green dim background |
--warning |
#f59e0b |
Yellow warning |
--danger |
#ef4444 |
Red danger |
--info |
#3b82f6 |
Blue info |
--radius |
8px |
Border radius |
--font |
-apple-system, Inter, system-ui, sans-serif |
Primary font |
--mono |
JetBrains Mono, Fira Code, monospace |
Code font |