Skip to content

StandardSoft/evolve-ui

Repository files navigation

Evolve-UI

Tagline: From API to UI — where pixels meet products.

A modern Next.js + TypeScript starter template designed for learning and building web applications with best practices built in.

Audience & Purpose

This template is designed for:

  • 11-12 year olds who have completed CS50 or similar introductory programming courses
  • Junior developers learning modern web development
  • QA engineers wanting to understand frontend architecture
  • Designers transitioning to development

Purpose: Provide a clean, well-tested foundation with strong guardrails that teaches good coding habits while staying simple enough to build upon.

Quick Start

# Install dependencies (requires Node.js 18+)
pnpm install

# Start development server
pnpm dev

# Open http://localhost:3000 in your browser

Project Structure

evolve-ui/
├── src/
│   ├── app/                    # Next.js App Router pages
│   │   ├── layout.tsx         # Root layout with global styles
│   │   ├── page.tsx           # Homepage
│   │   ├── globals.css        # Tailwind CSS imports
│   │   └── api/               # API routes
│   │       ├── products/      # Products endpoint
│   │       └── health/        # Health check endpoint
│   ├── components/            # Reusable React components
│   │   ├── Button.tsx         # Example button component
│   │   └── HealthCheck.tsx    # Client-side fetch demo
│   └── lib/                   # Utility functions
│       └── formatMoney.ts     # Currency formatting helper
├── __tests__/                 # Jest unit tests
│   ├── lib/
│   │   └── formatMoney.test.ts
│   └── components/
│       └── Button.test.tsx
├── e2e/                       # Playwright end-to-end tests
│   └── home.spec.ts
├── eslint.config.mjs          # ESLint configuration
├── jest.config.ts             # Jest configuration
├── playwright.config.ts       # Playwright configuration
├── tsconfig.json              # TypeScript configuration
└── package.json               # Dependencies and scripts

Key Configs Explained

TypeScript (tsconfig.json)

  • Strict mode enabled: Catches more bugs during development
  • Path aliases: Use @/ instead of ../../ for imports
  • ES2022 target: Modern JavaScript features

ESLint (eslint.config.mjs)

Enforces code quality with:

  • No unused imports: Auto-removes with pnpm lint:fix
  • TypeScript best practices: Prevents common mistakes
  • Explicit return types: Warns when functions don't have return types
  • No any types: Warns when using any instead of proper types
  • Prefer const: Encourages immutable variables

Jest (jest.config.ts)

  • Uses Next.js built-in configuration
  • Supports TypeScript and path aliases
  • Includes @testing-library/jest-dom matchers

Playwright (playwright.config.ts)

  • Runs tests in Chromium
  • Auto-starts dev server for testing
  • Generates HTML reports

shadcn/ui Components (components.json)

This project uses shadcn/ui - a collection of reusable components built with Radix UI and Tailwind CSS.

What is shadcn/ui?

  • Not a traditional component library (doesn't install via npm)
  • Components are copied directly into your project
  • Full control over the code
  • Built with Radix UI for accessibility
  • Styled with Tailwind CSS
  • Supports dark mode out of the box

Included Components:

  • Button - Multiple variants (default, outline, ghost, destructive)
  • Card - Container with header, content, footer sections
  • Badge - Labels and status indicators
  • Switch - Toggle switch for settings

Adding More Components:

npx shadcn@latest add [component-name]

Examples:

npx shadcn@latest add input        # Add Input component
npx shadcn@latest add dialog       # Add Dialog component
npx shadcn@latest add dropdown-menu  # Add Dropdown Menu

Using Components:

import { Button } from "@/components/ui/button";
import { Card, CardHeader, CardTitle, CardContent } from "@/components/ui/card";

<Card>
  <CardHeader>
    <CardTitle>Hello</CardTitle>
  </CardHeader>
  <CardContent>
    <Button>Click me</Button>
  </CardContent>
</Card>

Customization: All components use CSS variables defined in globals.css. You can customize the entire theme by changing these variables:

:root {
  --primary: 221.2 83.2% 53.3%;      /* Blue for light mode */
  --background: 0 0% 100%;            /* White background */
  /* ... more variables */
}

.dark {
  --primary: 217.2 91.2% 59.8%;      /* Lighter blue for dark mode */
  --background: 222.2 84% 4.9%;       /* Dark background */
}

Tailwind CSS v4 & Modern Colors

This template uses Tailwind CSS v4, which represents a major shift in how Tailwind works.

What Changed in Tailwind v4?

Philosophy Shift:

  • From "utility-first" to "utility-native": Tailwind v4 is now built from the ground up for modern CSS
  • Faster builds: New engine built in Rust makes everything much faster
  • Simpler config: Less configuration needed, more works out of the box
  • Native CSS features: Better use of modern CSS capabilities like CSS variables and @layer

Key Changes:

  • Zero-config by default: Works without tailwind.config.js for basic usage
  • Better performance: Up to 10x faster builds in some cases
  • CSS-first approach: Generates cleaner, more maintainable CSS
  • Modern color spaces: Built-in support for OKLCH colors

What This Means for You:

  • Faster development: Changes show up quicker in your browser
  • Less setup: Fewer configuration files to worry about
  • Better colors: More vibrant, consistent colors across devices
  • Future-proof: Uses the latest CSS standards

Understanding OKLCH Colors

What is OKLCH?

OKLCH is a modern color space that describes colors using three values:

  • L (Lightness): How bright the color is (0% = black, 100% = white)
  • C (Chroma): How colorful it is (0 = gray, higher = more vibrant)
  • H (Hue): The color itself (0-360 degrees around the color wheel)

Example:

/* OKLCH format */
--primary: 62.8% 0.214 259.6;
/*         ^^^^   ^^^^^  ^^^^^
           L      C      H
           62.8%  0.214  259.6°  = Beautiful blue */

Why OKLCH instead of HSL or RGB?

The older formats (HSL, RGB, Hex) have problems:

  1. HSL Problem: Colors with the same "lightness" value look different to our eyes

    /* Both at 50% lightness, but yellow looks much brighter than blue */
    --yellow-hsl: 60 100% 50%;   /* Looks very bright */
    --blue-hsl: 240 100% 50%;    /* Looks much darker */
  2. OKLCH Solution: Perceptually uniform - 50% lightness looks the same across all colors

    /* Both at 62.8% lightness, and they actually look similar brightness */
    --yellow-oklch: 62.8% 0.214 100;    /* Consistent brightness */
    --blue-oklch: 62.8% 0.214 259.6;    /* Consistent brightness */

Benefits of OKLCH:

  1. Perceptually Uniform: Colors with the same lightness value actually look the same brightness to human eyes
  2. Wider Color Gamut: Access to more vibrant colors that HSL/RGB can't represent
  3. Better Gradients: Smooth transitions between colors without muddy middle tones
  4. Accessibility: Easier to create accessible color contrasts
  5. Future-Proof: Modern browsers natively support it

Real-World Example:

In this project, our primary blue is defined as:

:root {
  --primary: 62.8% 0.214 259.6;
  /*         Bright  Vibrant  Blue */
}

.dark {
  --primary: 70.9% 0.221 263.4;
  /*         Lighter  More vibrant  Slightly different blue */
}

This gives us:

  • Light mode: A vibrant blue that's readable
  • Dark mode: A slightly brighter, more vibrant blue that stands out against dark backgrounds
  • Consistent perception: Both feel equally "bright" in their contexts

How Themes Work in This Project

CSS Variables Approach:

This template uses CSS variables to define all colors in one place (src/app/globals.css):

@layer base {
  :root {
    /* Light mode colors */
    --background: 100% 0 0;          /* Pure white */
    --foreground: 9.8% 0.038 265.75; /* Almost black */
    --primary: 62.8% 0.214 259.6;    /* Blue */
  }

  .dark {
    /* Dark mode colors */
    --background: 13.4% 0.034 265.75; /* Very dark blue */
    --foreground: 98% 0.013 264.5;    /* Almost white */
    --primary: 70.9% 0.221 263.4;     /* Brighter blue */
  }
}

How It Works:

  1. Define once: Set all color values in globals.css
  2. Reference everywhere: Use these values throughout your app
    <div className="bg-primary text-primary-foreground">
      This uses the CSS variables automatically
    </div>
  3. Automatic dark mode: When .dark class is added to <html>, all colors switch

Tailwind Config Connection:

The tailwind.config.ts tells Tailwind to use these CSS variables:

colors: {
  primary: {
    DEFAULT: "oklch(var(--primary))",
    foreground: "oklch(var(--primary-foreground))",
  },
  // ... more colors
}

Benefits:

  1. Single source of truth: Change colors in one place
  2. Type-safe: Tailwind autocomplete works
  3. Runtime switching: Can change themes without rebuilding
  4. Consistent: Same colors across all components

Customizing Your Theme:

To change colors, edit src/app/globals.css:

:root {
  /* Change primary to green */
  --primary: 65% 0.18 145;  /* L: 65%, C: 0.18, H: 145° (green) */
}

Pro Tip: Use tools like OKLCH Color Picker to find colors you like, then copy the values directly into your CSS.

Dark Mode (next-themes)

The template includes dark mode support using next-themes:

  • System Detection: Automatically uses system preference
  • Manual Toggle: Click the sun/moon icon in the header
  • Persistent: Choice is saved in localStorage
  • No Flash: Uses suppressHydrationWarning to prevent flash on load

The ThemeProvider in layout.tsx wraps the entire app and makes theme context available everywhere.

App Router & Routing Patterns

This template demonstrates several Next.js App Router patterns:

Simple Routes

src/app/
├── page.tsx          → /
├── about/
│   └── page.tsx      → /about

Nested Layouts

Blog Example (/blog/*):

src/app/blog/
├── layout.tsx        ← Shared layout for all blog pages
├── page.tsx          → /blog (blog index)
└── [slug]/
    └── page.tsx      → /blog/any-post (dynamic route)

The blog/layout.tsx provides a sidebar that persists across all blog pages. This is perfect for:

  • Navigation menus
  • Filters/search
  • Category lists
  • Anything that should stay visible while content changes

Dashboard Example (/dashboard/*):

src/app/dashboard/
├── layout.tsx        ← Shared sidebar for all dashboard pages
├── page.tsx          → /dashboard (overview)
├── analytics/
│   └── page.tsx      → /dashboard/analytics
└── settings/
    └── page.tsx      → /dashboard/settings

The dashboard demonstrates a common admin pattern with:

  • Persistent sidebar navigation
  • Quick stats section
  • Multiple sub-routes that share the layout

Dynamic Routes

Dynamic routes use [paramName] syntax:

// src/app/blog/[slug]/page.tsx

interface BlogPostPageProps {
  params: Promise<{ slug: string }>;
}

export default async function BlogPostPage(props: BlogPostPageProps) {
  const params = await props.params;
  const post = await getPost(params.slug);
  return <div>{post.title}</div>;
}

Server vs Client Components

Server Components (default):

  • No "use client" directive
  • Can fetch data directly
  • No hooks or browser APIs
  • Better performance
// src/app/page.tsx - Server Component
export default async function Home() {
  const data = await fetch(...);  // Direct data fetching
  return <div>...</div>;
}

Client Components:

  • Has "use client" at the top
  • Can use hooks (useState, useEffect, etc.)
  • Can access browser APIs
  • Interactive features
// src/components/theme-toggle.tsx - Client Component
"use client";

import { useTheme } from "next-themes";

export function ThemeToggle() {
  const { theme, setTheme } = useTheme();  // Hook usage
  return <button onClick={() => setTheme(...)}>...</button>;
}

Available Scripts

All scripts use pnpm - install it globally with npm install -g pnpm if needed.

Development

# Start development server on http://localhost:3000
pnpm dev

# Build for production
pnpm build

# Start production server
pnpm start

Code Quality

# Run ESLint to check for issues
pnpm lint

# Auto-fix ESLint issues
pnpm lint:fix

# Type-check TypeScript without building
pnpm typecheck

Testing

# Run all Jest tests
pnpm test

# Run tests in watch mode (re-runs on file changes)
pnpm test:watch

# Run Playwright e2e tests
pnpm e2e

# Run e2e tests with interactive UI
pnpm e2e:ui

# View last e2e test report
pnpm e2e:report

Recommended Workflow

Before committing code:

pnpm lint:fix        # Fix linting issues
pnpm typecheck       # Check for TypeScript errors
pnpm test            # Run unit tests
pnpm build           # Ensure production build works

What's Included

Frontend

  • Next.js 16 with App Router
  • React 19 with TypeScript
  • Tailwind CSS for styling
  • shadcn/ui for beautiful, accessible components
  • next-themes for dark mode support
  • Lucide Icons for iconography

Code Quality

  • ESLint with strict TypeScript checking
  • TypeScript in strict mode
  • Prettier-friendly config

Testing

  • Jest for unit/integration tests
  • React Testing Library for component tests
  • Playwright for end-to-end tests

Example Code

  • API Routes: /api/products and /api/health
  • Server Components: Homepage with data fetching
  • Client Components: HealthCheck, ThemeToggle with loading/error states
  • Utility Functions: formatMoney, cn with tests
  • shadcn/ui Components: Button, Card, Badge, Switch
  • Nested Layouts: Blog and Dashboard with sidebar navigation
  • Dynamic Routes: /blog/[slug] for individual posts

Learning Resources

File-by-File Guide

  1. Start with: src/app/page.tsx - See how pages work
  2. Then explore: src/components/Button.tsx - Reusable components
  3. Try testing: __tests__/components/Button.test.tsx - Learn testing
  4. Build an API: src/app/api/health/route.ts - Create endpoints
  5. Add utilities: src/lib/formatMoney.ts - Helper functions

Concepts Demonstrated

  • Server vs Client Components: See the difference in page.tsx vs HealthCheck.tsx
  • TypeScript Types: Every function has clear types
  • API Routes: RESTful endpoints with /api/*
  • Error Handling: Loading and error states in HealthCheck.tsx
  • Testing: Unit tests for utilities, component tests, and e2e tests

Troubleshooting

1. pnpm: command not found

Solution:

npm install -g pnpm

2. Port 3000 already in use

Solution:

# Kill the process using port 3000
# Windows:
netstat -ano | findstr :3000
taskkill /PID <PID> /F

# Mac/Linux:
lsof -ti:3000 | xargs kill -9

# Or use a different port:
pnpm dev -- -p 3001

3. ESLint errors after adding new files

Solution:

pnpm lint:fix

4. TypeScript errors in IDE but build works

Solution:

# Restart TypeScript server in VS Code
Ctrl+Shift+P → "TypeScript: Restart TS Server"

# Or reload window
Ctrl+Shift+P → "Developer: Reload Window"

5. Tests failing with module resolution errors

Solution:

# Clear Jest cache
pnpm test --clearCache

# Reinstall dependencies
rm -rf node_modules
pnpm install

6. Playwright browsers not installed

Solution:

pnpx playwright install

7. Build fails with "Module not found"

Solution:

Check that imports use @/ for src files:

// ✅ Correct
import { Button } from "@/components/Button";

// ❌ Wrong
import { Button } from "../components/Button";

8. Styles not applying (Tailwind)

Solution:

Ensure globals.css is imported in layout.tsx:

import "./globals.css";

9. "Cannot find module" in tests

Solution:

Check jest.config.ts has the correct moduleNameMapper:

moduleNameMapper: {
  "^@/(.*)$": "<rootDir>/src/$1",
}

10. Git pre-commit hooks failing

Solution:

# Run the same checks manually
pnpm lint:fix
pnpm typecheck
pnpm test

11. shadcn/ui component not found

Solution:

Make sure you've added the component:

npx shadcn@latest add button
npx shadcn@latest add card

Check the import path uses @/components/ui/...:

import { Button } from "@/components/ui/button";  // ✅ Correct
import { Button } from "../components/ui/button"; // ❌ Wrong

12. Dark mode not working

Solution:

Ensure suppressHydrationWarning is on the <html> tag:

<html lang="en" suppressHydrationWarning>

Check that ThemeProvider wraps your app in layout.tsx.

13. Components don't have proper styling

Solution:

Make sure globals.css has the CSS variables and Tailwind directives:

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

@layer base {
  :root {
    --background: 0 0% 100%;
    /* ... more variables */
  }
}

14. "Cannot find module '@/lib/utils'"

Solution:

Create the utils file:

// src/lib/utils.ts
import { clsx, type ClassValue } from "clsx";
import { twMerge } from "tailwind-merge";

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

15. Nested layout not applying

Solution:

Check that your layout.tsx is in the correct directory and exports a default function:

// src/app/blog/layout.tsx
export default function BlogLayout({ children }: { children: ReactNode }) {
  return <div>{/* sidebar */}{children}</div>;
}

Next Steps

Extend This Template

  1. Add a database: Try Prisma with SQLite
  2. Add authentication: Use NextAuth.js
  3. Add more pages: Create /about, /contact
  4. Style components: Build a component library
  5. Add forms: Use React Hook Form with validation
  6. Deploy: Try Vercel or Netlify

Learning Path

  1. Week 1: Understand the existing code
  2. Week 2: Add a new page and component
  3. Week 3: Create your own API endpoint
  4. Week 4: Write tests for your code
  5. Week 5: Build a small project (todo list, blog, etc.)

Contributing

This is a learning template - feel free to modify anything! If you find issues or have suggestions, create an issue or pull request.

License

MIT - Feel free to use this for learning and building your own projects.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors