# Technology Stack **Project:** Ready2Blob v1.2 — UI Polish & MD3 Overhaul **Researched:** 2026-03-31 **Scope:** Stack ADDITIONS for Material Design 3, dark mode, accent colors, responsive improvements **Existing stack (validated, not re-researched):** Vite 6, React 18, TypeScript 5, Tailwind v4 (@tailwindcss/vite), react-hook-form 7, Zod 4, Vitest 4, JSZip --- ## Recommendation: Zero New Runtime Dependencies The v1.2 UI overhaul should be achieved with **Tailwind v4's native theming system + hand-authored MD3 design tokens in CSS**. No component library. No runtime theming library. The existing approach (semantic HTML + Tailwind utilities) is the right foundation -- it just needs a proper token system layered on top. **Rationale:** The app currently has zero component library dependencies and 159 passing tests. Introducing a component library (Material Tailwind, MUI, shadcn/ui) at this stage would: 1. Require rewriting every existing component to match the library's API 2. Break existing tests that assert on current DOM structure 3. Add bundle weight for a wizard that needs at most 6-8 component types 4. Create upgrade debt for a library the team doesn't control Instead: define MD3 tokens as CSS custom properties, wire them into Tailwind v4's `@theme` directive, and build the small set of reusable patterns (card, input, button, elevation) as project-owned Tailwind utility compositions. --- ## New Stack Additions ### Design Token Generation (Dev Dependency Only) | Technology | Version | Purpose | Why | |------------|---------|---------|-----| | `@material/material-color-utilities` | 0.4.0 | Generate MD3 color palettes from seed color | Official Google library. Used at build/dev time via a small script to generate light + dark token sets from a single seed color. NOT bundled into the app -- it produces static CSS custom properties. This is the same algorithm the Material Theme Builder uses. HIGH confidence (official Google package, actively maintained). | **How it works:** Write a one-time Node script (`scripts/generate-theme.ts`) that: 1. Takes a seed color (hex) 2. Uses `themeFromSourceColor()` to generate full MD3 palette (primary, secondary, tertiary, error, surface, outline, etc.) 3. Outputs CSS custom properties in the `--md-sys-color-*` naming convention 4. Writes to `src/theme-tokens.css` which is imported into `src/index.css` This means the generated tokens are **static CSS** -- zero runtime cost, zero bundle impact from the color library. ### Tailwind v4 Theme Integration (No New Dependency) | Technology | Version | Purpose | Why | |------------|---------|---------|-----| | Tailwind v4 `@theme` directive | Already installed | Map MD3 tokens to Tailwind utility classes | Tailwind v4's `@theme` directive creates utility classes from CSS custom properties. Defining `--color-primary`, `--color-surface`, etc. in `@theme` blocks automatically generates `bg-primary`, `text-on-primary`, `bg-surface` utilities. No config file needed -- pure CSS. HIGH confidence (verified in official Tailwind v4 docs). | | Tailwind v4 `@custom-variant` | Already installed | Class-based dark mode toggle | Tailwind v4 replaces the old `darkMode: 'class'` config with `@custom-variant dark (&:where(.dark, .dark *));` in CSS. This enables the `dark:` prefix to respond to a `.dark` class on the HTML element. HIGH confidence (verified in official Tailwind v4 dark mode docs). | ### No Other New Dependencies | Category | Decision | Rationale | |----------|----------|-----------| | Component library | **Do NOT add** | Current semantic HTML + Tailwind is correct. MD3 styling is achieved through tokens + utility classes, not through library components. | | CSS-in-JS | **Do NOT add** | Tailwind v4 handles everything via CSS. Adding styled-components or Emotion would conflict with the existing Tailwind approach. | | Theme toggle library (next-themes) | **Do NOT add** | next-themes is Next.js-focused. For a pure Vite SPA, a 15-line React hook (`useTheme`) with `localStorage` + `classList.toggle` is all that's needed. | | Animation library | **Do NOT add** | MD3 motion tokens (duration, easing) are CSS custom properties. Tailwind v4's `@theme` can define transition tokens. No framer-motion or similar needed for the subtle transitions in a wizard UI. | | Icon library | **Evaluate later** | If MD3 icons are desired, `@material-design-icons/svg` provides tree-shakeable SVGs. But this is a nice-to-have, not a v1.2 blocker. | --- ## Detailed Integration Plan ### 1. MD3 Color Token System The Material Design 3 color system uses ~29 semantic color roles (not raw palette values). These map to CSS custom properties: ```css /* Light theme tokens (generated from seed color) */ :root { --md-sys-color-primary: #006A6A; --md-sys-color-on-primary: #FFFFFF; --md-sys-color-primary-container: #6FF7F6; --md-sys-color-on-primary-container: #002020; --md-sys-color-secondary: #4A6363; --md-sys-color-on-secondary: #FFFFFF; --md-sys-color-surface: #FAFDFC; --md-sys-color-on-surface: #191C1C; --md-sys-color-surface-container: #EFF2F1; --md-sys-color-surface-container-low: #F4F7F6; --md-sys-color-surface-container-high: #E9ECEB; --md-sys-color-outline: #6F7979; --md-sys-color-outline-variant: #BEC9C8; --md-sys-color-error: #BA1A1A; --md-sys-color-on-error: #FFFFFF; /* ... ~29 roles total */ } /* Dark theme tokens (same seed, dark scheme) */ .dark { --md-sys-color-primary: #4EDADA; --md-sys-color-on-primary: #003737; --md-sys-color-surface: #101414; --md-sys-color-on-surface: #E0E3E2; /* ... all roles overridden */ } ``` ### 2. Tailwind v4 Theme Wiring ```css /* src/index.css */ @import "tailwindcss"; @import "./theme-tokens.css"; /* Generated MD3 tokens */ @custom-variant dark (&:where(.dark, .dark *)); @theme { /* Map MD3 tokens to Tailwind color utilities */ --color-primary: var(--md-sys-color-primary); --color-on-primary: var(--md-sys-color-on-primary); --color-primary-container: var(--md-sys-color-primary-container); --color-on-primary-container: var(--md-sys-color-on-primary-container); --color-secondary: var(--md-sys-color-secondary); --color-on-secondary: var(--md-sys-color-on-secondary); --color-surface: var(--md-sys-color-surface); --color-on-surface: var(--md-sys-color-on-surface); --color-surface-container: var(--md-sys-color-surface-container); --color-surface-container-low: var(--md-sys-color-surface-container-low); --color-surface-container-high: var(--md-sys-color-surface-container-high); --color-outline: var(--md-sys-color-outline); --color-outline-variant: var(--md-sys-color-outline-variant); --color-error: var(--md-sys-color-error); --color-on-error: var(--md-sys-color-on-error); /* MD3 Shape scale */ --radius-xs: 4px; --radius-sm: 8px; --radius-md: 12px; --radius-lg: 16px; --radius-xl: 28px; --radius-full: 9999px; /* MD3 Elevation (box-shadows) */ --shadow-elevation-0: none; --shadow-elevation-1: 0 1px 2px 0 rgb(0 0 0 / 0.3), 0 1px 3px 1px rgb(0 0 0 / 0.15); --shadow-elevation-2: 0 1px 2px 0 rgb(0 0 0 / 0.3), 0 2px 6px 2px rgb(0 0 0 / 0.15); --shadow-elevation-3: 0 1px 3px 0 rgb(0 0 0 / 0.3), 0 4px 8px 3px rgb(0 0 0 / 0.15); --shadow-elevation-4: 0 2px 3px 0 rgb(0 0 0 / 0.3), 0 6px 10px 4px rgb(0 0 0 / 0.15); --shadow-elevation-5: 0 4px 4px 0 rgb(0 0 0 / 0.3), 0 8px 12px 6px rgb(0 0 0 / 0.15); /* MD3 Motion tokens */ --animate-md3-enter: md3-enter 0.2s cubic-bezier(0, 0, 0, 1); --animate-md3-exit: md3-exit 0.15s cubic-bezier(0.3, 0, 1, 1); @keyframes md3-enter { from { opacity: 0; transform: scale(0.92); } to { opacity: 1; transform: scale(1); } } @keyframes md3-exit { from { opacity: 1; transform: scale(1); } to { opacity: 0; transform: scale(0.92); } } } ``` **Usage in components** then becomes natural Tailwind: ```tsx