# Phase 12: Dark Mode Visibility Fixes - Research
**Researched:** 2026-04-01
**Domain:** CSS dark mode theming — Tailwind v4 CSS custom properties, native form control styling
**Confidence:** HIGH (all findings based on direct source code audit of the live codebase)
## Summary
Phase 12 addresses a specific set of dark-mode visibility regressions introduced during the v1.2 UI overhaul. The project uses a robust two-layer CSS token system (raw `--r2b-*` custom properties overridden per `.dark` class, wired to Tailwind utility classes via `@theme`). The architecture is sound. The problem is that several components were incompletely migrated: their structural/container elements received semantic tokens but their text elements, headings, and native form controls were left unstyled, rendering them invisible or unreadable in dark mode.
The issues fall into three categories: (1) bare `
` headings with no className in four wizard steps, (2) DeploymentStep using completely unstyled native form controls (fieldset, legend, labels, checkbox, radio), and (3) a `` element in FieldRenderer with no background or text color class, causing browsers to apply a white system background in dark mode.
The fix approach is purely additive: apply existing semantic token classes (`text-on-surface`, `bg-surface-container`, etc.) to the affected elements. Zero new CSS variables, zero new design decisions, zero new dependencies. The existing `.dark` token overrides in `index.css` already define the correct dark-mode values — the work is connecting those tokens to the elements that currently bypass them.
**Primary recommendation:** Audit each component's rendered elements and add semantic token classes to every text and interactive element that currently has none. Do not introduce hardcoded colors.
## Standard Stack
### Core (already in project — no changes needed)
| Library | Version | Purpose | Notes |
|---------|---------|---------|-------|
| Tailwind v4 | ^4.2.2 | Utility classes via `@theme` + CSS custom properties | Already wired; all needed tokens exist |
| `@tailwindcss/vite` | ^4.2.2 | Tailwind v4 Vite integration | No postcss config needed |
### CSS Token System (already complete in `src/index.css`)
All required tokens exist. Dark variants are already correct in `.dark` block:
| Token | Light value | Dark value | Use |
|-------|------------|-----------|-----|
| `--r2b-on-surface` | `#111827` (gray-900) | `#F9FAFB` (gray-50) | Primary text, headings |
| `--r2b-on-surface-container` | `#374151` (gray-700) | `#D1D5DB` (gray-300) | Labels, secondary text |
| `--r2b-surface-container` | `#FFFFFF` | `#1F2937` (gray-800) | Input/select backgrounds |
| `--r2b-outline` | `#D1D5DB` | `#4B5563` (gray-600) | Borders |
| `--r2b-warning` | `#D97706` | `#FCD34D` | Warning text |
| `--r2b-success` | `#15803D` | `#86EFAC` | Success text |
No installation needed. No new dependencies.
## Architecture Patterns
### Two-Layer Token Pattern (established, Phase 08-01)
```css
/* Layer 1: raw values, overridden per theme */
@layer base {
:root { --r2b-on-surface: #111827; }
.dark { --r2b-on-surface: #F9FAFB; }
}
/* Layer 2: wire to Tailwind utility classes */
@theme {
--color-on-surface: var(--r2b-on-surface);
}
```
This means `text-on-surface` already works correctly in both themes. The fix is always "add the right class", never "add a new CSS variable".
### Semantic Class Application Pattern
**What:** Apply semantic Tailwind utility classes to elements that currently have no className or only structural classes.
**Key classes for this phase:**
- `text-on-surface` — primary text, headings (h1, h2, h3)
- `text-on-surface-container` — labels, secondary text, legend elements
- `bg-surface-container` — input/select backgrounds
- `text-on-surface-container/60` — placeholder-like text, muted elements
- `border-outline` — form control borders
### Anti-Patterns to Avoid
- **Hardcoded color classes** (`text-gray-900`, `text-black`, `bg-white`): These ignore the `.dark` cascade — they were the root cause of v1.1 tech debt that THEME-01 fixed.
- **Adding `dark:` variants**: The project uses `.dark` class toggle (not `prefers-color-scheme`); the `@custom-variant dark (&:where(.dark, .dark *))` declaration in `index.css` means semantic tokens already handle dark automatically. Never add explicit `dark:` prefixes.
- **Styling `` elements**: Browser-native ` ` elements cannot be reliably styled cross-browser via CSS. Accept system rendering for options; only style the `` container itself.
## Don't Hand-Roll
| Problem | Don't Build | Use Instead | Why |
|---------|-------------|-------------|-----|
| Dark select styling | Custom `` component | Add `bg-surface-container text-on-surface` classes to existing `` | `` can't be styled cross-browser; native select with container styling is sufficient |
| Checkbox/radio dark mode | Custom checkboxes | Accept native rendering + add `accent-primary` Tailwind class | Already in project out-of-scope (REQUIREMENTS.md: "Custom checkbox/radio styling: High effort for few toggles") |
**Key insight:** All tokens already exist and resolve correctly in dark mode. The fix is application of existing classes, not new infrastructure.
## Specific Issues Found (Source Code Audit)
### Issue 1: Bare `` headings — CRITICAL (invisible in dark mode)
**Files affected:**
- `src/components/wizard/BackendSelectionStep.tsx` line 65: `Step 1: Select Backend `
- `src/components/wizard/RemoteConfigStep.tsx` line 65: `Step 2: Configure {backendLabel[backendType]} `
- `src/components/wizard/DeploymentStep.tsx` line 24: `Step 3: Deployment Options `
- `src/components/wizard/ReviewStep.tsx` line 75: `Step 4: Review & Download `
**Root cause:** No `className` at all — browser renders `` in its default black color, which is invisible on the dark `bg-surface` (#111827) background.
**Fix:** Add `className="text-2xl font-bold text-on-surface mb-2"` (matching the pattern used by IntroSection's `` in App.tsx line 19).
**Reference pattern (correct, from App.tsx line 19):**
```tsx
```
### Issue 2: DeploymentStep — completely unstyled (CRITICAL)
**File:** `src/components/wizard/DeploymentStep.tsx`
**Elements with no dark-mode styling:**
- Container `