Skip to main content
🤖AI-generated documentation curatedAI Generated
This page was drafted by an AI assistant and may contain inaccuracies.
About content generation types
🤖
AI GeneratedPage drafted entirely by AI from codebase or prompt instructions.
(e.g., docs generated from codebase analysis)
← this page
✋→🤖
AI TransformattedHuman provided raw material; AI restructured it into a different format.
(e.g., livestream → blog post, meeting notes → docs)
Human GeneratedPage written entirely by a human author.
(e.g., hand-written tutorial)
More info about content generation types ↗

Frontend Styling System

The new UI uses a hand-rolled CSS utility-class framework — not Tailwind, not CSS modules, not CSS-in-JS. Everything is global CSS with a flat namespace. This is a deliberate choice for simplicity, discoverability, and zero build-step magic.

If you're coming from the old MUI-based UI: there is no ThemeProvider, no sx prop, no createTheme. All theming is CSS custom properties. All layout is utility classes.

Architecture

src/index.css
→ src/styles/App.css (master import file)
→ reset.css CSS reset
→ font.css Global font stack
→ icons.css Icon class definitions
→ color.css Design tokens (CSS custom properties)
→ animation.css Keyframe animations
→ log-terminal.css Log terminal styles
→ framerate.css Framerate viewer styles
→ camera.css Camera feed styles
→ checkbox.css Custom checkbox styles
→ components.css Component-level classes
→ calibration-module.css Calibration UI styles
→ playback.css Playback page styles
→ tooltips.css Tooltip system
→ toggle.css Toggle switch styles
→ button-sm.css Small button variants
→ (utility classes) Hundreds of hand-written utilities

No CSS modules. No scoping. Every class is globally available. This means:

  • You can inspect any element in devtools and see exactly which classes apply
  • You never wonder "where is this style defined?"
  • You never fight CSS module import issues
  • The cost: you need to be careful about naming collisions

Design Tokens (color.css)

All colors are tokenized as CSS custom properties. There are four layers of abstraction:

Raw Values

--gray-900: #060606;
--gray-800: #1b1b1b;
--gray-700: #272727;
--blue-500: #2ba4ff;
--green-100: #16dd12;
--red-500: #d7184b;
--warning-500: #e64900;

Alpha / Opacity Tokens

--gray-100-alpha-10: rgba(228, 228, 228, 0.1);
--blue-500-alpha-10: rgba(43, 164, 255, 0.1);
--red-500-alpha-10: rgba(215, 24, 75, 0.1);

Semantic Tokens (the ones you actually use)

--color-bg-primary: var(--gray-900);
--color-bg-secondary: var(--gray-800);
--color-bg-tertiary: var(--gray-700);
--color-text-primary: var(--gray-100);
--color-text-muted: var(--gray-400);
--color-border-primary: var(--gray-500);
--color-success: var(--green-100);
--color-danger: var(--red-500);
--color-warning: var(--warning-500);
--color-info: var(--blue-500);

Speed Tokens

--speed-1: 150ms; /* Fast transitions */
--speed-2: 300ms; /* Medium transitions */

Utility Classes Generated from Tokens

Every semantic token has corresponding utility classes. For example, --color-success generates:

.text-success { color: var(--color-success); }
.text-success-strong { color: var(--color-success-strong); }
.text-success-muted { color: var(--color-success-muted); }
.bg-success { background-color: var(--color-success); }
.bg-success-surface { background-color: var(--color-success-surface); }
.border-success { border-color: var(--color-success); }
.hover\:bg-success:hover { background-color: var(--color-success); }

The pattern is consistent: {property}-{semantic-name}[-{intensity}]. Intensities: (none), strong, medium, muted, idle, surface.

Utility Class System

All utility classes are defined in App.css. They follow a predictable naming convention: {property-abbrev}-{value}.

Layout

ClassEffect
flexdisplay: flex
flex-colflex-direction: column
flex-rowflex-direction: row
flex-wrapflex-wrap: wrap
flex-1flex: 1
items-centeralign-items: center
items-startalign-items: flex-start
justify-centerjustify-content: center
justify-betweenjustify-content: space-between
gap-0 through gap-4gap in increments

Spacing

Class patternEffect
p-0 through p-4padding all sides
pt-0 through pt-4padding-top
pb-0 through pb-4padding-bottom
pl-0 through pl-4padding-left
pr-0 through pr-4padding-right
m-0 through m-4margin
mt-45Special top margin (used for header spacing)

Positioning

ClassEffect
pos-relposition: relative
pos-absposition: absolute
pos-stickyposition: sticky
pos-fixedposition: fixed

Sizing

ClassEffect
w-fullwidth: 100%
h-fullheight: 100%
min-h-0min-height: 0 (critical for flex children)
min-w-0min-width: 0
h-25Fixed height 25px
max-w-350max-width: 350px

Visual

ClassEffect
bg-darkbackground-color: var(--color-bg-primary)
bg-darkgraybackground-color: var(--color-bg-secondary)
bg-graybackground-color: var(--color-bg-tertiary)
overflow-hiddenoverflow: hidden
overflow-yoverflow-y: auto
text-center / text-left / text-rightText alignment
text-nowrapwhite-space: nowrap
br-1 through br-5border-radius
border-1 through border-5border-width
z-1 through z-10z-index

Icon System

Icons are SVG files in src/assets/icons/. They're referenced as CSS custom properties and used via class names.

/* In icons.css */
--camera-icon: url('../assets/icons/camera.svg');
--expand-icon: url('../assets/icons/expand.svg');

.icon {
/* base icon styles: mask-image, sizing, etc. */
}
.load-icon {
mask-image: var(--load-icon);
-webkit-mask-image: var(--load-icon);
}

Usage in JSX:

<div className="icon camera-icon" />

To add a new icon:

  1. Add the SVG file to src/assets/icons/
  2. Declare a CSS custom property in icons.css
  3. Create an icon class that references it

Sub-Style Sheets

FileWhat it covers
reset.cssBox-sizing, margin/padding reset
font.cssfont-family, font-size on html and body
components.cssTags/badges, video tiles, segmented controls, headers, resizable panels, version badges, sidebar styles, modals, search inputs, pipeline cards
camera.cssCamera tile layout, overlay positioning
framerate.cssD3 chart container styles
log-terminal.cssLog row formatting, level colors, stack traces
playback.cssVideo player layout, transport bar, recording browser
calibration-module.cssCalibration config form, progress indicators
checkbox.cssCustom checkbox appearance
toggle.cssToggle switch appearance
button-sm.cssSmall button variant
tooltips.cssTooltip positioning and animation

Common Pitfalls

Don't create a new CSS file without importing it in App.css

App.css is the master import file. If you add a new .css file and forget to @import it there, the styles won't load.

Don't use inline styles — check if a utility class exists first

The utility class system covers most layout needs. Adding inline style={{}} props fragments the styling across JSX and CSS. Always check if a utility class exists before reaching for inline styles.

Don't duplicate a utility class with a slightly different name

If you need gap-5, check if the existing gap-0 through gap-4 pattern can be extended. Don't create gap-lg or gap-large — follow the numbering convention.

Utility classes are added manually, not generated

Unlike Tailwind's build-time class generation, these classes are written by hand. gap-5 doesn't exist until someone adds it. This means the class list is finite and auditable — but it also means you occasionally need to add a class.

CSS custom properties are the only theming mechanism

There is no JavaScript theme object, no ThemeProvider, no dark/light mode toggle that swaps CSS files. The dark theme is the default (and currently only) theme. If a light theme is added later, it will be done by swapping CSS custom property values.

min-h-0 is often necessary on flex children

Flexbox children have an implicit min-height: auto that can prevent them from shrinking below their content size. If you have a flex child that needs to scroll or be sized by its parent, add min-h-0 (or min-w-0 for horizontal).