1139 lines
30 KiB
Markdown
1139 lines
30 KiB
Markdown
# 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](#technology-stack)
|
|
2. [Project Architecture](#project-architecture)
|
|
3. [Color System & Theming](#color-system--theming)
|
|
4. [Layout System](#layout-system)
|
|
5. [Navigation & Sidebar](#navigation--sidebar)
|
|
6. [Page Layouts](#page-layouts)
|
|
7. [Component Library](#component-library)
|
|
8. [Typography & Spacing](#typography--spacing)
|
|
9. [Form Patterns](#form-patterns)
|
|
10. [Responsive Design](#responsive-design)
|
|
11. [Dark Mode Implementation](#dark-mode-implementation)
|
|
12. [Reusable Patterns](#reusable-patterns)
|
|
13. [Asset Guidelines](#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):**
|
|
```tsx
|
|
// 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):**
|
|
```tsx
|
|
// 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`):**
|
|
```css
|
|
--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`):**
|
|
```css
|
|
.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
|
|
```css
|
|
--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
|
|
```css
|
|
--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
|
|
|
|
```tsx
|
|
// 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
|
|
|
|
```tsx
|
|
// 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
|
|
|
|
```tsx
|
|
// 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:**
|
|
```tsx
|
|
<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:**
|
|
```tsx
|
|
<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:**
|
|
```tsx
|
|
<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`:
|
|
```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
|
|
|
|
```tsx
|
|
// 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`
|
|
|
|
```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:**
|
|
```tsx
|
|
<DataTable
|
|
columns={columns}
|
|
data={data}
|
|
onSave={handleSave}
|
|
onDelete={handleDelete}
|
|
/>
|
|
```
|
|
|
|
**Modal Form Pattern:**
|
|
```tsx
|
|
<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:
|
|
|
|
```tsx
|
|
// 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
|
|
|
|
```tsx
|
|
const isMobile = useIsMobile();
|
|
|
|
return isMobile ? <MobileLayout /> : <DesktopLayout />;
|
|
```
|
|
|
|
### Container Queries
|
|
|
|
Used for responsive component sizing:
|
|
|
|
```tsx
|
|
<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`:
|
|
|
|
```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:
|
|
|
|
```tsx
|
|
// 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
|
|
|
|
```tsx
|
|
// 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):**
|
|
```tsx
|
|
// 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):**
|
|
```tsx
|
|
// 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`:
|
|
|
|
```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`:
|
|
|
|
```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:
|
|
|
|
```tsx
|
|
// 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 */}</>;
|
|
}
|
|
```
|
|
|
|
### Carousel Pattern
|
|
|
|
```tsx
|
|
// 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
|
|
|
|
```tsx
|
|
// 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
|
|
|
|
```tsx
|
|
// 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
|
|
|
|
```tsx
|
|
// 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
|
|
```tsx
|
|
<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
|
|
```tsx
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle>Title</CardTitle>
|
|
<CardDescription>Subtitle</CardDescription>
|
|
</CardHeader>
|
|
<CardContent>
|
|
{/* Content */}
|
|
</CardContent>
|
|
<CardFooter>
|
|
{/* Footer */}
|
|
</CardFooter>
|
|
</Card>
|
|
```
|
|
|
|
### Container Classes
|
|
```tsx
|
|
// 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
|
|
```tsx
|
|
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
|
|
```tsx
|
|
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.
|