# 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

Step Title

``` ### 3. Dark Mode Toggle Hook No library needed. A simple React hook: ```typescript // src/hooks/useTheme.ts function useTheme() { const [theme, setTheme] = useState<'light' | 'dark' | 'system'>(() => { return (localStorage.getItem('theme') as 'light' | 'dark') ?? 'system'; }); // Toggle .dark class on , persist to localStorage // Listen to prefers-color-scheme for 'system' mode } ``` ### 4. Accent Color System For user-selectable accent colors, the approach is: 1. Offer 3-5 preset seed colors (not arbitrary color picker) 2. Pre-generate token sets for each seed color at build time 3. Switch accent by swapping a CSS class on `` that loads a different set of `--md-sys-color-*` variables This avoids runtime color generation (which would require bundling `@material/material-color-utilities`). --- ## Alternatives Considered | Category | Recommended | Alternative | Why Not | |----------|-------------|-------------|---------| | Component library | None (keep semantic HTML + Tailwind) | Material Tailwind v3 | Material Tailwind is React + Tailwind but: (a) requires rewriting all existing components, (b) v3 is still in pre-order/beta, (c) adds ~50KB+ bundle weight for components the wizard doesn't need. Not worth the rewrite cost for 6-8 component types. | | Component library | None | MUI (Material UI) | MUI uses Emotion CSS-in-JS, fundamentally conflicts with Tailwind. Would require ripping out Tailwind entirely. Wrong direction. | | Component library | None | shadcn/ui | Good library but opinionated toward Radix primitives. Adding it now means learning a new component API while also implementing MD3 tokens. For v1.2 scope (cards, inputs, buttons, toggles), hand-authored Tailwind components are faster and simpler. | | Color generation | `@material/material-color-utilities` (dev-only) | `m3-tailwind-colors` npm package | Only 3 GitHub stars, single maintainer, uncertain maintenance. The underlying `@material/material-color-utilities` is the official Google package -- better to use it directly with a small script than depend on a wrapper. | | Color generation | Build-time script | Runtime `@material/material-color-utilities` in bundle | Adds ~30KB to the client bundle for something that only needs to run once per accent color change. Pre-generate at build time instead. | | Dark mode toggle | Custom 15-line hook | next-themes | next-themes is designed for Next.js SSR hydration edge cases. For a Vite SPA, it's unnecessary complexity. The core logic is `classList.toggle('dark')` + `localStorage`. | | Dark mode approach | Class-based (`@custom-variant`) | Media query (prefers-color-scheme only) | Media query approach doesn't allow manual toggle. Users expect a toggle button. Class-based supports both: system preference as default, manual override via toggle. | --- ## Installation ```bash # Dev dependency only -- NOT bundled into the app npm install -D @material/material-color-utilities # That's it. No other new dependencies. ``` ### Files to Create | File | Purpose | |------|---------| | `scripts/generate-theme.ts` | Node script: seed color -> MD3 CSS tokens | | `src/theme-tokens.css` | Generated output: CSS custom properties for light + dark | | `src/hooks/useTheme.ts` | Theme toggle hook (light/dark/system) | ### Files to Modify | File | Change | |------|--------| | `src/index.css` | Add `@import "./theme-tokens.css"`, `@custom-variant dark`, `@theme` block | | `package.json` | Add script: `"generate-theme": "tsx scripts/generate-theme.ts"` | --- ## What NOT to Add | Technology | Why Not | |------------|---------| | Material Tailwind / MUI / any component library | Rewrite cost exceeds benefit. Keep semantic HTML + Tailwind utilities with MD3 tokens. | | CSS-in-JS (Emotion, styled-components) | Conflicts with Tailwind. Wrong direction. | | next-themes | Next.js-specific. Vite SPA needs 15 lines of code, not a library. | | framer-motion | MD3 motion is subtle transitions (opacity, scale). CSS transitions + Tailwind's `@theme` animation tokens handle it. | | PostCSS plugins | Tailwind v4 uses the `@tailwindcss/vite` plugin, not PostCSS. Don't add PostCSS config. | | tailwind.config.js | Tailwind v4 is CSS-first. All config goes in `src/index.css` via `@theme`. No JS config file. | | Runtime color generation in browser | Pre-generate tokens at build time. Don't ship the color algorithm to users. | --- ## Confidence Assessment | Decision | Confidence | Basis | |----------|------------|-------| | Zero new runtime dependencies | HIGH | Existing Tailwind v4 handles everything; verified in official docs | | `@custom-variant dark` for dark mode | HIGH | Verified in official Tailwind v4 dark mode documentation | | `@theme` directive for MD3 tokens | HIGH | Verified in official Tailwind v4 theme documentation | | `@material/material-color-utilities` 0.4.0 for token generation | HIGH | Official Google package, actively maintained, used by Material Theme Builder | | MD3 shape scale values (4/8/12/16/28/9999 px) | HIGH | Confirmed in official Material Design 3 shape documentation | | MD3 elevation box-shadow values | MEDIUM | Values sourced from community reference (Studio N Creations) cross-referenced with Material Web component source. Official docs don't publish exact CSS box-shadow -- they use `--md-elevation-level` in their web components. The shadow values are a reasonable approximation. | | No component library needed | HIGH | Project has 159 tests against current DOM structure; rewriting components is unjustified for a styling overhaul | | Pre-generated accent colors (not runtime) | MEDIUM | Architectural choice -- runtime generation is valid but adds bundle weight for a rarely-used feature | --- ## Sources - [Tailwind v4 Dark Mode Documentation](https://tailwindcss.com/docs/dark-mode) -- `@custom-variant` syntax, class-based toggle, localStorage pattern - [Tailwind v4 Theme Documentation](https://tailwindcss.com/docs/theme) -- `@theme` directive, CSS variable generation, namespace conventions - [Material Design 3 Design Tokens](https://m3.material.io/foundations/design-tokens) -- token naming, semantic color roles - [Material Design 3 Shape Scale](https://m3.material.io/styles/shape/corner-radius-scale) -- corner radius values (4/8/12/16/28dp) - [Material Design 3 Elevation](https://m3.material.io/styles/elevation/applying-elevation) -- elevation levels 0-5 - [@material/material-color-utilities on npm](https://www.npmjs.com/package/@material/material-color-utilities) -- v0.4.0, official Google color algorithm - [Material Theme Builder](https://material-foundation.github.io/material-theme-builder/) -- CSS export format, `--md-sys-color-*` naming convention - [MD3 Box-Shadow CSS Values](https://studioncreations.com/blog/material-design-3-box-shadow-css-values/) -- elevation shadow approximations (MEDIUM confidence) - [m3-tailwind-colors GitHub](https://github.com/somteacodes/m3-tailwind-colors) -- evaluated and rejected (3 stars, single maintainer) - [Tailwind v4 Multi-Theme Strategy](https://simonswiss.com/posts/tailwind-v4-multi-theme) -- community pattern for theme switching with CSS variables