Files
Vault/UI_DESIGN_SYSTEM.md
twotalesanimation 81ad7e4ea9 Initial commit
2026-06-11 10:46:09 +02:00

30 KiB

OW Animation Arts Vault - UI Design System & Layout Guide

This document outlines the complete UI/style and layout architecture used in this codebase. Use this guide to create additional applications for the same client with consistent design and user experience.


Table of Contents

  1. Technology Stack
  2. Project Architecture
  3. Color System & Theming
  4. Layout System
  5. Navigation & Sidebar
  6. Page Layouts
  7. Component Library
  8. Typography & Spacing
  9. Form Patterns
  10. Responsive Design
  11. Dark Mode Implementation
  12. Reusable Patterns
  13. Asset Guidelines

1. Technology Stack

Core Framework

  • Next.js 16.0.3 - React framework with App Router
  • React 18+ - UI library
  • TypeScript - Type safety
  • Tailwind CSS 3.4+ - Utility-first CSS framework

UI Component Library

  • shadcn/ui - Pre-built, composable React components
  • @radix-ui/ - Unstyled, accessible component primitives
  • Lucide React - Beautiful, consistent icon library
  • Tabler Icons - Alternative icon set

State Management & Data

  • NextAuth.js - Authentication
  • Next-Themes - Theme management (dark/light mode)
  • Prisma ORM - Database abstraction
  • TanStack React Table - Advanced table data management
  • Sonner - Toast notifications

Additional Libraries

  • @dnd-kit - Drag and drop functionality
  • Embla Carousel - Carousel/slider component
  • Recharts - Data visualization charts
  • HLS.js - Video streaming (HLS protocol)
  • date-fns - Date utilities
  • class-variance-authority (CVA) - Component variant management
  • clsx - Conditional className merging

2. Project Architecture

Directory Structure

app/
├── layout.tsx                 # Root layout (server component)
├── page.tsx                   # Landing/home page
├── providers.tsx              # Client-side providers (SessionProvider, ThemeProvider)
├── globals.css                # Global styles & CSS variables
├── login/
│   ├── page.tsx               # Login page
│   └── unauthorized/
├── dashboard/
│   ├── page.tsx               # Protected dashboard
│   ├── dashboard-client.tsx   # Client component with content
│   ├── liked-videos/
│   └── watch-history/
├── admin/
│   ├── page.tsx               # Admin gate (server component)
│   ├── admin-client.tsx       # Admin UI (client component)
│   ├── users/
│   ├── courses/
│   ├── playlists/
│   ├── videos/
│   ├── enrollments/
│   └── stats/
├── api/                       # API routes for server operations
└── videoplayer/               # Video player page

components/
├── app-sidebar.tsx            # Main navigation sidebar
├── site-header.tsx            # Top header with breadcrumbs
├── nav-main.tsx               # Primary navigation menu
├── nav-secondary.tsx          # Secondary navigation items
├── nav-user.tsx               # User profile dropdown
├── login-form.tsx             # Google OAuth login form
├── data-table.tsx             # Advanced data table component
├── hls.tsx                     # Video player (HLS streaming)
├── chart-area-interactive.tsx # Interactive chart component
├── comments-section.tsx       # Comments/discussion area
├── admin-notifications.tsx    # Admin notifications
├── ui/                        # shadcn/ui components
│   ├── button.tsx
│   ├── card.tsx
│   ├── sidebar.tsx
│   ├── input.tsx
│   ├── label.tsx
│   ├── badge.tsx
│   ├── dropdown-menu.tsx
│   ├── dialog.tsx
│   ├── drawer.tsx
│   ├── table.tsx
│   ├── tabs.tsx
│   ├── carousel.tsx
│   ├── chart.tsx
│   ├── select.tsx
│   ├── checkbox.tsx
│   ├── field.tsx
│   ├── collapsible.tsx
│   └── ... (27+ components)

lib/
├── utils.ts                   # Utility functions (cn for className merging)
├── auth-options.ts            # NextAuth configuration
├── auth-check.ts              # Authentication helpers
├── prisma.ts                  # Prisma client
└── video-urls.ts              # Video URL generation

hooks/
├── use-mobile.ts              # Mobile breakpoint hook
├── useVideo.ts                # Video data hooks
├── usePlaylists.ts            # Playlist data hooks
└── usePlaylist.ts             # Single playlist hook

public/
├── icon.svg                   # App icon
├── vault.png                  # Vault image (login page)
└── vault.exr                  # Vault image (high-res)

Component Structure Pattern

Server Component (pages):

// app/dashboard/page.tsx
import { requireUser } from '@/lib/auth-check';
import DashboardClient from './dashboard-client';

export default async function DashboardPage() {
  const session = await requireUser('/login');
  return <DashboardClient />;
}

Client Component (content):

// app/dashboard/dashboard-client.tsx
'use client';
import React from 'react';
import { AppSidebar } from '@/components/app-sidebar';
import { SiteHeader } from '@/components/site-header';
import { SidebarInset, SidebarProvider } from '@/components/ui/sidebar';

export default function DashboardClient() {
  return (
    <SidebarProvider>
      <AppSidebar />
      <SidebarInset>
        <SiteHeader />
        <main className="flex flex-col gap-4 p-4">
          {/* Page content */}
        </main>
      </SidebarInset>
    </SidebarProvider>
  );
}

3. Color System & Theming

CSS Variables (OKLch Color Space)

The design system uses OKLch color space for perceptually uniform colors across light and dark modes.

Light Mode (:root):

--background: oklch(1 0 0);                      /* White */
--foreground: oklch(0.141 0.005 285.823);        /* Near Black */
--card: oklch(1 0 0);                            /* White */
--card-foreground: oklch(0.141 0.005 285.823);   /* Near Black */
--primary: oklch(0.646 0.222 41.116);            /* Warm Yellow/Orange */
--primary-foreground: oklch(0.98 0.016 73.684);  /* Light cream */
--secondary: oklch(0.967 0.001 286.375);         /* Light gray */
--secondary-foreground: oklch(0.21 0.006 285.885);
--muted: oklch(0.967 0.001 286.375);             /* Light gray (buttons, etc) */
--muted-foreground: oklch(0.552 0.016 285.938);  /* Medium gray (text) */
--accent: oklch(0.967 0.001 286.375);            /* Light gray */
--accent-foreground: oklch(0.21 0.006 285.885);
--destructive: oklch(0.577 0.245 27.325);        /* Red */
--border: oklch(0.92 0.004 286.32);              /* Light gray */
--input: oklch(0.92 0.004 286.32);               /* Light gray */
--ring: oklch(0.75 0.183 55.934);                /* Orange/Gold for focus */

Dark Mode (.dark):

.dark {
  --background: oklch(0.141 0.005 285.823);      /* Near Black */
  --foreground: oklch(0.985 0 0);                /* White */
  --card: oklch(0.21 0.006 285.885);             /* Dark gray */
  --card-foreground: oklch(0.985 0 0);           /* White */
  --primary: oklch(0.705 0.213 47.604);          /* Lighter Orange */
  --secondary: oklch(0.274 0.006 286.033);       /* Dark gray */
  --muted: oklch(0.274 0.006 286.033);           /* Dark gray */
  --muted-foreground: oklch(0.705 0.015 286.067);
  --border: oklch(1 0 0 / 10%);                  /* White with 10% opacity */
  --input: oklch(1 0 0 / 15%);                   /* White with 15% opacity */
  --ring: oklch(0.408 0.123 38.172);             /* Red-orange for focus */
}

Chart Colors

--chart-1: oklch(0.837 0.128 66.29);   /* Warm yellow */
--chart-2: oklch(0.705 0.213 47.604);  /* Orange */
--chart-3: oklch(0.646 0.222 41.116);  /* Dark orange */
--chart-4: oklch(0.553 0.195 38.402);  /* Red-orange */
--chart-5: oklch(0.47 0.157 37.304);   /* Deep red-orange */

Sidebar Colors

--sidebar: oklch(0.985 0 0);
--sidebar-foreground: oklch(0.141 0.005 285.823);
--sidebar-primary: oklch(0.646 0.222 41.116);
--sidebar-primary-foreground: oklch(0.98 0.016 73.684);
--sidebar-accent: oklch(0.967 0.001 286.375);
--sidebar-accent-foreground: oklch(0.21 0.006 285.885);
--sidebar-border: oklch(0.92 0.004 286.32);
--sidebar-ring: oklch(0.75 0.183 55.934);

Color Usage

Color Usage
Primary Main CTA buttons, active links, selected items
Secondary Secondary buttons, badges
Muted Disabled states, placeholder text, subtle backgrounds
Accent Hover states, subtle highlights
Destructive Delete buttons, error states
Ring Focus indicators, form input focus

4. Layout System

Main Layout Structure

The application uses a sidebar + content layout pattern:

┌─────────────────────────────┐
│     SidebarProvider         │
├──────────┬──────────────────┤
│          │                  │
│AppSidebar│  SidebarInset    │
│          ├──────────────────┤
│          │   SiteHeader     │
│  Logo    │  (breadcrumbs)   │
│  Nav     ├──────────────────┤
│  Items   │                  │
│          │   Main Content   │
│ Footer   │                  │
│ (User)   │                  │
│          │                  │
└──────────┴──────────────────┘

Layout Implementation

// Standard page layout
export default function DashboardClient() {
  return (
    <SidebarProvider>
      <AppSidebar />
      <SidebarInset>
        <SiteHeader />
        <main className="flex flex-col gap-4 p-4 md:gap-6 md:p-6">
          {/* Page content */}
        </main>
      </SidebarInset>
    </SidebarProvider>
  );
}

Content Container Sizing

// Wide content area with centered max-width
<div className="flex flex-col gap-6 py-4 md:gap-6 md:py-6 px-4 lg:px-16">
  {/* Content */}
</div>

// Grid layout patterns
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
  {/* Columns adjust: 1 on mobile, 3 on desktop */}
</div>

<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
  {/* Responsive grid: 1 → 2 → 3 columns */}
</div>

5. Navigation & Sidebar

AppSidebar Component

Located in components/app-sidebar.tsx

Features:

  • Collapsible sidebar (offcanvas mode on mobile)
  • Uses next-auth for user session and roles
  • Dynamic navigation based on user role (admin/superadmin/user)
  • Logo and brand in header
  • User profile footer with avatar

Navigation Items:

For Regular Users:

  • Library → /dashboard
  • Liked Videos → /dashboard/liked-videos
  • History → /dashboard/watch-history
  • Search → # (secondary item)

For Admins/Superadmins (Additional):

  • Admin (collapsible parent with submenu):
    • Manage Users → /admin/users
    • Manage Enrollments → /admin/enrollments
    • Manage Courses → /admin/courses
    • Manage Playlists → /admin/playlists
    • Manage Videos → /admin/videos

SiteHeader Component

Located in components/site-header.tsx

Features:

  • Responsive breadcrumb navigation
  • Sidebar toggle trigger button
  • Dynamic breadcrumbs based on current route
  • Breadcrumb mapping for all main routes

Breadcrumb Pattern:

Dashboard
Dashboard > Liked Videos
Dashboard > Watch History
Admin Panel
Admin Panel > Users > User Details
Admin Panel > Videos > Edit Video

NavMain Component

Located in components/nav-main.tsx

Features:

  • Collapsible menu items with expandable submenus
  • Icon support (Tabler icons)
  • Smooth transitions with chevron rotation
  • Accessible keyboard navigation
// Usage
<NavMain 
  items={[
    { 
      title: 'Dashboard', 
      url: '/dashboard', 
      icon: IconDashboard 
    },
    { 
      title: 'Admin', 
      url: '/admin', 
      icon: IconShieldCheck,
      items: [
        { title: 'Manage Users', url: '/admin/users' }
      ]
    }
  ]}
/>

NavSecondary Component

Located in components/nav-secondary.tsx

For secondary navigation items (Search, Help, etc.)

NavUser Component

Located in components/nav-user.tsx

Features:

  • User avatar display
  • User name and email
  • Dropdown menu with user actions
  • Sign out button
  • Accessible user menu

6. Page Layouts

Login Page

Location: app/login/page.tsx

Layout Structure:

Centered Container (min-h-screen, flex-col)
├── Brand Logo + Text (top)
├── Vault Image
└── Login Form (Card with Google OAuth button)

Styling:

<div className="bg-muted flex min-h-svh flex-col items-center justify-center gap-6 p-6 md:p-10">
  <div className="flex w-full max-w-sm flex-col gap-6">
    {/* Content */}
  </div>
</div>

Key Features:

  • Centered, max-width container (sm = 448px)
  • Muted background color
  • Responsive padding (6 on mobile, 10 on desktop)
  • Logo + app name at top
  • Vault imagery
  • Google OAuth login form in card

Dashboard Page

Location: app/dashboard/page.tsx (server) → dashboard-client.tsx (client)

Layout Structure:

Sidebar + Content Layout
├── AppSidebar
└── SidebarInset
    ├── SiteHeader (breadcrumbs)
    └── Main Content
        ├── Hero Section (Welcome)
        ├── Video Carousels (by course/playlist)
        ├── Segmented Progress Bars
        └── Lock indicators for restricted content

Content Structure:

<div className="flex flex-col gap-6">
  {/* Section: Welcome/Title */}
  <div>
    <h1 className="text-2xl font-bold">Welcome back, {userName}</h1>
    <p className="text-muted-foreground">Continue your learning</p>
  </div>

  {/* Section: Courses/Playlists with Video Carousels */}
  {playlists.map(playlist => (
    <div key={playlist.id} className="flex flex-col gap-4">
      <h2 className="text-xl font-semibold">{playlist.title}</h2>
      <Carousel>
        {/* Video cards with progress bars */}
      </Carousel>
    </div>
  ))}
</div>

Admin Page

Location: app/admin/page.tsx (server) → admin-client.tsx (client)

Layout Structure:

Sidebar + Content Layout
├── AppSidebar (with Admin menu)
└── SidebarInset
    ├── SiteHeader (breadcrumbs)
    └── Main Content
        ├── Page Title & Description
        ├── Grid of Management Cards (2-3 columns)
        │   ├── Video Statistics Card
        │   ├── Manage Users Card
        │   ├── Manage Courses Card
        │   ├── Manage Playlists Card
        │   ├── Manage Videos Card
        │   └── Manage Enrollments Card
        └── Admin Notifications (right sidebar)

Admin Grid Layout:

<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
  <div className="lg:col-span-2">
    <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
      {/* Admin cards */}
    </div>
  </div>
  <div className="lg:col-span-1">
    <AdminNotifications />
  </div>
</div>

7. Component Library

shadcn/ui Components

The following pre-built components are used throughout the application:

Component Used For
Button CTAs, form submissions, interactive elements
Card Container for content sections
Input Text input fields
Label Form labels
Badge Tags, status indicators, trending indicators
Sidebar Main navigation (with collapsible support)
Dropdown Menu User menus, action menus
Dialog Modal dialogs
Drawer Mobile-friendly off-canvas menus
Table Data tables with sorting, filtering, pagination
Tabs Tab navigation
Carousel Image/video sliders
Chart Data visualization (AreaChart, BarChart, etc.)
Select Dropdown select fields
Checkbox Multi-select checkboxes
Form/Field Form field containers and validation
Collapsible Expandable/collapsible sections
Progress Progress bars
Avatar User profile pictures
Separator Visual dividers
Toast/Sonner Toast notifications
Tooltip Info tooltips

Custom Components

HlsPlayer (components/hls.tsx)

  • HLS video streaming with fallback to MP4
  • Subtitle/caption support
  • Controls with no-download protection
  • Used in: Video player pages

DataTable (components/data-table.tsx)

  • Advanced table with sorting, filtering, pagination
  • Column visibility toggle
  • Drag-and-drop row reordering (via dnd-kit)
  • Export functionality
  • Used in: Admin management pages (Users, Videos, Courses, etc.)

SegmentedProgressBar (components/segmented-progress-bar.tsx)

  • Shows watched segments of video
  • Visual indication of progress across duration
  • Used in: Dashboard video carousels

VideoCarousel (components/VideoCarousel.tsx)

  • Embla carousel-based video slider
  • Responsive grid layout
  • Lock indicators for restricted content
  • Used in: Dashboard, video listings

CommentsSection (components/comments-section.tsx)

  • Comments display and threading
  • Used in: Video player pages

LoginForm (components/login-form.tsx)

  • Google OAuth integration
  • Card-based layout
  • Terms and privacy links
  • Used in: Login page

8. Typography & Spacing

Font Families

The project uses Geist font family (Next.js default):

  • --font-sans: var(--font-geist-sans) - Primary (body text, UI)
  • --font-mono: var(--font-geist-mono) - Monospace (code, technical text)

Imported in layout.tsx:

import { Geist, Geist_Mono } from "next/font/google";

Typography Scale

Element Class Size Weight Usage
H1 .text-3xl 30px bold (700) Page titles
H2 .text-2xl 24px bold (700) Section titles
H3 .text-xl 20px semibold (600) Subsection titles
H4 .text-lg 18px semibold (600) Card titles
Body .text-base 16px normal (400) Body text
Small .text-sm 14px normal (400) Labels, captions, secondary text
Extra Small .text-xs 12px normal (400) Tiny labels

Spacing Scale

Tailwind default spacing (4px base):

Class Size
gap-1 4px
gap-2 8px
gap-3 12px
gap-4 16px
gap-6 24px
gap-8 32px
p-4 16px padding
p-6 24px padding
py-4 16px vertical padding
px-4 16px horizontal padding

Text Opacity/Color Hierarchy

// Primary text
<p className="text-foreground">Main content</p>

// Secondary text
<p className="text-muted-foreground">Secondary info</p>

// Subtle text
<span className="text-xs text-muted-foreground">Caption</span>

9. Form Patterns

Login Form Pattern

Located in components/login-form.tsx

<div className="flex flex-col gap-6">
  <Card>
    <CardHeader className="text-center">
      <CardTitle className="text-xl">Welcome</CardTitle>
      <CardDescription>Please login with your account</CardDescription>
    </CardHeader>
    <CardContent>
      <form>
        <FieldGroup>
          <Field>
            <Button 
              variant="outline" 
              className="w-full flex items-center justify-center gap-2"
              onClick={() => signIn('google')}
            >
              <GoogleIcon className="w-4 h-4" />
              Login with Google
            </Button>
          </Field>
        </FieldGroup>
      </form>
    </CardContent>
  </Card>
  <FieldDescription className="text-center">
    By continuing, you agree to our <a href="#">Terms of Service</a> and 
    <a href="#">Privacy Policy</a>
  </FieldDescription>
</div>

Admin Form Patterns

Located in app/admin/*/ directories

Data Table Integration:

<DataTable 
  columns={columns}
  data={data}
  onSave={handleSave}
  onDelete={handleDelete}
/>

Modal Form Pattern:

<Dialog open={open} onOpenChange={setOpen}>
  <DialogContent>
    <DialogHeader>
      <DialogTitle>Create New Item</DialogTitle>
    </DialogHeader>
    <form>
      {/* Form fields */}
    </form>
  </DialogContent>
</Dialog>

10. Responsive Design

Tailwind Breakpoints

sm: 640px
md: 768px
lg: 1024px
xl: 1280px
2xl: 1536px

Mobile-First Approach

All responsive classes use mobile-first convention:

// Mobile: 1 column, Tablet: 2 columns, Desktop: 3 columns
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
  {/* Content */}
</div>

// Mobile: padding-4, Desktop: padding-6
<main className="p-4 md:p-6">
  {/* Content */}
</main>

// Mobile: hidden, Desktop: visible
<aside className="hidden md:block">
  {/* Content only on desktop */}
</aside>

Mobile-Specific Components

  • Drawer - Off-canvas menus on mobile (instead of Dialog)
  • Sidebar Collapsible - Sidebar collapses to mobile menu on small screens
  • useMobile Hook - hooks/use-mobile.ts for React-based breakpoint logic
const isMobile = useIsMobile();

return isMobile ? <MobileLayout /> : <DesktopLayout />;

Container Queries

Used for responsive component sizing:

<div className="@container">
  <h1 className="@[250px]:text-3xl text-2xl">
    Responsive heading
  </h1>
</div>

11. Dark Mode Implementation

Theme Provider Setup

Located in app/providers.tsx:

<ThemeProvider
  attribute="class"        // Toggles 'class' on <html>
  defaultTheme="dark"      // Dark by default
  enableSystem={false}     // Don't auto-detect system preference
>
  {children}
</ThemeProvider>

Dark Mode Styling

CSS variables automatically switch in .dark class:

// Light mode (automatic)
:root {
  --background: oklch(1 0 0);      /* White */
  --foreground: oklch(0.141 0.005 285.823); /* Dark gray */
}

// Dark mode (when .dark is on <html>)
.dark {
  --background: oklch(0.141 0.005 285.823); /* Dark gray */
  --foreground: oklch(0.985 0 0);  /* White */
}

Toggle Theme Button Pattern

// In component
const { theme, setTheme } = useTheme();

<Button 
  onClick={() => setTheme(theme === 'dark' ? 'light' : 'dark')}
>
  {theme === 'dark' ? <SunIcon /> : <MoonIcon />}
</Button>

12. Reusable Patterns

Protected Page Pattern

Server Component (checks auth):

// app/dashboard/page.tsx
import { requireUser } from '@/lib/auth-check';
import DashboardClient from './dashboard-client';

export default async function DashboardPage() {
  const session = await requireUser('/login');
  return <DashboardClient />;
}

Client Component (renders content):

// app/dashboard/dashboard-client.tsx
'use client';
import React from 'react';
import { AppSidebar } from '@/components/app-sidebar';
import { SiteHeader } from '@/components/site-header';

export default function DashboardClient() {
  return (
    <SidebarProvider>
      <AppSidebar />
      <SidebarInset>
        <SiteHeader />
        <main /* content */>
      </SidebarInset>
    </SidebarProvider>
  );
}

Role-Based Navigation

Located in components/app-sidebar.tsx:

const { data: session } = useSession();
const role = session?.user?.role ?? 'user';

const navMain = React.useMemo(() => {
  const base = [...navMainBase];
  
  if (role === 'admin' || role === 'superadmin') {
    base.unshift({
      title: 'Admin',
      url: '/admin',
      icon: IconShieldCheck,
      items: [/*submenu items*/]
    });
  }
  
  return base;
}, [role]);

Session Initialization

Located in app/layout.tsx:

export default async function RootLayout({ children }) {
  // Get server session once, pass to client providers
  const session = await getServerSession(authOptions);

  return (
    <html>
      <body>
        <Providers session={session}>
          {children}
        </Providers>
        <Toaster />
      </body>
    </html>
  );
}

Data Fetching Pattern

Using useFetch/SWR for client-side data:

// hooks/usePlaylists.ts
export function usePlaylists() {
  const [subjects, setSubjects] = React.useState([]);
  const [isLoading, setIsLoading] = React.useState(true);

  React.useEffect(() => {
    fetchPlaylists().then(data => {
      setSubjects(data);
      setIsLoading(false);
    });
  }, []);

  return { subjects, isLoading };
}

// Usage in component
export default function DashboardClient() {
  const { subjects, isLoading } = usePlaylists();
  
  if (isLoading) return <LoadingState />;
  
  return <>{/* Content */}</>;
}
// Embla-based carousel
<Carousel className="w-full">
  <CarouselContent className="gap-4">
    {videos.map(video => (
      <CarouselItem key={video.id} className="basis-1/2 md:basis-1/3 lg:basis-1/4">
        <VideoCard video={video} />
      </CarouselItem>
    ))}
  </CarouselContent>
  <CarouselPrevious />
  <CarouselNext />
</Carousel>

Badge & Status Pattern

// Icon + Badge combination
<div className="flex items-center gap-2">
  <Badge variant="outline">
    <IconTrendingUp className="size-4" />
    +12.5%
  </Badge>
</div>

// Status badge
<Badge variant={status === 'active' ? 'default' : 'secondary'}>
  {status}
</Badge>

13. Asset Guidelines

Logo & Branding

  • Icon: /public/icon.svg - Small 5x5 size in sidebar header
  • Vault Image: /public/vault.png - 1:1 aspect ratio for login page
  • High-Res Alternative: /public/vault.exr - For promotional use

Image Optimization

// Use Next.js Image for optimization
import Image from 'next/image';

<Image 
  src="/path/to/image.png" 
  alt="Description" 
  width={400} 
  height={300}
  quality={60}
/>

// Regular images
<img src="/icon.svg" alt="Icon" className="size-5" />

Approved Icon Sets

  1. Tabler Icons (@tabler/icons-react) - Primary

    • Large collection (4000+)
    • Consistent stroke weight
    • Import: import { IconHome, IconUsers } from '@tabler/icons-react';
  2. Lucide React (lucide-react) - Secondary

    • Modern, clean icons
    • Import: import { Home, Users } from 'lucide-react';
  3. SVG Files - Custom branding

    • Small icons in public/
    • Optimized for performance

Sizing Conventions

// Icon sizes
<Icon className="size-3" />  /* 12px */
<Icon className="size-4" />  /* 16px */
<Icon className="size-5" />  /* 20px */
<Icon className="w-6 h-6" /> /* 24px */
<Icon className="w-8 h-8" /> /* 32px */

// Image sizes
<Image className="w-full" />     /* Full width */
<Image className="max-w-sm" />   /* Max 448px */
<Image className="max-w-md" />   /* Max 512px */

Quick Reference - Common Patterns

Button Variants

<Button>Default</Button>
<Button variant="secondary">Secondary</Button>
<Button variant="outline">Outline</Button>
<Button variant="ghost">Ghost</Button>
<Button variant="destructive">Delete</Button>
<Button disabled>Disabled</Button>
<Button size="sm">Small</Button>
<Button size="lg">Large</Button>

Card Layout

<Card>
  <CardHeader>
    <CardTitle>Title</CardTitle>
    <CardDescription>Subtitle</CardDescription>
  </CardHeader>
  <CardContent>
    {/* Content */}
  </CardContent>
  <CardFooter>
    {/* Footer */}
  </CardFooter>
</Card>

Container Classes

// Full width
className="w-full"

// Max width containers
className="w-full max-w-sm"      /* 448px */
className="w-full max-w-md"      /* 512px */
className="w-full max-w-lg"      /* 576px */
className="w-full max-w-2xl"     /* 672px */

// Flex containers
className="flex flex-col gap-4"
className="flex items-center justify-between"

// Grid containers
className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4"

Responsive Padding

className="p-4 md:p-6 lg:p-8"          /* All sides */
className="px-4 py-6"                  /* Horizontal & vertical */
className="p-4 md:py-6 md:px-8"        /* Different per breakpoint */

Text & Styling

className="text-foreground"            /* Main text */
className="text-muted-foreground"      /* Secondary text */
className="text-sm text-muted-foreground" /* Label text */
className="font-semibold"              /* Bold text */
className="line-clamp-1"               /* Truncate to 1 line */
className="text-center"                /* Centered text */

Migration Checklist for New Projects

When creating a new app with this design system:

  • Copy globals.css (CSS variables)
  • Copy tailwind.config.cjs
  • Copy components/ui/ directory (all base components)
  • Copy components/app-sidebar.tsx, site-header.tsx, nav-*.tsx
  • Copy app/providers.tsx (SessionProvider, ThemeProvider)
  • Copy lib/utils.ts (utility functions)
  • Copy lib/auth-options.ts and auth-related files
  • Install dependencies from package.json
  • Update branding assets (/public icons, logos)
  • Customize navigation items in app-sidebar.tsx
  • Update theme colors in globals.css if needed
  • Test responsive design on mobile/tablet/desktop

Key Takeaways

  1. Framework: Next.js 16 with shadcn/ui component library
  2. Styling: Tailwind CSS with OKLch color variables
  3. Layout: Sidebar + content pattern with collapsible nav
  4. Dark Mode: CSS class-based switching with automatic variable updates
  5. Components: Pre-built shadcn/ui components + custom components (HLS, DataTable, etc.)
  6. Responsiveness: Mobile-first, Tailwind breakpoints (sm/md/lg/xl)
  7. Navigation: Role-based dynamic nav, breadcrumb system
  8. Auth: NextAuth.js with Google OAuth integration
  9. Icons: Tabler Icons (primary), Lucide (secondary)
  10. Consistency: Use className patterns, spacing scale, and color variables throughout

Last Updated: February 2026 Version: 1.0

For questions or updates to this design system guide, refer to the component files and config files referenced throughout.