# HTW Design System

**Source of Truth**: `docs/design-tokens.json`
**Visual Reference**: `/public/htw-design-system.html`
**Kit Version**: 3.0.0
**Updated**: 2026-09-18 (Hawaii time)
**Public Kit**: [/brand-kit](/brand-kit)
**Public History**: [/brand-kit/history](/brand-kit/history)

## Scope and version history

This edition records the approved public website direction: a video- and photo-led Hawaii Tech Week brand with a precise, product-like interface. The logo and core brand palette remain recognizable. The composition, typography, color roles, and navigation have changed.

Use the **public-site profile** below for public marketing pages. Existing light/dark UI tokens, form controls, calendar categories, and component recipes remain available for their original jobs; they are not a mandate to make every page look alike. Forms, tools, and report reading areas can use light surfaces for readability.

The public history provides dated changes, reasons, and permanent edition links. Version [2.0.0](/brand-kit/versions/2.0.0) preserves the pre-refresh guide, tokens, visual reference, and available logo assets. Its original publication date is not established; the archive records its capture date instead. Version [3.0.0](/brand-kit/versions/3.0.0) documents this refresh. Preserve a released edition before publishing a replacement; do not overwrite its downloads.

Core brand editions and annual campaign kits are separate. A year-specific graphic, sponsor roster, date, or report is not a new core identity. Existing dates, totals, offers, and claims must be verified for each use.

## Public-site profile (3.0.0)

- **Composition:** Full-width bands on a predominantly navy canvas, with shared alignment, clear hierarchy, thin dividers, and restrained controls. Media shows the people and experience; text makes participation understandable. Place About before featured media on the homepage.
- **Color:** Navy and royal blue anchor the experience. Gold is the primary action color. Green supports nature, community, active states, and selected bands; it does not replace gold CTAs. The homepage About copy panel uses editorial sand. Keep the footer navy.
- **Typography:** Inter, sentence-case headings, medium to semibold weights (500–600), large responsive scale, and comfortable body line height. Small uppercase labels can provide orientation; decorative numerical section eyebrows are not part of this direction. Functional step numbers and carousel positions still have a purpose.
- **Interface:** 4px corners, 1px borders for public cards and actions, visible focus, predictable interaction, and 48px primary action height. Avoid making every section a rounded card. Preserve working signup, host, and sponsor flows.
- **Media:** Use existing approved media as a starting point; choose the shape and placement independently of final photography. Preserve sponsor marks, tiers, and recognition. Autoplay background video is muted, offers pause, and respects reduced-motion preferences; a separate playback action can open the full film.
- **Reading surfaces:** White and near-white remain useful for forms, tools, and long reports. A historical report can receive layout improvements without changing its original figures, photos, quotations, or sponsor recognition.

| Public role | Value | Implementation reference |
|-------------|-------|-------------------------|
| Main canvas / footer | `#04153D` | HTW Navy, `htw-navy` |
| Royal blue / portrait backing | `#002868` | Royal Navy, `htw-royal-navy` |
| Main action / hover | `#FFB81C` / `#FFC843` | `htw-gold` / `htw-gold-hover` |
| Nature accent | `#1B7A5A` | Royal Green, `htw-green` |
| Soft green accents | `#B9D8B0` | `htw-mist` |
| Blue heading highlights | `#91B8EC` | `htw-highlight` |
| Body text on navy | `#B7C6DF` | `htw-muted` |
| Homepage About surface | `#F2EEE3` | Editorial sand, `htw-editorial-sand` |
| Fine dividers | `rgb(255 255 255 / 17%)` | Public section/card borders |
| Keyboard focus on dark | `#00D4FF` | Cyan, `htw-cyan`; use a contrasting dark focus ring on light panels |

The public profile is recorded under `publicSite` in `docs/design-tokens.json`. Its implementation references are `src/components/redesign/public.module.css` and `src/components/redesign/bands/bands.module.css`. Reuse these shared patterns rather than creating another page-level theme.

---

## 1. Color Palette

### 1a. Brand Colors (Core)

8 colors derived from King Kalakaua's Royal Standard + tech accent.

| Name | Hex | Tailwind Class | Role |
|------|-----|----------------|------|
| **HTW Navy** | `#04153D` | `htw-navy` | Primary brand color (logo). Headers, dark backgrounds, text on light. |
| **Royal Navy** | `#002868` | `htw-royal-navy` | Heritage blue. Gradient base, alternate dark backgrounds. |
| **Royal Gold** | `#FFB81C` | `htw-gold` | Primary CTA buttons, highlights, Hawaiian warmth. |
| **Royal Gold Hover** | `#FFC843` | `htw-gold-hover` | Gold hover/active state. |
| **Royal Green** | `#1B7A5A` | `htw-green` | Nature accent, success states, land (aina). |
| **Neon Cyan** | `#00D4FF` | `htw-cyan` | Tech energy, focus rings, links on dark. |
| **Royal Red** | `#CE1126` | `htw-red` | Error states, destructive actions. Sacred to alii. |
| **White** | `#FFFFFF` | `white` | Primary neutral. Text on dark, backgrounds, breathing room. |

> Use with any Tailwind prefix: `bg-htw-navy`, `text-htw-gold`, `border-htw-cyan`, `from-htw-royal-navy`, etc.

### 1b. Background Colors

| Name | Hex | Tailwind Class | Usage |
|------|-----|----------------|-------|
| **White** | `#FFFFFF` | `bg-white` | Reading, form, tool, and report surfaces where useful |
| **Solar Cream** | `#FFFAEC` | `bg-htw-sand` | Existing utility surface; distinct from the new editorial sand, not a global replacement target |
| **Editorial Sand** | `#F2EEE3` | `bg-htw-editorial-sand` | Approved homepage About copy panel; use deliberately |
| **HTW Navy** | `#04153D` | `bg-htw-navy` | Primary public marketing canvas, media overlays, header, and footer |
| **Royal Navy** | `#002868` | `bg-htw-royal-navy` | Gradients and deep overlays on dark imagery |

**Section backgrounds:** The approved public site uses full-width bands with navy continuity. Create rhythm through type, media, alignment, and spacing; change surface color when it helps a section do its job. Sand is an intentional About panel, and green supports selected community areas. White remains a reading or interaction surface, not the default public marketing canvas.

### 1c. Neutrals — Light UI / Reading Mode

These are available as Tailwind utility classes: `text-neutral-text-primary`, `bg-neutral-surface-elevated`, `border-neutral-border`, etc.

| Token | Hex | Usage |
|-------|-----|-------|
| `neutral-bg` | `#FFFFFF` | Primary background |
| `neutral-bg-subtle` | `#FFFAEC` | Solar Cream — sparse warm surface, not page-stripe wallpaper |
| `neutral-surface` | `#FFFFFF` | Cards, panels |
| `neutral-surface-elevated` | `#F8FAFC` | Dropdowns, modals, elevated surfaces |
| `neutral-text-primary` | `#0B1220` | Primary text |
| `neutral-text-secondary` | `#334155` | Secondary text |
| `neutral-text-muted` | `#64748B` | Helper text, captions |
| `neutral-border` | `#CBD5E1` | Standard borders |
| `neutral-border-subtle` | `#F1F5F9` | Subtle dividers |

### 1d. Neutrals — Existing Dark UI Mode

These retained component tokens are distinct from the navy public-site profile above.

| Token | Hex | Usage |
|-------|-----|-------|
| `neutral-bg` | `#071A2B` | Ocean 900 — primary dark background |
| `neutral-bg-subtle` | `#0B243C` | Ocean 800 — alternate dark background |
| `neutral-surface` | `#0B243C` | Cards, panels |
| `neutral-surface-elevated` | `#12314E` | Elevated surfaces |
| `neutral-text-primary` | `#FFFFFF` | Primary text |
| `neutral-text-secondary` | `#CFE3EE` | Secondary text |
| `neutral-text-muted` | `#5F8FA9` | Helper text |
| `neutral-border` | `#12314E` | Standard borders |
| `neutral-border-subtle` | `#0B243C` | Subtle dividers |

### 1e. Accent Colors

| Name | Light Mode | Dark Mode | Usage |
|------|-----------|-----------|-------|
| **Ocean** | `#007C91` (Deep Teal) | `#22D3EE` (Bright Cyan) | Ocean/water accent |
| **Futuristic** | `#7C3AED` (Ultraviolet) | `#C4B5FD` (Light Violet) | Tech/innovation accent |
| **Warm** | `#F06A6A` (Coral) | `#F06A6A` (Coral) | Warm accent (same both modes) |

### 1f. Status Colors

Available as Tailwind utility classes: `text-status-error`, `bg-status-success`, `border-status-warning`, etc.

| State | Light Mode | Dark Mode | Source |
|-------|-----------|-----------|--------|
| **Success** | `#1B7A5A` | `#5BC6A6` | Royal Green / Light Mint |
| **Warning** | `#F59E0B` | `#FBBF24` | Amber |
| **Error** | `#CE1126` | `#F06A6A` | Royal Red / Coral |
| **Info** | `#00B7D6` | `#22D3EE` | Reef Cyan / Bright Cyan |

### 1g. Calendar Category Colors

Available as Tailwind utility classes (badge usage):  
`bg-calendar-category-*-bg text-calendar-category-*-text border-calendar-category-*-border`

| Category | Background | Text | Border | Notes |
|----------|------------|------|--------|-------|
| Panel / Talk | `#E9F0FF` | `#002868` | `#BFD3FF` | Royal navy-derived |
| Networking / Mixer | `#EAF7F2` | `#1B7A5A` | `#BFE6D5` | Royal green-derived |
| Workshop / Masterclass | `#FFF4D6` | `#8A5A00` | `#FFE19C` | Gold-derived, dark text for contrast |
| Activity / Social | `#FDE8EA` | `#9B1020` | `#F6C8CF` | Royal red-derived |
| Demo / Experiential | `#E3F9FF` | `#007C91` | `#B7EEF9` | Cyan/ocean-derived |
| Hackathon / Pitch | `#E8EDFA` | `#04153D` | `#C8D5F2` | HTW navy-derived |
| Summit / Conference | `#F1EAFF` | `#5B21B6` | `#DCCAFF` | Futuristic accent-derived |

### 1h. Gradient

**Ocean Sunrise** — retained brand gradient recipe. The current public website also uses quieter navy overlays and a green-to-navy community band; this recipe is not required on every hero:

```
linear-gradient(90deg, #002868 0%, #0052A3 25%, #5B9FD8 50%, #93C5FD 75%, rgba(147, 197, 253, 0.1) 100%)
```

| Stop | Hex | Note |
|------|-----|------|
| 0% | `#002868` | Royal Navy (brand color) |
| 25% | `#0052A3` | Gradient-only (not a standalone brand color) |
| 50% | `#5B9FD8` | Gradient-only |
| 75% | `#93C5FD` | Gradient-only |
| 100% | `rgba(147, 197, 253, 0.1)` | Fade to transparent |

---

## 2. Typography

### Font Families

| Token | Value | Usage |
|-------|-------|-------|
| `font-inter` | Inter, system-ui, sans-serif | All text (loaded via next/font/google in layout.tsx) |

> Body text uses Inter via the `body` font-family declaration in globals.css. `font-inter` class is available for explicit use.

### Public Heading Scale

| Context | Responsive size | Weight / line height |
|---------|-----------------|----------------------|
| Homepage hero | `clamp(70px, 8.6vw, 148px)` before mobile overrides | 600 / .97 |
| Interior hero | `clamp(48px, 6.8vw, 110px)`; mobile `clamp(44px, 9vw, 72px)` | 500 / 1.02 |
| Public section heading | `clamp(32px, 4vw, 62px)` | 500 / 1.08 |
| Homepage section heading | `clamp(38px, 4.1vw, 66px)` | 500 / 1.06 |
| Body | 16px; intros 17–21px | 400 / 1.5–1.7 |

Use sentence case and responsive line breaks that preserve meaning. The shared CSS modules contain the context-specific mobile overrides.

### Retained UI / Component Heading Scale

The following utilities remain useful for forms, tools, and existing components. They do not override the public heading profile.

| Level | Mobile | Desktop | Weight | Classes |
|-------|--------|---------|--------|---------|
| **H1** | 48px (`text-5xl`) | 72px (`text-7xl`) | Bold | `text-5xl md:text-7xl font-bold font-inter` |
| **H2** | 30px (`text-3xl`) | 36px (`text-4xl`) | Bold | `text-3xl md:text-4xl font-bold font-inter mb-12` |
| **H3** | 20px (`text-xl`) | 24px (`text-2xl`) | Bold | `text-xl md:text-2xl font-bold font-inter` |

### Body Text (CSS Utility Classes)

| Class | Size | Color | Usage |
|-------|------|-------|-------|
| `.htw-body` | `text-base md:text-lg` | `text-neutral-text-secondary` | Standard paragraphs |
| `.htw-body-sm` | `text-sm md:text-base` | `text-neutral-text-secondary` | Captions, metadata |
| `.htw-section-intro` | `text-lg md:text-xl` | `text-neutral-text-secondary` | Intro text below H2 |
| `.htw-section-intro-xl` | `text-xl md:text-2xl` | `text-neutral-text-secondary` | Large "pop" intro sections |
| `.htw-blockquote` | `text-lg md:text-xl` | `text-neutral-text-secondary italic` | Testimonials, pull quotes |

### Card Geometry

These are retained component recipes. Card proportions should be consistent within a deck; full-width media bands and the current speaker image-plus-copy treatment follow their shared public components instead.

| Card Type | Ratio | Media/Text Split | Usage |
|-----------|-------|------------------|-------|
| Speaker/profile image cards | `4:5` portrait | Overlay text | Speaker grids, carousels, headshots |
| Non-speaker media cards | `5:4` horizontal | `65%` image / `35%` text when separate bands are used | About cards, Experience tiles, Get Involved cards, featured media |
| Proof/testimonial cards | `5:4` horizontal | `65%` quote / `35%` attribution | Testimonial rails and proof cards |

Implementation rules:
- Use `aspect-[4/5]` only for speaker/profile cards.
- Use `aspect-[5/4]` for the non-speaker card recipes below. Public media bands are not constrained to this card ratio.
- If a `5:4` card has a separate image and copy band, the image must stay between `60%` and `67%` of the card height; copy/action space should stay between `33%` and `40%`.
- Do not let text content determine card height in card decks. Edit copy, clamp text, or reduce text scale before breaking the card ratio.

### Testimonial Card Component

Use `src/components/TestimonialCard.tsx` for reusable testimonial proof cards on the homepage, sponsor pages, reports, and future proof sections.

| Element | Mobile/Base | Desktop | Notes |
|---------|-------------|---------|-------|
| Card | `aspect-[5/4] w-[min(82vw,320px)]` | `aspect-[5/4] w-[400px]` | Locked horizontal proof card |
| Quote row | `65%` | `65%` | Quote is the primary proof content |
| Attribution row | `35%` | `35%` | Avatar/name/title/company/logo live here |
| Quote | `text-sm leading-relaxed` | `text-base leading-relaxed` | Quote is clipped by the fixed row if too long |
| Avatar | `36px` | `40px` | Optional; falls back to initials/name when available |
| Logo box | `84px × 32px` | `104px × 36px` | Optional; strict box prevents layout breakage |
| Logo max | `80px × 24px` | `100px × 28px` | `object-contain`; no overflow |

Component contract:
- `quote` is required.
- `name`, `title`, `company`, `avatarUrl`, `companyLogoUrl`, and `initials` are optional.
- Missing optional fields must not change card dimensions or break the footer.
- Company logo is a secondary proof mark; keep company name as text when present.

### Hero Heading Utility

| Class | Usage |
|-------|-------|
| `.htw-hero-heading` | Retained for pages still using the utility. Refreshed public heroes use the shared public CSS profile; this class does not control every public hero. |

### Line Heights

| Token | Value | Usage |
|-------|-------|-------|
| Tight | 1.25 | Headings |
| Normal | 1.5 | Body text |
| Relaxed | 1.625 | Long-form content |

### Font Weights

| Token | Value |
|-------|-------|
| Normal | 400 |
| Medium | 500 |
| Semibold | 600 |
| Bold | 700 |

---

## 3. Buttons

**Public marketing actions:** sentence case, 14px/600 text, 48px minimum height, 14px × 20px padding, 4px radius, and 1px borders. Primary is gold with navy text; supporting actions use a thin outline. Preserve the shared public components' hover and focus states.

**Retained Button component recipes:** the tables below describe `src/components/ui/button.tsx` and its existing consumers. The uppercase and 2px-border recipe is not a universal rule for public marketing actions.

### 3a. Variants

| Variant | Background | Text | Border | Usage |
|---------|-----------|------|--------|-------|
| **Primary** | `#FFB81C` (Royal Gold) | `#04153D` (HTW Navy) | `#FFB81C` (color-matched) | Main CTA. Max 1 per section. |
| **Primary Hover** | `#FFC843` | `#04153D` | `#FFC843` | |
| **Secondary Light** | transparent | `#FFFFFF` | `#FFFFFF` | Supporting action on dark backgrounds |
| **Secondary Light Hover** | `#FFFFFF` | `#04153D` | `#FFFFFF` | |
| **Secondary Dark** | `#04153D` | `#FFFFFF` | `#04153D` | Supporting action on light backgrounds |
| **Secondary Dark Hover** | `#04153D/90` | `#FFFFFF` | `#04153D/90` | |
| **Tertiary** | transparent | `#04153D` / `#FFFFFF` | `#04153D` / `#FFFFFF` | Ghost. Cancel, back, low priority. |
| **Tertiary Hover** | `#04153D` | `#FFFFFF` | `#04153D` | Fills on hover. |
| **Outline** | transparent | inherited | `neutral-border` | Subtle supporting action. |
| **Ghost** | transparent | inherited | transparent | Minimal action, no border. |
| **Destructive** | `#CE1126` | `#FFFFFF` | `#CE1126` (color-matched) | Dangerous actions. |

> **Existing Button component box-model rule**: Every variant uses `border-2` (2px). Solid-fill variants (primary, secondary-dark, destructive) use color-matched borders. This ensures all buttons of the same `size` have identical outer dimensions regardless of variant.

### 3b. Sizes

| Size | Padding | Min Height | Font Size | Usage |
|------|---------|------------|-----------|-------|
| **sm** | `px-5 py-2` (20px / 8px) | 36px | 16px (`text-base`) | Header nav, compact spaces |
| **md** | `px-6 py-3` (24px / 12px) | 44px | 16px (`text-base`) | Default. Most buttons. |
| **lg** | `px-8 py-4` (32px / 16px) | 52px | 18px (`text-lg`) | Hero CTAs, prominent actions |

### 3c. Shared Properties

| Property | Value |
|----------|-------|
| Font weight | 600 (semibold) |
| Text transform | UPPERCASE |
| Border radius | 4px |
| Border width | 2px on ALL variants (solid-fill buttons use color-matched or transparent borders for uniform box-model sizing) |
| Focus ring | `#00D4FF` (Neon Cyan), 2px offset |

---

## 4. Spacing

**Public profile:** full-width bands, horizontal gutter `clamp(24px, 4.5vw, 88px)`, interior content maximum 1760px, and typical section padding 72px desktop / 48px mobile. Homepage bands have context-specific spacing. Keep body copy measures comfortable inside those wider compositions.

The utilities below remain available for contained reading areas and existing UI components; `max-w-6xl` is not a limit on every public band.

### Section Padding (Vertical)

| Context | Mobile | Desktop | Classes |
|---------|--------|---------|---------|
| Standard section | 64px | 80px | `py-16 md:py-20` |
| Hero section | 85vh min | 100vh min | `min-h-[85vh] md:min-h-screen` |
| Compact hero | 96px | 96px | `py-24` |

### Container

| Property | Value | Classes |
|----------|-------|---------|
| Max width | 1152px | `max-w-6xl mx-auto` |
| Horizontal padding (mobile) | 16px | `px-4` |
| Horizontal padding (desktop) | 24px | `sm:px-6` |

### Grid Gaps

| Token | Value | Classes |
|-------|-------|---------|
| Default gap | 32px | `gap-8` |
| Large gap | 48px | `gap-12` |

---

## 5. Border Radius

Public marketing cards and actions use the 4px radius in `publicSite.layout.radius`. The table below retains existing UI component defaults; it does not require public cards to use 8px corners.

| Token | Value | Usage |
|-------|-------|-------|
| None | 0px | — |
| Button | 4px | All buttons |
| Card | 8px | Cards, modals, panels |
| Large | 12px | Large containers, hero cards |

### Shape System Governance (Rounded vs Squared)

- **Source of truth**: radius decisions are controlled by tokens in `docs/design-tokens.json` (`publicSite.layout.radius` for public marketing, and `radius.none`, `radius.button`, `radius.card`, `radius.large` for existing UI recipes).
- **Implementation order**: update tokens first, then shared components (`Button`, card primitives, `SpeakerProfileCard`), then any page-level one-off classes.
- **Default policy**: avoid hardcoding `rounded-*` values in page code when a shared tokenized component exists.
- **If we switch to squared corners**:
  1. Set relevant radius tokens to `0px` in `docs/design-tokens.json`
  2. Ensure shared components consume tokenized radius values (or `rounded-none`)
  3. Sweep page-level overrides and remove legacy `rounded-lg`/`rounded-xl` classes
  4. Re-validate visual regressions in key surfaces (buttons, cards, modals, hero blocks)

---

## 6. Shadows

The public marketing profile primarily separates surfaces with fine borders. These shadow recipes remain available for elevated UI, popovers, and older components.

| Token | Value | Usage |
|-------|-------|-------|
| **sm** | `0 1px 2px 0 rgba(0,0,0,0.05)` | Subtle depth |
| **base** | `0 1px 3px rgba(0,0,0,0.1), 0 1px 2px rgba(0,0,0,0.06)` | Default cards |
| **md** | `0 4px 6px -1px rgba(0,0,0,0.1), 0 2px 4px -1px rgba(0,0,0,0.06)` | Elevated cards |
| **lg** | `0 10px 15px -3px rgba(0,0,0,0.1), 0 4px 6px -2px rgba(0,0,0,0.05)` | Modals, dropdowns |
| **xl** | `0 20px 25px -5px rgba(0,0,0,0.1), 0 10px 10px -5px rgba(0,0,0,0.04)` | Hero cards, featured |

---

## 7. Transitions & Animation

### Durations

| Token | Value | Usage |
|-------|-------|-------|
| Fast | 150ms | Micro-interactions (hover color change) |
| Base | 300ms | Standard transitions (card lift, button) |
| Slow | 500ms | Complex animations (page transitions) |

### Easing Curves

| Token | Value |
|-------|-------|
| Default | `cubic-bezier(0.4, 0, 0.2, 1)` |
| Ease In | `cubic-bezier(0.4, 0, 1, 1)` |
| Ease Out | `cubic-bezier(0, 0, 0.2, 1)` |
| Ease In-Out | `cubic-bezier(0.4, 0, 0.2, 1)` |

### Retained Scroll Animation Recipes

These are optional component recipes, not a requirement to animate public sections. Respect reduced-motion preferences and keep essential content available without animation.

| Element | Animation | Duration | Delay |
|---------|-----------|----------|-------|
| Section headings | Fade up (20px) | 600ms | 0ms |
| Card grids | Staggered fade-in | 600ms | 50ms per card (up to 12) |
| Text blocks | Fade in | 600ms | 0ms |
| Images | Fade + scale (0.95 → 1) | 600ms | 0ms |

### Retained Card Hover Recipes

Current public controls generally use restrained color and border feedback; apply these older lift/scale recipes only where the component calls for them.

| Type | Effect | Classes |
|------|--------|---------|
| **Interactive** (clickable) | Lift + border + scale | `hover:-translate-y-2 hover:border-htw-green hover:scale-[1.02] transition-all duration-300` |
| **Featured** (non-linked) | Lift + border | `hover:-translate-y-2 hover:border-htw-green transition-all duration-300` |
| **Informational** (low-priority) | Border change | `hover:border-neutral-border transition-colors duration-200` |
| **Image** (photos, logos) | Scale + brightness | `hover:scale-105 hover:brightness-110 transition-all duration-300` |

---

## 8. Breakpoints

| Token | Width | Tailwind Prefix |
|-------|-------|-----------------|
| sm | 640px | `sm:` |
| md | 768px | `md:` |
| lg | 1024px | `lg:` |
| xl | 1280px | `xl:` |
| 2xl | 1536px | `2xl:` |

---

## 9. Z-Index Scale

| Token | Value | Usage |
|-------|-------|-------|
| Base | 0 | Default stacking |
| Dropdown | 10 | Click-outside backdrops (below sticky) |
| Sticky | 20 | Fixed header, sticky filter bars, FAB buttons, ShareBar |
| Dropdown-on-sticky | 30 | Dropdowns opening from sticky elements (Header menu, filter dropdowns) |
| Backdrop | 40 | Visual overlays (sidebar backdrop) |
| Modal | 50 | Dialogs, sheets, popovers, preview banners |
| Toast | 100 | Toast notifications |

---

## 10. Grid Layouts

| Layout | Classes |
|--------|---------|
| Two column | `grid lg:grid-cols-2 gap-12` |
| Three column | `grid md:grid-cols-2 lg:grid-cols-3 gap-8` |

---

## 11. Components

### Custom HTW Components

| Component | File | Usage |
|-----------|------|-------|
| **Button** | `src/components/ui/button.tsx` | HTW-branded button with `asChild` support. Variants: primary, secondary, secondary-dark, tertiary, link, outline, ghost, destructive. Sizes: sm, md, lg, icon. Use `asChild` with `<Link>` or `<a>` for navigation. |
| **SpeakerProfileCard** | `src/components/SpeakerProfileCard.tsx` | Reusable speaker card system. Variants: `overlay` (portrait image with text overlay + optional social hover icons), `horizontal` (left image + right metadata + optional social row). |
| **ImageWithFallback** | `src/components/ImageWithFallback.tsx` | All images (.avif + .png fallback, lazy loading) |
| **Header** | `src/components/Header.tsx` | Shared site header |
| **Footer** | `src/components/Footer.tsx` | Shared site footer |

### Forms (React Hook Form + shadcn)

**Stack:** [React Hook Form](https://react-hook-form.com/) for client UX, [shadcn/ui Form primitives](https://ui.shadcn.com/docs/components/form) (`FormProvider` as `Form`, `FormField`, `FormItem`, `FormLabel`, `FormControl`, `FormMessage`) wired to RHF, **typed validation helpers** in `src/lib/validation/`, and **route handlers or Server Actions** that validate again before writing to the DB. Prefer **single-column** field stacks on marketing flows unless a spec calls for multi-column layouts.

| Area | Location | Role |
|------|----------|------|
| **Form UI + RHF wiring** | `src/components/ui/form.tsx` | shadcn-style `Form` / `FormField` / `FormMessage` (HTW error + label styling). |
| **Marketing field building blocks** | `src/components/forms/htw-marketing-fields.tsx` | `HtwMarketingTextField`, `HtwMarketingTextareaField`, `HtwMarketingWebsiteField`, `HtwSubmitButton` — compose shadcn `Input` + tokens. |
| **Layout shell** | `src/components/forms/form-shell.tsx` | `<form noValidate>` + section spacing. |
| **Helper copy** | `src/components/forms/field-hint.tsx` | Neutral secondary hint text. |
| **Shared field styles** | `src/lib/forms/htw-marketing-field-styles.ts` | HTW borders/focus rings (error vs OK). |
| **Validation** | `src/lib/validation/*.ts` | Plain functions (e.g. `validate…FormClient`, `validate…Server`) aligned with client + API routes. |

**Rules**

1. Centralize **min/max length, required, URL shape, enums** in `src/lib/validation/`; keep `maxLength` on inputs aligned.
2. **Client:** `useForm` + submit handler that runs validators and `setError`; show errors via `FormMessage`.
3. **Server:** validate POST bodies in route handlers; return `400` with a clear message when invalid.
4. **Files / cross-field rules** (e.g. “logo required if no existing asset”) may use manual `setError` after base field validation.

**Reference implementation:** sponsor onboarding — `src/lib/validation/sponsor-onboard.ts` (`validateSponsorOnboardFormClient`, `validateSponsorOnboardFullServer`, `validateSponsorOnboardPartialServer`) and `src/app/(landing)/sponsor/onboard/page.tsx`.

### shadcn/ui Components (Radix-based)

Located in `src/components/ui/`:

| Component | File | Current Usage |
|-----------|------|---------------|
| Accordion | `accordion.tsx` | FAQ page |
| Badge | `badge.tsx` | Calendar filters |
| Card | `card.tsx` | Available |
| Carousel | `carousel.tsx` | Available |
| Command | `command.tsx` | Available |
| Dialog | `dialog.tsx` | Available |
| Form | `form.tsx` | RHF + shadcn forms (see Forms above) |
| Input | `input.tsx` | Available |
| Label | `label.tsx` | Available |
| Popover | `popover.tsx` | Available |
| Sheet | `sheet.tsx` | Header mobile menu |
| Sonner | `sonner.tsx` | Brand Kit toasts |
| Tabs | `tabs.tsx` | Calendar filters |

### shadcn Theme Variables (How HTW colors flow into shadcn primitives)

shadcn/ui primitives read CSS custom properties from `:root` (light) and `.dark` (dark) — `--primary`, `--border`, `--ring`, `--destructive`, etc. In `src/styles/globals.css` these are **bound directly to HTW design tokens** so primitives render HTW-correct without per-page overrides.

| shadcn variable | Bound to | Resolves to (light) | Used by |
|---|---|---|---|
| `--background` | `--neutral-bg` | `#FFFFFF` | Body, full-page surfaces |
| `--foreground` | `--neutral-text-primary` | `#0B1220` | Default text |
| `--card`, `--popover` | `--neutral-surface` / `-elevated` | `#FFFFFF` / `#F8FAFC` | `Card`, `Dialog`, `Popover` bg |
| `--primary` | `--brand-primary` | `#04153D` (HTW Navy) | Checkbox-checked fill, switch-on, calendar selected day, default `Button` bg |
| `--secondary`, `--muted`, `--accent` | `--neutral-border-subtle` | `#F1F5F9` | Subtle fills, hover surfaces |
| `--muted-foreground` | `--neutral-text-muted` | `#64748B` | Helper / placeholder text |
| `--destructive` | `--status-error` | `#CE1126` (Royal Red) | `aria-invalid` ring, destructive Button |
| `--border`, `--input` | `--neutral-border` | `#CBD5E1` | All shadcn primitive borders |
| `--ring` | `--focus-ring` | `#00D4FF` (HTW Cyan) | Focus rings on inputs, buttons, links |

**Important: `--primary` is Navy, not Gold.** shadcn uses `--primary` for *selection-fill* semantics (checkbox checked, switch on, etc.) where high contrast on white matters. The HTW Gold CTA aesthetic lives in our **custom `Button` component** (`src/components/ui/button.tsx`) via `variant="primary"`, which uses Tailwind classes (`bg-htw-gold`) directly — independent of `--primary`. Don't try to make shadcn `--primary` Gold; you'll break checkbox/switch contrast.

**Rules**

1. **Don't duplicate token stacks on individual pages.** If you find yourself writing `border-neutral-border focus:ring-htw-cyan` to make a shadcn primitive look HTW-branded, the work is already done — drop the overrides. The marketing flows currently keep them via `htw-marketing-fields.tsx` for layout reasons (`h-11`, `text-sm`, `rounded-lg`); those layout-only tweaks are fine, but **color/border/focus overrides are no longer necessary**.
2. **HTW tokens are the source of truth.** If you need to change a brand color, update the relevant `--brand-*` / `--neutral-*` / `--status-*` token in the `@theme inline` block. shadcn vars will follow automatically via the `var()` references in `:root`.
3. **For new shadcn primitives**, add them with `npx shadcn add` (or copy from upstream) and they'll inherit HTW styling on first render. No tweaks required.

---

## 12. Page Structure

Every page follows this order:

1. `<Head>` — SEO (title, description, OG tags)
2. `<Header />` — shared site header
3. Hero section (if applicable)
4. Content sections using the shared public profile or the appropriate contained reading/UI recipe
5. `<Footer />` — shared site footer

**Pacing:** Use the approved full-width public bands with shared alignment and navy continuity. Put About before featured media on the homepage. Keep the shared footer navy. Light surfaces remain appropriate for reading and form tasks — see the public-site profile and §1b.

---

## 13. How to Add or Change a Color

1. **Define in `docs/design-tokens.json`** — add the color with `value`, `type`, and `description`. This is the source of truth.
2. **Add to `src/styles/globals.css`** — add a `--color-<name>: #hex;` line inside the `@theme inline` block. This creates Tailwind utility classes (`bg-<name>`, `text-<name>`, `border-<name>`, etc.).
3. **Update this file** (`DESIGN_SYSTEM.md`) — document the new color in the relevant section above.
4. **Check contrast** — verify WCAG AA compliance (4.5:1 for text, 3:1 for UI) using [WebAIM Contrast Checker](https://webaim.org/resources/contrastchecker/).
5. **Test** — run `npm run build` to verify, then check visually in the browser.

To remove a color, search the codebase first (`rg "color-name" src/`), then reverse steps 1-3.

---

## Publishing a Brand Kit Edition

1. Preserve the prior guide, token JSON, visual reference, and available assets under an immutable version path.
2. Record the version, date, what changed, why, and what remained recognizable in the public history. State explicitly when an older publication date or asset is unavailable.
3. Update the current guide, token profile, examples, and public kit together; ensure the static reference fallback matches the token JSON.
4. Publish permanent edition links and downloadable files. Older editions are references, clearly marked as superseded.
5. Keep annual campaign files separate and identify which core edition they use. Never carry forward dates, offers, or totals without checking them.

## Quick Reference

| Task | Reference |
|------|-----------|
| All token values | `docs/design-tokens.json` |
| Visual preview | `/public/htw-design-system.html` |
| Feature specs | `docs/spec-template.md` |
