| title | Component Usage Guide | |||
|---|---|---|---|---|
| filename | /docs/component-usage-guide.md | |||
| created | 2026-03-12 | |||
| modified | 2026-03-12 | |||
| version | 1.0.0 | |||
| related_docs |
|
Created: March 12, 2026 Last Updated: March 12, 2026 Status: Active
This guide provides practical examples and best practices for using Nova News components. It covers common patterns, prop configurations, and integration examples for all major component categories.
Target Audience: Developers working with the Nova News codebase who need quick reference for component usage.
Purpose: Reusable hero section with auto-stagger animations and flexible content slots.
Props:
interface HeroLayoutProps {
title: string;
subtitle?: string;
description?: string;
ctaButtons?: React.ReactNode;
media?: React.ReactNode;
autoStagger?: boolean;
darkMode?: boolean;
className?: string;
}
```text
**Basic Usage:**
```tsx
import { HeroLayout } from '@/components/sections/HeroLayout';
<HeroLayout
title="Welcome to Nova News"
subtitle="A retro 80s neon CLI aesthetic"
description="Dive into the story behind the dancefloor"
autoStagger={true}
darkMode={true}
/>
```text
**With CTA Buttons:**
```tsx
<HeroLayout
title="Read the draft"
subtitle="This one time at band camp"
ctaButtons={
<>
<a href="/ebook" className="btn btn--primary animate-neon-pulse">
Start reading
</a>
<a href="/about" className="btn btn--outline">
About the author
</a>
</>
}
autoStagger={true}
/>
```text
## With Floating Media
```tsx
<HeroLayout
title="Portfolio"
subtitle="Neon makeup artistry"
media={
<img
src="/hero-image.jpg"
alt="Portfolio hero"
className="hero__media animate-float"
/>
}
autoStagger={true}
/>
```text
**Key Features:**
- ✅ Auto-stagger animations (100ms delays)
- ✅ Terminal boot sequence
- ✅ Neon pulse CTAs
- ✅ Holographic title shimmer (optional)
- ✅ Floating media animation
- ✅ Fully accessible (reduced motion support)
---
### VideoArchiveLayout
**Purpose:** Shared layout for video archive pages (VideosPage,
VideoCategoryPage, VideoTagPage).
**Props:**
```typescript
interface VideoArchiveLayoutProps {
title: string;
description?: string;
videos: Video[];
categories: string[];
tags: string[];
showFilters?: boolean;
}
```text
**Usage:**
```tsx
import { VideoArchiveLayout } from '@/components/layout/VideoArchiveLayout';
import { videoData } from '@/data/mock/videos/video-data';
<VideoArchiveLayout
title="All videos"
description="Watch makeup tutorials and behind-the-scenes content"
videos={videoData.videos}
categories={videoData.categories}
tags={videoData.tags}
showFilters={true}
/>
```text
**Features:**
- ✅ Archive filters (category/tag/search)
- ✅ Sort controls (date/views/title)
- ✅ Responsive grid (1-4 columns)
- ✅ VideoCard integration
- ✅ Empty state handling
---
## Common Components
### Header
**Purpose:** Global site navigation with mobile menu, mega menus, and theme
switcher.
**Usage:**
```tsx
import { Header } from '@/components/common/Header';
<Header />
```text
**No props required** - Header uses internal state and context.
**Features:**
- ✅ Desktop navigation with hover mega menus
- ✅ Mobile hamburger menu (slide-in drawer)
- ✅ Theme switcher toggle
- ✅ Active link highlighting
- ✅ Scroll-aware sticky positioning
- ✅ Keyboard accessible
**Customization:**
To modify navigation links, edit `/data/mock/ui/navigation.ts`:
```typescript
export const navigationData = {
mainNav: [
{ label: 'Home', href: '/' },
{ label: 'Portfolio', href: '/portfolio', hasMegaMenu: true },
{ label: 'Blog', href: '/blog', hasMegaMenu: true },
{ label: 'Videos', href: '/videos' },
{ label: 'Podcasts', href: '/podcasts' },
{ label: 'About', href: '/about' },
{ label: 'Contact', href: '/contact' },
],
};
```text
---
### Footer
**Purpose:** Site footer with navigation, social links, and copyright.
**Usage:**
```tsx
import { Footer } from '@/components/common/Footer';
<Footer />
```text
**Features:**
- ✅ Multi-column link layout
- ✅ Social media icons (Instagram, Twitter, etc.)
- ✅ Newsletter signup link
- ✅ Copyright with dynamic year
- ✅ BEM CSS (no inline styles)
---
### MobileMenu
**Purpose:** Slide-in drawer navigation for mobile devices.
**Props:**
```typescript
interface MobileMenuProps {
isOpen: boolean;
onClose: () => void;
}
```text
**Usage:**
```tsx
import { MobileMenu } from '@/components/common/MobileMenu';
const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
<MobileMenu
isOpen={mobileMenuOpen}
onClose={() => setMobileMenuOpen(false)}
/>
```text
**Features:**
- ✅ Slide-in from right animation
- ✅ Backdrop overlay
- ✅ Close on backdrop click
- ✅ Close on Escape key
- ✅ Theme switcher integrated
- ✅ Focus trap for accessibility
---
### ThemeSwitcher
**Purpose:** Toggle between light and dark themes.
**Usage:**
```tsx
import { ThemeSwitcher } from '@/components/common/ThemeSwitcher';
<ThemeSwitcher />
```text
**No props required** - Uses localStorage to persist theme preference.
**How It Works:**
1. Reads saved theme from `localStorage.getItem('theme')`
1. Applies `data-theme` attribute to `<html>` element
1. CSS responds to `[data-theme="dark"]` and `[data-theme="light"]`
1. Updates icon (☀️ for dark mode, 🌙 for light mode)
**Customization:**
To change default theme, edit `ThemeSwitcher.tsx`:
```tsx
const [theme, setTheme] = useState(() => {
if (typeof window !== 'undefined') {
return localStorage.getItem('theme') || 'dark'; // Change default here
}
return 'dark';
});
```text
---
## UI Components
### Breadcrumbs
**Purpose:** Hierarchical navigation trail.
**Props:**
```typescript
interface BreadcrumbItem {
label: string;
href?: string; // Omit for current page
}
interface BreadcrumbsProps {
items: BreadcrumbItem[];
}
```text
**Usage:**
```tsx
import { Breadcrumbs } from '@/components/ui/Breadcrumbs';
<Breadcrumbs items={[
{ label: 'Home', href: '/' },
{ label: 'Portfolio', href: '/portfolio' },
{ label: 'Festival Makeup' }, // No href = current page
]} />
```text
**Features:**
- ✅ Semantic `<nav>` with `aria-label="Breadcrumb"`
- ✅ Schema.org BreadcrumbList JSON-LD
- ✅ Current page marked with `aria-current="page"`
- ✅ Chevron separators (›)
- ✅ BEM CSS styling
**Best Practices:**
- Always include Home as first item
- Last item should NOT have `href` (represents current page)
- Use on all sub-pages (not on homepage)
---
### Button
**Purpose:** Reusable button component with variant styles.
**Props:**
```typescript
interface ButtonProps {
children: React.ReactNode;
variant?: 'primary' | 'secondary' | 'outline' | 'ghost';
size?: 'sm' | 'md' | 'lg';
disabled?: boolean;
onClick?: () => void;
href?: string;
className?: string;
}
```text
**Usage:**
```tsx
import { Button } from '@/components/ui/Button';
// Primary CTA
<Button variant="primary" size="lg">
Get started
</Button>
// Outline button
<Button variant="outline">
Learn more
</Button>
// Link button
<Button href="/contact" variant="secondary">
Contact us
</Button>
// Disabled button
<Button disabled>
Coming soon
</Button>
```text
**Variants:**
- `primary` - Cyberpunk gradient background, white text, neon pulse animation
- `secondary` - Neon pink background, atomic black text
- `outline` - Transparent background, neon pink border
- `ghost` - Transparent background, no border, hover effect
**Sizes:**
- `sm` - 32px height, 16px padding
- `md` - 40px height (default), 24px padding
- `lg` - 48px height, 32px padding
---
### PortfolioCard
**Purpose:** Display portfolio entry in grid/list layouts.
**Props:**
```typescript
interface PortfolioCardProps {
title: string;
image: string;
category: string;
location?: string;
featured?: boolean;
url: string;
}
```text
**Usage:**
```tsx
import { PortfolioCard } from '@/components/ui/PortfolioCard';
<PortfolioCard
title="Origin Festival 2025"
image="/images/portfolio/origin-2025.jpg"
category="Festival Makeup"
location="Cape Town, South Africa"
featured={true}
url="/portfolio/origin-festival-2025"
/>
```text
**Features:**
- ✅ Hover scale animation
- ✅ Neon glow on featured entries
- ✅ Category badge
- ✅ Location icon
- ✅ Lazy-loaded images
- ✅ Accessible alt text
**Grid Layout:**
```tsx
<div className="portfolio-grid portfolio-grid--3-col">
{portfolioData.entries.map((entry) => (
<PortfolioCard key={entry.id} {...entry} />
))}
</div>
```text
---
### VideoCard
**Purpose:** Display video entry with thumbnail and metadata.
**Props:**
```typescript
interface VideoCardProps {
title: string;
thumbnail: string;
duration: string;
views: number;
category: string;
url: string;
}
```text
**Usage:**
```tsx
import { VideoCard } from '@/components/ui/VideoCard';
<VideoCard
title="UV makeup tutorial"
thumbnail="/images/videos/uv-tutorial.jpg"
duration="5:32"
views={12500}
category="Tutorial"
url="/videos/uv-makeup-tutorial"
/>
```text
**Features:**
- ✅ Play button overlay
- ✅ Duration badge
- ✅ View count
- ✅ Category badge
- ✅ Hover effect (lift + glow)
---
### BlogCard
**Purpose:** Display blog post in archive grids.
**Props:**
```typescript
interface BlogCardProps {
title: string;
excerpt: string;
image: string;
date: string;
readTime: string;
category: string;
tags: string[];
url: string;
}
```text
**Usage:**
```tsx
import { BlogCard } from '@/components/ui/BlogCard';
<BlogCard
title="The dancefloor gave me everything"
excerpt="A journey through Berlin's techno scene..."
image="/images/blog/dancefloor.jpg"
date="2026-02-15"
readTime="5 min read"
category="Personal"
tags={['Berlin', 'Techno', 'Community']}
url="/blog/the-dancefloor-gave-me-everything"
/>
```text
**Features:**
- ✅ Excerpt truncation (3 lines max)
- ✅ Category badge
- ✅ Tag pills
- ✅ Read time indicator
- ✅ Formatted date
---
### ArchiveFilters
**Purpose:** Filter controls for archive pages (blog, videos, portfolio).
**Props:**
```typescript
interface ArchiveFiltersProps {
categories: string[];
tags?: string[];
activeCategory: string;
activeTags: string[];
onCategoryChange: (category: string) => void;
onTagToggle: (tag: string) => void;
onSearchChange?: (query: string) => void;
sortOptions?: { label: string; value: string }[];
sortBy?: string;
onSortChange?: (sort: string) => void;
}
```text
**Usage:**
```tsx
import { ArchiveFilters } from '@/components/ui/ArchiveFilters';
import { useState } from 'react';
const [activeCategory, setActiveCategory] = useState('all');
const [activeTags, setActiveTags] = useState<string[]>([]);
const [sortBy, setSortBy] = useState('date');
<ArchiveFilters
categories={['All', 'Festival', 'Editorial', 'UV']}
tags={['Berlin', 'Cape Town', 'Koh Phangan']}
activeCategory={activeCategory}
activeTags={activeTags}
onCategoryChange={setActiveCategory}
onTagToggle={(tag) => {
setActiveTags(prev =>
prev.includes(tag)
? prev.filter(t => t !== tag)
: [...prev, tag]
);
}}
sortOptions={[
{ label: 'Newest first', value: 'date' },
{ label: 'Most viewed', value: 'views' },
{ label: 'Title A-Z', value: 'title' },
]}
sortBy={sortBy}
onSortChange={setSortBy}
/>
```text
**Features:**
- ✅ Category buttons (exclusive selection)
- ✅ Tag pills (multi-select)
- ✅ Search input (optional)
- ✅ Sort dropdown (optional)
- ✅ Active state styling
- ✅ Responsive layout (stacks on mobile)
---
### Lightbox
**Purpose:** Full-screen image viewer with gallery navigation.
**Props:**
```typescript
interface LightboxProps {
images: Array<{ url: string; alt: string }>;
currentIndex: number;
onClose: () => void;
onNext?: () => void;
onPrev?: () => void;
}
```text
**Usage:**
```tsx
import { Lightbox } from '@/components/ui/Lightbox';
import { useState } from 'react';
const [lightboxOpen, setLightboxOpen] = useState(false);
const [currentImage, setCurrentImage] = useState(0);
const images = [
{ url: '/image1.jpg', alt: 'Festival makeup 1' },
{ url: '/image2.jpg', alt: 'Festival makeup 2' },
{ url: '/image3.jpg', alt: 'Festival makeup 3' },
];
// Open lightbox on thumbnail click
<img
src={images[0].url}
onClick={() => {
setCurrentImage(0);
setLightboxOpen(true);
}}
/>
{lightboxOpen && (
<Lightbox
images={images}
currentIndex={currentImage}
onClose={() => setLightboxOpen(false)}
onNext={() => setCurrentImage(prev => (prev + 1) % images.length)}
onPrev={() => setCurrentImage(prev => (prev - 1 + images.length) %
images.length)}
/>
)}
```text
**Features:**
- ✅ Fullscreen overlay
- ✅ Prev/Next navigation
- ✅ Close on Escape key
- ✅ Close on backdrop click
- ✅ Keyboard arrow key navigation
- ✅ Image counter (1 of 10)
- ✅ Zoom support
---
## Page Components
### HomePage
**Purpose:** Landing page with hero, featured content, and blog preview.
**Usage:**
```tsx
import { HomePage } from '@/components/pages/HomePage';
<HomePage />
```text
**Features:**
- ✅ Terminal boot animation
- ✅ Auto-stagger hero elements
- ✅ Featured portfolio grid (3 entries)
- ✅ Featured blog posts (3 posts)
- ✅ Neon pulse CTAs
- ✅ Holographic title shimmer
**Customization:**
Edit `/data/mock/pages/home-page.ts`:
```typescript
export const homePageContent = {
hero: {
title: 'Welcome to Nova News',
subtitle: 'A retro 80s neon CLI aesthetic',
description: 'Dive into the story behind the dancefloor',
},
featuredPortfolio: {
title: 'Featured portfolio',
entries: [...], // Portfolio IDs
},
featuredBlog: {
title: 'Latest insights',
posts: [...], // Blog post IDs
},
};
```text
---
### PortfolioPage
**Purpose:** Portfolio archive with filters and grid.
**Usage:**
```tsx
import { PortfolioPage } from '@/components/pages/portfolio/PortfolioPage';
<PortfolioPage />
```text
**Features:**
- ✅ Category filters (Festival, Editorial, UV, etc.)
- ✅ Tag filters (Location, event name)
- ✅ Sort controls (date, views, title)
- ✅ Responsive grid (1-4 columns)
- ✅ Lightbox integration
- ✅ Page header animations
---
### BlogPage
**Purpose:** Blog archive with search, filters, and pagination.
**Usage:**
```tsx
import { BlogPage } from '@/components/pages/blog/BlogPage';
<BlogPage />
```text
**Features:**
- ✅ Full-text search
- ✅ Category filters
- ✅ Tag filters
- ✅ Sort controls
- ✅ Pagination (10 posts per page)
- ✅ Featured posts section
- ✅ Page header animations
---
## Section Components
### CTASection
**Purpose:** Call-to-action banner with gradient background.
**Props:**
```typescript
interface CTASectionProps {
title: string;
description: string;
primaryButton: {
text: string;
href: string;
};
secondaryButton?: {
text: string;
href: string;
};
}
```text
**Usage:**
```tsx
import { CTASection } from '@/components/sections/CTASection';
<CTASection
title="Ready to dive in?"
description="Join the waitlist for exclusive updates"
primaryButton={{
text: 'Join waitlist',
href: '/waitlist',
}}
secondaryButton={{
text: 'Learn more',
href: '/about',
}}
/>
```text
**Features:**
- ✅ Cyberpunk gradient background
- ✅ Neon pulse buttons
- ✅ Centered layout
- ✅ Responsive text sizing
---
## Form Components
### ContactForm
**Purpose:** Typeform embed for contact inquiries.
**Usage:**
```tsx
import { ContactForm } from '@/components/ui/ContactForm';
<ContactForm />
```text
**No props required** - Typeform URL configured internally.
**Features:**
- ✅ Responsive iframe embed
- ✅ Full-screen modal option
- ✅ Loading state
- ✅ BEM CSS styling
---
### NewsletterSignup
**Purpose:** Email capture form (currently mock).
**Props:**
```typescript
interface NewsletterSignupProps {
inline?: boolean;
}
```text
**Usage:**
```tsx
import { NewsletterSignup } from '@/components/ui/NewsletterSignup';
// Inline form (e.g., in footer)
<NewsletterSignup inline={true} />
// Full-width form (e.g., dedicated page)
<NewsletterSignup />
```text
**Features:**
- ✅ Email validation
- ✅ Success/error states
- ✅ GDPR compliance notice
- ✅ BEM CSS styling
---
## Animation Components
### ScrollDownArrow
**Purpose:** Animated scroll indicator.
**Usage:**
```tsx
import { ScrollDownArrow } from '@/components/ui/ScrollDownArrow';
<ScrollDownArrow targetId="content" />
```text
**Features:**
- ✅ Bounce animation
- ✅ Smooth scroll to target
- ✅ Auto-hide on scroll
- ✅ Neon glow effect
---
### ScrollToTop
**Purpose:** Floating button to scroll to page top.
**Usage:**
```tsx
import { ScrollToTop } from '@/components/ui/ScrollToTop';
<ScrollToTop />
```text
**No props required** - Auto-shows after 300px scroll.
**Features:**
- ✅ Fade-in/out on scroll
- ✅ Fixed bottom-right position
- ✅ Smooth scroll to top
- ✅ Neon pink background
---
## Best Practices
### 1. Always Import from Barrel Exports
```tsx
// ✅ GOOD - Barrel export
import { Breadcrumbs, Button, PortfolioCard } from '@/components/ui';
// ❌ BAD - Direct file imports
import { Breadcrumbs } from '@/components/ui/Breadcrumbs';
import { Button } from '@/components/ui/Button';
```text
### 2. Use Mock Data for Content
```tsx
// ✅ GOOD - Import from mock data
import { portfolioData } from '@/data/mock/portfolio/portfolio-data';
<PortfolioCard {...portfolioData.entries[0]} />
// ❌ BAD - Hardcoded data
<PortfolioCard
title="Hardcoded title"
image="/hardcoded.jpg"
/>
```text
### 3. Set SEO Meta Tags on Pages
```tsx
// ✅ GOOD - Set SEO on page load
import { setSEO } from '@/utils/seo';
import { pageSEO } from '@/data/mock/seo';
useEffect(() => {
setSEO(pageSEO.portfolio);
}, []);
// ❌ BAD - Forget SEO
// Page loads with default meta tags
```text
### 4. Always Add Breadcrumbs to Sub-Pages
```tsx
// ✅ GOOD - Breadcrumbs on every sub-page
<Breadcrumbs items={[
{ label: 'Home', href: '/' },
{ label: 'Portfolio' },
]} />
// ❌ BAD - No breadcrumbs
// Users can't navigate back
```text
### 5. Use BEM Classes, Not Tailwind
```tsx
// ✅ GOOD - BEM classes
<div className="portfolio-card portfolio-card--featured">
<h3 className="portfolio-card__title">Title</h3>
</div>
// ❌ BAD - Tailwind utilities
<div className="bg-black p-4 rounded-lg">
<h3 className="text-xl font-bold">Title</h3>
</div>
```text
### 6. Wrap Interactive Components in SafetyWrapper
```tsx
// ✅ GOOD - Error boundary wrapper
import { SafetyWrapper } from '@/components/common/SafetyWrapper';
<SafetyWrapper fallback={<div>Failed to load form</div>}>
<ContactForm />
</SafetyWrapper>
// ❌ BAD - No error handling
<ContactForm />
// If ContactForm crashes, entire page breaks
```text
### 7. Test with Reduced Motion
```tsx
// ✅ GOOD - All animations respect prefers-reduced-motion
@media (prefers-reduced-motion: reduce) {
.animate-float {
animation: none;
}
}
// ❌ BAD - Forced animations
.animate-float {
animation: float 4s ease-in-out infinite !important;
}
```text
---
## Quick Reference
### Component Import Map
| Component | Import Path |
|---|---|
| Header | `@/components/common/Header` or `@/components/common` |
| Footer | `@/components/common/Footer` or `@/components/common` |
| ThemeSwitcher | `@/components/common/ThemeSwitcher` or `@/components/common` |
| Breadcrumbs | `@/components/ui/Breadcrumbs` or `@/components/ui` |
| Button | `@/components/ui/Button` or `@/components/ui` |
| PortfolioCard | `@/components/ui/PortfolioCard` or `@/components/ui` |
| VideoCard | `@/components/ui/VideoCard` or `@/components/ui` |
| BlogCard | `@/components/ui/BlogCard` or `@/components/ui` |
| HeroLayout | `@/components/sections/HeroLayout` |
| ArchiveFilters | `@/components/ui/ArchiveFilters` or `@/components/ui` |
| Lightbox | `@/components/ui/Lightbox` or `@/components/ui` |
| ScrollToTop | `@/components/ui/ScrollToTop` or `@/components/ui` |
| ScrollDownArrow | `@/components/ui/ScrollDownArrow` or `@/components/ui` |
### BEM Class Reference
| Component | Block Class | Modifiers |
|---|---|---|
| Portfolio Card | `.portfolio-card` | `--featured`, `--large` |
| Video Card | `.video-card` | `--featured` |
| Blog Card | `.blog-card` | `--featured`, `--large` |
| Button | `.btn` | `--primary`, `--secondary`, `--outline`, `--ghost` |
| Hero | `.hero` | `--dark`, `--light` |
| Section | `.section` | `--dark`, `--narrow`, `--wide` |
---
## Summary
This guide covered:
1. ✅ Layout components (HeroLayout, VideoArchiveLayout)
1. ✅ Common components (Header, Footer, ThemeSwitcher, MobileMenu)
1. ✅ UI components (Breadcrumbs, Button, Cards, Lightbox, Filters)
1. ✅ Page components (HomePage, PortfolioPage, BlogPage)
1. ✅ Section components (CTASection)
1. ✅ Form components (ContactForm, NewsletterSignup)
1. ✅ Animation components (ScrollToTop, ScrollDownArrow)
1. ✅ Best practices and quick reference
**Related Guides:**
- [Component Architecture Overview](../guidelines/overview-components.md)
- [Component Composition Guide](./component-composition-guide.md)
- [Custom Hooks Guide](./custom-hooks-guide.md)
---
**Last Updated:** March 12, 2026
**Maintained By:** Development Team