---
name: frontend-ui-dark-ts
type: reference
description: "Builds dark-themed TypeScript UIs with accessible color systems, contrast compliance, and responsive design patterns. Use when implementing dark mode or building accessible TypeScript UI components."
paths: ["**/*.tsx", "**/*.ts", "**/*.css", "**/globals.css", "**/tailwind.config.*"]
effort: 3
allowed-tools: Read, Glob, Grep, Write, Edit, Bash
user-invocable: true
when_to_use: "When implementing dark mode, designing accessible color systems, or building TypeScript UI components"
---
# Frontend UI Dark (TypeScript)
## Critical rules (non-obvious)
- **WCAG contrast minimums**: text on bg requires 4.5:1 (AA) or 7:1 (AAA); UI elements (borders, icons) require 3:1
- **Never use `prefers-color-scheme` media query alone** — users need a toggle; sync with `localStorage` to avoid flash on hydration
- **HSL for dark themes**: use `hsl(220 15% 10%)` not `#1a1a2e` — HSL lets you programmatically adjust lightness
- **Avoid pure black (`#000`)** for dark backgrounds — causes eye strain; use `hsl(220 15% 8%)` instead
- **`color-scheme: dark`** on `:root` makes browser UI (scrollbars, inputs) follow dark theme
## CSS variable token system
```css
/* globals.css */
:root {
/* HSL values only (no hsl() wrapper) — allows opacity modifiers */
--bg-base: 222 47% 8%;
--bg-surface: 222 47% 12%;
--bg-elevated: 222 47% 16%;
--text-primary: 220 20% 95%;
--text-secondary: 220 15% 70%;
--text-muted: 220 10% 50%;
--brand: 220 90% 60%;
--brand-hover: 220 90% 65%;
--border: 220 20% 20%;
--error: 0 85% 60%;
--success: 142 70% 45%;
color-scheme: dark;
}
/* Light mode override */
[data-theme="light"] {
--bg-base: 0 0% 100%;
--bg-surface: 220 14% 96%;
--bg-elevated: 0 0% 100%;
--text-primary: 222 47% 11%;
--text-secondary: 220 14% 40%;
color-scheme: light;
}
```
## Theme provider (React + no flash)
```typescript
// providers/ThemeProvider.tsx
export function ThemeProvider({ children }: { children: React.ReactNode }) {
const [theme, setTheme] = useState<"dark" | "light">(() =>
typeof window !== "undefined"
? (localStorage.getItem("theme") as "dark" | "light") ?? "dark"
: "dark"
);
useEffect(() => {
document.documentElement.dataset.theme = theme;
localStorage.setItem("theme", theme);
}, [theme]);
return (