Story 4.1.1: Setup shadcn/ui & Dark Theme

Last updated: November 23, 2025 Reading time: 14 minutes File size: 21.27 KB

Story 4.1.1: Setup shadcn/ui & Dark Theme

Status

Done

Story

As a developer, I want to set up shadcn/ui components with a dark theme design system, so that we can build the modern public website with consistent styling and components.

Domain Context

Primary Domain

Shared/Infrastructure (Cross-cutting concerns - Frontend foundation)

Involved Services

  • web-frontend (React 19 + TypeScript)
  • No backend services involved in this setup story

Cross-Domain Dependencies

None - This is pure frontend infrastructure setup

Requirements Context

  • Epic 4: Public Website & Content Discovery
  • Story 4.1: Current Event Landing Page with Registration
  • Supports all UI requirements with modern component library

Acceptance Criteria Source

Setup requirements derived from implementation plan for Story 4.1

Architecture Context

Architecture Patterns

  • Frontend Architecture (05-frontend-architecture.md): React components, Tailwind CSS
  • Design System: shadcn/ui components (new for public website)
  • Existing: Material-UI for authenticated portal (unchanged)

Infrastructure Components

  • Node.js 20+ runtime
  • npm package manager
  • Vite build tool (already configured)
  • Tailwind CSS 4.1.14 (already installed)

Wireframe Context

Wireframe References

  • /docs/wireframes/batbern-newdesign.html - Reference for dark theme aesthetic

Design Specifications

Dark Theme Colors:

  • Background: zinc-950 (almost black)
  • Text: zinc-100 (off-white)
  • Accent: blue-400 (bright blue for CTAs)
  • Secondary: zinc-800 (dark gray for cards)

Typography:

  • Font weight: Ultra-light headings (font-light)
  • Generous whitespace for modern feel
  • Sans-serif font family

Component Style:

  • Clean, minimal design
  • Rounded corners (rounded-lg)
  • Subtle shadows and borders
  • Hover states with smooth transitions

Acceptance Criteria

  1. shadcn/ui dependencies installed and configured
  2. Tailwind dark theme configured with zinc/blue color palette
  3. Component directory structure created at src/components/public/ui/
  4. Core shadcn components installed (button, card, badge, dialog, form, tabs)
  5. TypeScript utilities configured for component composition (clsx, class-variance-authority)
  6. Existing Material-UI components for portal unaffected
  7. Build completes successfully with no errors

Tasks / Subtasks

  • Task 1: Install shadcn/ui dependencies

    • Install Radix UI primitives: @radix-ui/react-dialog, @radix-ui/react-dropdown-menu, @radix-ui/react-select,
    • Install composition utilities: class-variance-authority, clsx,
  • Task 2: Initialize shadcn/ui configuration

    • Run npx shadcn-ui@latest init
    • Configure components.json
      • style: β€œnew-york”
      • baseColor: β€œzinc”
      • cssVariables: true
      • Component alias: @/components/public/ui
    • Create src/lib/utils.ts
  • Task 3: Configure Tailwind dark theme

    • Update tailwind.config.js
  • Task 4: Install core shadcn components

    • Install button:
    • Install card:
    • Install badge:
    • Install dialog:
    • Install form:
    • Install tabs:
    • Verify all components created in
  • Task 5: Create public component directory structure

    • Create src/components/public/
    • Create src/components/public/ui/
    • Create src/styles/public.css
    • Update src/index.css
  • Task 6: Verify build and compatibility

    • Run npm run build
    • Run npm run type-check
    • Test dev server starts:
  • Task 7: Create simple test component

    • Create src/components/public/ShadcnTest.tsx
    • Add dark theme wrapper with

Dev Notes - Implementation Guide

Service Setup

Location: BATbern-develop/web-frontend/

Directory Structure:

web-frontend/
β”œβ”€β”€ src/
β”‚   β”œβ”€β”€ components/
β”‚   β”‚   β”œβ”€β”€ auth/          # Existing Material-UI
β”‚   β”‚   β”œβ”€β”€ organizer/     # Existing Material-UI
β”‚   β”‚   β”œβ”€β”€ shared/        # Existing Material-UI
β”‚   β”‚   └── public/        # NEW - shadcn components
β”‚   β”‚       └── ui/        # Auto-created by shadcn CLI
β”‚   β”œβ”€β”€ lib/
β”‚   β”‚   └── utils.ts       # NEW - cn() utility
β”‚   └── styles/
β”‚       └── public.css     # NEW - Custom public styles
β”œβ”€β”€ components.json        # NEW - shadcn config
└── package.json

Installation Commands

Step 1: Install dependencies

bash
cd web-frontend
npm install @radix-ui/react-dialog @radix-ui/react-dropdown-menu @radix-ui/react-select @radix-ui/react-tabs @radix-ui/react-slot
npm install class-variance-authority clsx tailwind-merge

Step 2: Initialize shadcn/ui

bash
npx shadcn-ui@latest init
# Select:
# - TypeScript: Yes
# - Style: New York
# - Base color: Zinc
# - CSS variables: Yes
# - Import alias: @/components/public/ui

Step 3: Install components

bash
npx shadcn-ui@latest add button card badge dialog form tabs

Configuration Files

components.json (created by init):

json
{
  "$schema": "https://ui.shadcn.com/schema.json",
  "style": "new-york",
  "rsc": false,
  "tsx": true,
  "tailwind": {
    "config": "tailwind.config.js",
    "css": "src/index.css",
    "baseColor": "zinc",
    "cssVariables": true
  },
  "aliases": {
    "components": "@/components/public/ui",
    "utils": "@/lib/utils"
  }
}

src/lib/utils.ts (utility for component composition):

typescript
import { type ClassValue, clsx } from "clsx"
import { twMerge } from "tailwind-merge"

export function cn(...inputs: ClassValue[]) {
  return twMerge(clsx(inputs))
}

tailwind.config.js (update for dark theme):

javascript
/** @type {import('tailwindcss').Config} */
export default {
  darkMode: ["class"],
  content: [
    "./index.html",
    "./src/**/*.{js,ts,jsx,tsx}",
  ],
  theme: {
    extend: {
      colors: {
        // Existing MUI colors remain
        // shadcn zinc palette (auto-configured by CLI)
        // Custom accents
        accent: {
          DEFAULT: "hsl(var(--accent))",
          foreground: "hsl(var(--accent-foreground))",
        },
      },
    },
  },
  plugins: [require("tailwindcss-animate")],
}

src/index.css (ensure Tailwind directives present):

css
@tailwind base;
@tailwind components;
@tailwind utilities;

/* Dark theme CSS variables will be auto-injected by shadcn */

Verification Test Component

Create temporary test component to verify setup:

src/components/public/ShadcnTest.tsx:

typescript
import { Button } from "@/components/public/ui/button"
import { Card, CardHeader, CardTitle, CardDescription, CardContent } from "@/components/public/ui/card"
import { Badge } from "@/components/public/ui/badge"

export const ShadcnTest = () => {
  return (
    <div className="dark min-h-screen bg-zinc-950 p-8">
      <Card className="max-w-md">
        <CardHeader>
          <CardTitle>shadcn/ui Setup Test</CardTitle>
          <CardDescription>Dark theme with zinc palette</CardDescription>
        </CardHeader>
        <CardContent className="space-y-4">
          <div className="flex gap-2">
            <Badge>Technology</Badge>
            <Badge variant="outline">Innovation</Badge>
          </div>
          <Button className="w-full">Test Button</Button>
          <Button variant="outline" className="w-full">Outline Button</Button>
        </CardContent>
      </Card>
    </div>
  )
}

Render in App.tsx temporarily to verify, then remove.

Code Patterns

Using shadcn components in public pages:

typescript
// Public components use shadcn/ui
import { Button } from "@/components/public/ui/button"

// Authenticated portal components still use Material-UI
import { Button as MuiButton } from "@mui/material"

Dark theme wrapper:

typescript
export const PublicLayout = ({ children }) => {
  return (
    <div className="dark min-h-screen bg-zinc-950 text-zinc-100">
      {children}
    </div>
  )
}

Package.json Changes

After installation, verify these new dependencies in package.json:

json
{
  "dependencies": {
    "@radix-ui/react-dialog": "^1.0.5",
    "@radix-ui/react-dropdown-menu": "^2.0.6",
    "@radix-ui/react-select": "^2.0.0",
    "@radix-ui/react-tabs": "^1.0.4",
    "@radix-ui/react-slot": "^1.0.2",
    "class-variance-authority": "^0.7.0",
    "clsx": "^2.1.0",
    "tailwind-merge": "^2.2.1",
    "tailwindcss-animate": "^1.0.7"
  }
}

Testing Requirements

Build Verification:

bash
# Type check
npm run type-check

# Build
npm run build

# Should complete with no errors

Compatibility Check:

  • Ensure existing Material-UI components still work
  • No conflicts between MUI and shadcn imports
  • Both design systems can coexist

Visual Verification:

  • Render ShadcnTest component
  • Verify dark background (zinc-950)
  • Verify components render with proper styling
  • Check responsive behavior

Definition of Done Checklist

Development Complete:

No Tests Required - This is infrastructure setup only

Change Log

Date Version Description Author
2025-11-02 1.0 Initial story creation Winston (Architect Agent)
2025-11-02 1.1 Implementation completed - shadcn/ui setup with dark theme James (Dev Agent)

Dev Agent Record

Agent Model Used

Claude Sonnet 4.5 (claude-sonnet-4-5-20250929)

Completion Notes

  • βœ… All shadcn/ui dependencies installed successfully
  • βœ… Configuration files created (components.json, utils.ts)
  • βœ… Tailwind dark mode configured with zinc palette and CSS variables
  • βœ… All core components installed (button, card, badge, dialog, form, tabs, label)
  • βœ… Fixed nested ui directory issue (moved files from ui/ui/ to ui/)
  • βœ… Fixed form.tsx import path for label component
  • βœ… Installed missing @radix-ui/react-icons dependency
  • βœ… TypeScript compilation successful
  • βœ… Production build successful (built in 10.01s)
  • βœ… Material-UI components remain unaffected
  • βœ… Test component created, verified, and removed as per requirements

Debug Log References

None - implementation completed without issues

File List

Created:

  • web-frontend/components.json
  • web-frontend/src/lib/utils.ts
  • web-frontend/src/styles/public.css
  • web-frontend/src/components/public/ui/button.tsx
  • web-frontend/src/components/public/ui/card.tsx
  • web-frontend/src/components/public/ui/badge.tsx
  • web-frontend/src/components/public/ui/dialog.tsx
  • web-frontend/src/components/public/ui/form.tsx
  • web-frontend/src/components/public/ui/tabs.tsx
  • web-frontend/src/components/public/ui/label.tsx

Modified:

  • web-frontend/package.json (added shadcn dependencies)
  • web-frontend/package-lock.json (dependency lockfile)
  • web-frontend/tailwind.config.js (dark mode, CSS variables, animations)
  • web-frontend/src/index.css (CSS variables for dark theme)

Deployment Notes

  • No deployment required - local development only
  • Changes will be deployed with Story 4.1.2

QA Results

Review Date: 2025-11-02

Reviewed By: Quinn (Test Architect)

Code Quality Assessment

Overall: EXCELLENT βœ…

This infrastructure setup story demonstrates high-quality implementation with careful attention to configuration details. All acceptance criteria have been fully met with proper dependency management, configuration, and verification steps. The implementation follows shadcn/ui best practices and successfully integrates the dark theme design system without disrupting existing Material-UI components.

Strengths:

  • Clean, well-organized configuration files (components.json, utils.ts)
  • Proper CSS variable setup for themeable components
  • Comprehensive dark theme implementation with HSL color values
  • All required Radix UI primitives and utilities installed
  • TypeScript compilation successful with no errors
  • Production build successful (10.01s)
  • Material-UI compatibility verified and maintained

Minor Observations:

  • The implementation correctly handles the dual design system approach (shadcn for public, MUI for authenticated)
  • Import aliases properly configured in vite.config.ts (@/ resolving to src/)
  • Dev identified and fixed nested directory issue (ui/ui/ β†’ ui/)
  • Dev proactively installed missing @radix-ui/react-icons dependency

Refactoring Performed

No refactoring performed - This is a pure infrastructure setup story with generated component code from shadcn CLI. The components are standard shadcn templates and should not be modified.

Compliance Check

  • Coding Standards: βœ“ N/A - Infrastructure setup, no custom code written
  • Project Structure: βœ“ PASS - Correctly created src/components/public/ui/ directory structure
  • Testing Strategy: βœ“ PASS - Appropriate for infrastructure setup (manual verification)
  • All ACs Met: βœ“ PASS - All 7 acceptance criteria fully satisfied

AC Validation:

  1. βœ… shadcn/ui dependencies installed and configured
  2. βœ… Tailwind dark theme configured with zinc/blue color palette (verified in tailwind.config.js and index.css)
  3. βœ… Component directory structure created at src/components/public/ui/
  4. βœ… Core shadcn components installed (button, card, badge, dialog, form, tabs + label)
  5. βœ… TypeScript utilities configured (clsx, class-variance-authority)
  6. βœ… Existing Material-UI components for portal unaffected
  7. βœ… Build completes successfully with no errors

Improvements Checklist

All items completed by Dev during implementation:

No additional improvements needed - Implementation is production-ready.

Security Review

Status: PASS βœ…

No security concerns for this infrastructure setup story. The shadcn/ui components and Radix UI primitives are well-maintained open-source libraries with active security maintenance.

Dependency Security:

  • All dependencies are from trusted sources (@radix-ui, shadcn/ui official registry)
  • No custom code that could introduce vulnerabilities
  • Dependencies follow semantic versioning with caret ranges (^)

Performance Considerations

Status: PASS βœ…

Build Performance:

  • Production build completed in 10.01s (excellent)
  • Gzip compression active and working
  • Brotli compression active and working
  • Code splitting maintained for shadcn components

Runtime Performance:

  • shadcn/ui components are lightweight and performant
  • CSS-in-JS avoided (uses Tailwind utility classes)
  • Dark theme uses CSS variables (no runtime overhead)
  • No performance regressions expected

Bundle Size Impact:

  • New dependencies add ~87 packages (reasonable for component library)
  • Components are tree-shakeable
  • Only imported components will be bundled in final build

Test Architecture Assessment

Status: APPROPRIATE βœ…

Testing Approach: This infrastructure setup story appropriately does not require automated tests. The story explicitly states β€œNo Tests Required - This is infrastructure setup only.”

Verification Strategy Used:

  1. βœ… TypeScript compilation (npm run type-check)
  2. βœ… Production build verification (npm run build)
  3. βœ… Linting verification (npm run lint - 4 warnings, acceptable)
  4. βœ… Manual component rendering test (ShadcnTest.tsx created and removed)
  5. βœ… Material-UI compatibility check (existing components unaffected)

Rationale: Infrastructure setup stories are validated through:

  • Build-time checks (TypeScript, linting)
  • Integration verification (build success)
  • Manual visual verification (test component)

This approach is appropriate and aligns with industry best practices for design system/component library integration.

Requirements Traceability

All Acceptance Criteria β†’ Verification Mapping:

AC1: shadcn/ui dependencies installed and configured

  • Given: New project needs shadcn/ui components
  • When: Dependencies installed via npm
  • Then: package.json contains all required @radix-ui packages and utilities
  • Evidence: package.json lines showing @radix-ui/react-dialog, dropdown-menu, select, tabs, slot, icons, label + class-variance-authority, clsx, tailwind-merge, tailwindcss-animate

AC2: Tailwind dark theme configured with zinc/blue color palette

  • Given: Dark theme design requirements
  • When: Tailwind config and CSS variables configured
  • Then: Dark mode uses β€˜class’ strategy with zinc palette and CSS variables
  • Evidence: tailwind.config.js (darkMode: [β€˜class’]), index.css (CSS variables for :root and .dark)

AC3: Component directory structure created at src/components/public/ui/

  • Given: Need for isolated public component directory
  • When: Directory structure created
  • Then: src/components/public/ui/ exists with shadcn components
  • Evidence: Directory listing shows 7 component files in correct location

AC4: Core shadcn components installed

  • Given: Need for button, card, badge, dialog, form, tabs components
  • When: Components installed via shadcn CLI
  • Then: All 7 components (including label) exist as .tsx files
  • Evidence: button.tsx, card.tsx, badge.tsx, dialog.tsx, form.tsx, tabs.tsx, label.tsx

AC5: TypeScript utilities configured

  • Given: Need for component composition utilities
  • When: class-variance-authority, clsx, tailwind-merge installed and utils.ts created
  • Then: cn() utility function available for component styling
  • Evidence: src/lib/utils.ts with cn() function implementation

AC6: Existing Material-UI components unaffected

  • Given: Dual design system requirement
  • When: shadcn added alongside existing MUI
  • Then: Build succeeds and MUI imports work correctly
  • Evidence: Build success + lint shows only 4 fast-refresh warnings (not MUI conflicts)

AC7: Build completes successfully

  • Given: All setup complete
  • When: Production build executed
  • Then: Build completes with no errors
  • Evidence: Build log shows successful completion in 10.01s with PWA and compression

Coverage Assessment: 7/7 acceptance criteria validated (100%)

Non-Functional Requirements Assessment

Maintainability: EXCELLENT βœ…

  • Configuration files are well-structured and documented
  • shadcn components follow consistent patterns
  • Import aliases simplify component usage
  • CSS variables enable easy theme customization

Compatibility: EXCELLENT βœ…

  • Successfully maintains dual design system (shadcn + MUI)
  • No conflicts between component libraries
  • Separate namespaces prevent collisions

Extensibility: EXCELLENT βœ…

  • Easy to add additional shadcn components via CLI
  • Theme customization through CSS variables
  • Component composition via cn() utility

Documentation: GOOD βœ…

  • Story file includes comprehensive implementation guide
  • Configuration examples provided
  • Dev notes document all setup steps
  • Minor: Could benefit from inline comments in tailwind.config.js explaining color variable structure

Files Modified During Review

None - No files modified during QA review. Implementation is complete and correct.

Gate Status

Gate: PASS β†’ docs/qa/gates/4.1.1-setup-shadcn-dark-theme.yml

Decision Rationale: All acceptance criteria met with high-quality implementation. No blocking issues, no security concerns, appropriate testing strategy for infrastructure setup. Build verification and compatibility checks confirm production readiness.

βœ“ Ready for Done

This story is complete and ready to move to Done status. The infrastructure setup provides a solid foundation for Story 4.1.2 (Public Layout & Navigation) to build upon.

Next Steps:

  1. Story owner to update status to β€œDone”
  2. Subsequent stories (4.1.2+) can proceed with building public website components using shadcn/ui