---
name: acarshub-design-language
description: Use ONLY when working in the docker-acarshub repository AND building UI -- React components, SCSS modules, theme variables, accessibility, mobile responsiveness, Catppuccin color usage, Zustand store wiring. Points at the in-repo agent-docs/ as the canonical reference and codifies the rules that the model otherwise forgets between sessions (no inline styles, mobile-first, 44x44 touch targets, WCAG 2.1 AA, namespace logger, Socket.IO `/main` binding quirk).
---
# ACARS Hub: design language & UI rules
The full design language lives in `agent-docs/DESIGN_LANGUAGE.md` and
`agent-docs/CATPPUCCIN.md` in the docker-acarshub repo. Read those
first when doing real UI work. This skill captures the rules that
otherwise leak between sessions.
## Styling
- **No inline styles.** Every style lives in `.scss` modules.
- **No CSS frameworks.** No Tailwind, Bootstrap, Material-UI, etc.
ACARS Hub does Catppuccin tokens directly via CSS variables.
- **`@use` / `@forward`**, not deprecated `@import`.
- **Catppuccin colors via CSS variables**: `var(--color-text)`,
`var(--color-primary)`, etc. Theme switching is a value swap on
the variables via SCSS mixins (Mocha = dark, Latte = light). See
`agent-docs/CATPPUCCIN.md` for the full palette.
## Mobile-first responsive (non-negotiable)
- Base styles for **320px+**.
- `@media (min-width: 768px)` for tablet.
- `@media (min-width: 1024px)` for desktop.
- **Touch targets minimum 44x44px**.
- **No horizontal scrolling at any screen size.**
- Test at: 320px, 375px, 768px, 1024px, 1920px.
Example:
```scss
.button {
// Base: mobile
padding: 0.75rem 1rem;
font-size: 1rem;
// Tablet and up
@media (min-width: 768px) {
padding: 0.5rem 1rem;
font-size: 0.875rem;
}
}
```
## Accessibility (WCAG 2.1 AA)
- Color contrast: 4.5:1 for normal text, 3:1 for large text.
- Touch targets: minimum 44x44px (same as mobile rule above).
- Keyboard navigation: every interactive element reachable.
- ARIA labels, roles, landmarks on non-obvious widgets.
- Visible focus indicators; focus traps in modals.
Verify with `just test-a11y` (axe-core automated tests).
## Component patterns
See `agent-docs/DESIGN_LANGUAGE.md` for the canonical examples. The
quick-reference shape:
```typescript
import type { ReactNode } from "react";
import "./Button.scss";
interface ButtonProps {
variant?: "primary" | "secondary" | "danger";
size?: "small" | "medium" | "large";
disabled?: boolean;
onClick?: () => void;
children: ReactNode;
}
export function Button({
variant = "primary",
size = "medium",
disabled = false,
onClick,
children,
}: ButtonProps) {
return (
);
}
```
BEM-style class naming (`button button--primary`) is the convention.
## State (Zustand)
- `useAppStore` -- global app state (messages, alerts, connection).
- `useSettingsStore` -- user preferences (theme, locale,
notifications). Persisted via `zustand/middleware`'s `persist`.
Store pattern (canonical):
```typescript
import { create } from "zustand";
import { persist } from "zustand/middleware";
interface SettingsState {
theme: "mocha" | "latte";
setTheme: (theme: "mocha" | "latte") => void;
}
export const useSettingsStore = create()(
persist(
(set) => ({
theme: "mocha",
setTheme: (theme) => set({ theme }),
}),
{ name: "acars-settings" },
),
);
```
## Performance gates
- Target: <500KB per chunk (gzipped). Monitor with `npm run analyze`.
- 60fps with 100+ aircraft on the map.
- No memory leaks in long-running sessions.
- `React.memo`, `useMemo`, `useCallback` where they earn it -- not
prophylactically everywhere.
- `just lighthouse` runs Lighthouse CI.
## When to stop and ask
- A UI requirement looks like it needs a CSS framework. It doesn't.
Surface the requirement; the project's stance is "no frameworks"
and the right answer is custom SCSS following the existing
patterns.
- A new design pattern is needed that isn't in `DESIGN_LANGUAGE.md`.
Propose adding the pattern to that doc as part of the PR; do NOT
inline a one-off pattern without updating the standard.
- An accessibility violation is intrinsic to the design (e.g. a
custom widget that can't meet WCAG 2.1 AA with a reasonable
amount of work). Stop and surface -- a11y is the floor, not a
preference.