Initial commit
This commit is contained in:
500
docs/ACCESSIBILITY.md
Normal file
500
docs/ACCESSIBILITY.md
Normal file
@@ -0,0 +1,500 @@
|
||||
# Accessibility Standards
|
||||
|
||||
This document outlines accessibility (a11y) requirements for all components in the library, ensuring compatibility with screen readers and assistive technologies.
|
||||
|
||||
## Interactive Components
|
||||
|
||||
For buttons, links, and other interactive elements.
|
||||
|
||||
### Required Props
|
||||
|
||||
```tsx
|
||||
interface ButtonProps {
|
||||
text: string;
|
||||
onClick?: () => void;
|
||||
className?: string;
|
||||
// Accessibility props
|
||||
disabled?: boolean;
|
||||
ariaLabel?: string;
|
||||
type?: "button" | "submit" | "reset";
|
||||
}
|
||||
```
|
||||
|
||||
### Implementation Pattern
|
||||
|
||||
```tsx
|
||||
const Button = ({
|
||||
text,
|
||||
onClick,
|
||||
className = "",
|
||||
disabled = false,
|
||||
ariaLabel,
|
||||
type = "button",
|
||||
}: ButtonProps) => {
|
||||
return (
|
||||
<button
|
||||
type={type}
|
||||
onClick={onClick}
|
||||
disabled={disabled}
|
||||
aria-label={ariaLabel || text}
|
||||
className={cls(
|
||||
"base-button-styles",
|
||||
"disabled:cursor-not-allowed disabled:opacity-50",
|
||||
className
|
||||
)}
|
||||
>
|
||||
{text}
|
||||
</button>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
### Key Points
|
||||
|
||||
**ariaLabel:**
|
||||
- Optional prop with sensible fallback
|
||||
- Falls back to `text` content for buttons
|
||||
- Provides context for screen readers
|
||||
|
||||
**type:**
|
||||
- Default: `"button"`
|
||||
- Options: `"button" | "submit" | "reset"`
|
||||
- Prevents accidental form submission
|
||||
|
||||
**disabled:**
|
||||
- Default: `false`
|
||||
- Includes visual disabled states:
|
||||
- `disabled:cursor-not-allowed` - Shows not-allowed cursor
|
||||
- `disabled:opacity-50` - Reduces opacity for visual feedback
|
||||
|
||||
## Media Components
|
||||
|
||||
### Images
|
||||
|
||||
**Required Props:**
|
||||
```tsx
|
||||
interface ImageProps {
|
||||
imageSrc: string;
|
||||
imageAlt?: string; // Empty string for decorative images
|
||||
className?: string;
|
||||
}
|
||||
```
|
||||
|
||||
**Implementation:**
|
||||
```tsx
|
||||
const ImageComponent = ({
|
||||
imageSrc,
|
||||
imageAlt = "",
|
||||
className = "",
|
||||
}: ImageProps) => {
|
||||
return (
|
||||
<Image
|
||||
src={imageSrc}
|
||||
alt={imageAlt}
|
||||
aria-hidden={imageAlt === ""}
|
||||
className={className}
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
**Key Points:**
|
||||
- `imageAlt` - Alt text for images
|
||||
- Provide descriptive alt text for meaningful images
|
||||
- Use empty string (`""`) for decorative images
|
||||
- `aria-hidden={true}` - When alt is empty, mark as decorative
|
||||
- Screen readers will skip decorative images
|
||||
|
||||
### Videos
|
||||
|
||||
**Required Props:**
|
||||
```tsx
|
||||
interface VideoProps {
|
||||
videoSrc: string;
|
||||
videoAriaLabel?: string;
|
||||
className?: string;
|
||||
}
|
||||
```
|
||||
|
||||
**Implementation:**
|
||||
```tsx
|
||||
const VideoComponent = ({
|
||||
videoSrc,
|
||||
videoAriaLabel = "Video content",
|
||||
className = "",
|
||||
}: VideoProps) => {
|
||||
return (
|
||||
<video
|
||||
src={videoSrc}
|
||||
aria-label={videoAriaLabel}
|
||||
autoPlay
|
||||
loop
|
||||
muted
|
||||
playsInline
|
||||
className={className}
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
**Key Points:**
|
||||
- `videoAriaLabel` - Descriptive label for video element
|
||||
- Default: Sensible fallback like "Video content" or "Hero video"
|
||||
- Always include for screen reader context
|
||||
|
||||
### Media Content Pattern
|
||||
|
||||
For components supporting both images and videos:
|
||||
|
||||
```tsx
|
||||
interface HeroProps {
|
||||
imageSrc?: string;
|
||||
imageAlt?: string;
|
||||
videoSrc?: string;
|
||||
videoAriaLabel?: string;
|
||||
}
|
||||
|
||||
const Hero = ({
|
||||
imageSrc,
|
||||
imageAlt = "",
|
||||
videoSrc,
|
||||
videoAriaLabel = "Hero video",
|
||||
}: HeroProps) => {
|
||||
return (
|
||||
<>
|
||||
{videoSrc ? (
|
||||
<video
|
||||
src={videoSrc}
|
||||
aria-label={videoAriaLabel}
|
||||
autoPlay
|
||||
loop
|
||||
muted
|
||||
playsInline
|
||||
/>
|
||||
) : (
|
||||
imageSrc && (
|
||||
<Image
|
||||
src={imageSrc}
|
||||
alt={imageAlt}
|
||||
aria-hidden={imageAlt === ""}
|
||||
/>
|
||||
)
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
## Section Components
|
||||
|
||||
### Semantic HTML
|
||||
|
||||
Use semantic HTML elements for proper document structure:
|
||||
|
||||
```tsx
|
||||
<section> // For sections of content
|
||||
<header> // For page/section headers
|
||||
<nav> // For navigation
|
||||
<footer> // For page/section footers
|
||||
<article> // For self-contained content
|
||||
<aside> // For tangentially related content
|
||||
<main> // For main content area
|
||||
```
|
||||
|
||||
### Section Pattern
|
||||
|
||||
```tsx
|
||||
interface SectionProps {
|
||||
title: string;
|
||||
description: string;
|
||||
ariaLabel?: string;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const Section = ({
|
||||
title,
|
||||
description,
|
||||
ariaLabel = "Section name",
|
||||
className = "",
|
||||
}: SectionProps) => {
|
||||
return (
|
||||
<section
|
||||
aria-label={ariaLabel}
|
||||
className={cls("w-full py-20", className)}
|
||||
>
|
||||
<div className="w-content-width mx-auto">
|
||||
<h2>{title}</h2>
|
||||
<p>{description}</p>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
### Sensible Default aria-labels
|
||||
|
||||
Each section type should have a descriptive default:
|
||||
|
||||
```tsx
|
||||
// Hero section
|
||||
ariaLabel = "Hero section"
|
||||
|
||||
// About section
|
||||
ariaLabel = "About section"
|
||||
|
||||
// Feature section
|
||||
ariaLabel = "Features section"
|
||||
|
||||
// Testimonial section
|
||||
ariaLabel = "Testimonials section"
|
||||
|
||||
// Footer
|
||||
ariaLabel = "Footer"
|
||||
|
||||
// Navigation
|
||||
ariaLabel = "Navigation"
|
||||
```
|
||||
|
||||
### Heading Hierarchy
|
||||
|
||||
Maintain proper heading levels:
|
||||
|
||||
```tsx
|
||||
<h1> // Page title (once per page)
|
||||
<h2> // Section titles
|
||||
<h3> // Subsection titles
|
||||
<h4> // Card/component titles
|
||||
```
|
||||
|
||||
**Example:**
|
||||
```tsx
|
||||
<section aria-label="Features section">
|
||||
<h2>Our Features</h2> {/* Section title */}
|
||||
<div>
|
||||
<h3>Feature One</h3> {/* Feature title */}
|
||||
<p>Description...</p>
|
||||
</div>
|
||||
</section>
|
||||
```
|
||||
|
||||
## Form Components
|
||||
|
||||
### Input Fields
|
||||
|
||||
```tsx
|
||||
interface InputProps {
|
||||
label: string;
|
||||
id: string;
|
||||
type?: string;
|
||||
required?: boolean;
|
||||
ariaLabel?: string;
|
||||
ariaDescribedBy?: string;
|
||||
}
|
||||
|
||||
const Input = ({
|
||||
label,
|
||||
id,
|
||||
type = "text",
|
||||
required = false,
|
||||
ariaLabel,
|
||||
ariaDescribedBy,
|
||||
}: InputProps) => {
|
||||
return (
|
||||
<div>
|
||||
<label htmlFor={id}>
|
||||
{label}
|
||||
{required && <span aria-label="required">*</span>}
|
||||
</label>
|
||||
<input
|
||||
id={id}
|
||||
type={type}
|
||||
required={required}
|
||||
aria-label={ariaLabel || label}
|
||||
aria-describedby={ariaDescribedBy}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
**Key Points:**
|
||||
- Always associate `<label>` with input using `htmlFor` and `id`
|
||||
- Mark required fields visually and semantically
|
||||
- Use `aria-describedby` for error messages or hints
|
||||
|
||||
### Form Validation
|
||||
|
||||
```tsx
|
||||
const [error, setError] = useState("");
|
||||
|
||||
<input
|
||||
aria-invalid={!!error}
|
||||
aria-describedby={error ? "error-message" : undefined}
|
||||
/>
|
||||
{error && (
|
||||
<p id="error-message" role="alert">
|
||||
{error}
|
||||
</p>
|
||||
)}
|
||||
```
|
||||
|
||||
## Focus Management
|
||||
|
||||
### Focus Indicators
|
||||
|
||||
Never remove focus outlines without providing alternatives:
|
||||
|
||||
```tsx
|
||||
// ❌ WRONG
|
||||
className="outline-none"
|
||||
|
||||
// ✅ CORRECT - Custom focus indicator
|
||||
className="focus:outline-none focus:ring-2 focus:ring-foreground/50"
|
||||
```
|
||||
|
||||
### Focus Trap (for modals/dialogs)
|
||||
|
||||
When implementing modals or dialogs, ensure:
|
||||
- Focus moves to modal when opened
|
||||
- Tab/Shift+Tab cycles through modal elements only
|
||||
- Focus returns to trigger element when closed
|
||||
- Escape key closes modal
|
||||
|
||||
## Keyboard Navigation
|
||||
|
||||
### Interactive Elements
|
||||
|
||||
All interactive elements must be keyboard accessible:
|
||||
|
||||
```tsx
|
||||
// Buttons and links work by default
|
||||
<button onClick={handleClick}>Click me</button>
|
||||
<a href="/page">Link</a>
|
||||
|
||||
// Custom interactive elements need tabIndex and keyboard handlers
|
||||
<div
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
onClick={handleClick}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === 'Enter' || e.key === ' ') {
|
||||
e.preventDefault();
|
||||
handleClick();
|
||||
}
|
||||
}}
|
||||
>
|
||||
Custom button
|
||||
</div>
|
||||
```
|
||||
|
||||
### Skip Links
|
||||
|
||||
For navigation-heavy pages, provide skip links:
|
||||
|
||||
```tsx
|
||||
<a href="#main-content" className="sr-only focus:not-sr-only">
|
||||
Skip to main content
|
||||
</a>
|
||||
```
|
||||
|
||||
## Screen Reader Only Content
|
||||
|
||||
Use the `sr-only` class for content that should only be read by screen readers:
|
||||
|
||||
```tsx
|
||||
<span className="sr-only">Additional context for screen readers</span>
|
||||
```
|
||||
|
||||
Tailwind's `sr-only` class:
|
||||
```css
|
||||
.sr-only {
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
padding: 0;
|
||||
margin: -1px;
|
||||
overflow: hidden;
|
||||
clip: rect(0, 0, 0, 0);
|
||||
white-space: nowrap;
|
||||
border-width: 0;
|
||||
}
|
||||
```
|
||||
|
||||
## ARIA Roles
|
||||
|
||||
Use ARIA roles when semantic HTML isn't sufficient:
|
||||
|
||||
```tsx
|
||||
// Navigation
|
||||
<nav role="navigation" aria-label="Main navigation">
|
||||
|
||||
// Button (for non-button elements)
|
||||
<div role="button" tabIndex={0}>
|
||||
|
||||
// Alert/Status messages
|
||||
<div role="alert">Error message</div>
|
||||
<div role="status">Loading...</div>
|
||||
|
||||
// Presentation (decorative)
|
||||
<div role="presentation">
|
||||
|
||||
// Dialog/Modal
|
||||
<div role="dialog" aria-modal="true" aria-labelledby="dialog-title">
|
||||
```
|
||||
|
||||
## Loading States
|
||||
|
||||
Provide feedback for loading states:
|
||||
|
||||
```tsx
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
|
||||
<button disabled={isLoading} aria-busy={isLoading}>
|
||||
{isLoading ? (
|
||||
<>
|
||||
<span className="sr-only">Loading...</span>
|
||||
<Spinner aria-hidden="true" />
|
||||
</>
|
||||
) : (
|
||||
"Submit"
|
||||
)}
|
||||
</button>
|
||||
```
|
||||
|
||||
## Accessibility Checklist
|
||||
|
||||
### Interactive Components
|
||||
- [ ] Add `ariaLabel` prop (optional with sensible fallback)
|
||||
- [ ] Add `type` prop for buttons (default: `"button"`)
|
||||
- [ ] Add `disabled` prop with visual states
|
||||
- [ ] Include disabled state styling (`disabled:cursor-not-allowed disabled:opacity-50`)
|
||||
- [ ] Ensure keyboard accessibility (Enter/Space for custom elements)
|
||||
- [ ] Provide custom focus indicators if removing default outline
|
||||
|
||||
### Media Components
|
||||
- [ ] Images: Add `imageAlt` prop
|
||||
- [ ] Images: Use `aria-hidden={true}` when alt is empty (decorative)
|
||||
- [ ] Videos: Add `videoAriaLabel` prop with sensible default
|
||||
- [ ] Provide meaningful default labels
|
||||
|
||||
### Section Components
|
||||
- [ ] Use semantic HTML (`<section>`, `<header>`, `<nav>`, `<footer>`)
|
||||
- [ ] Add `ariaLabel` prop with sensible default
|
||||
- [ ] Follow proper heading hierarchy (h1 → h2 → h3)
|
||||
- [ ] Use `w-full py-20` for section spacing (except hero/footer)
|
||||
- [ ] Use `w-content-width mx-auto` for content wrapper
|
||||
|
||||
### Form Components
|
||||
- [ ] Associate labels with inputs using `htmlFor` and `id`
|
||||
- [ ] Mark required fields semantically
|
||||
- [ ] Use `aria-invalid` for validation errors
|
||||
- [ ] Use `aria-describedby` for error messages/hints
|
||||
- [ ] Provide `role="alert"` for error messages
|
||||
|
||||
### General
|
||||
- [ ] Test with keyboard navigation (Tab, Shift+Tab, Enter, Space, Escape)
|
||||
- [ ] Test with screen reader (VoiceOver, NVDA, JAWS)
|
||||
- [ ] Ensure sufficient color contrast (WCAG AA minimum)
|
||||
- [ ] Provide focus indicators for all interactive elements
|
||||
- [ ] Use semantic HTML before ARIA roles
|
||||
- [ ] Include screen reader only text when needed (`sr-only`)
|
||||
492
docs/CARDSTACK_SECTIONS.md
Normal file
492
docs/CARDSTACK_SECTIONS.md
Normal file
@@ -0,0 +1,492 @@
|
||||
# CardStack Section Pattern
|
||||
|
||||
This document covers the CardStack pattern used in Feature, Product, Pricing, Testimonial, Team, Blog, and Metrics section components.
|
||||
|
||||
## Required Type Imports
|
||||
|
||||
```tsx
|
||||
// Centralized type definitions
|
||||
import type { TextboxLayout, InvertedBackground } from "@/providers/themeProvider/config/constants";
|
||||
import type { ButtonConfig, GridVariant, CardAnimationType, ContainerStyle, TitleSegment } from "@/components/cardStack/types";
|
||||
```
|
||||
|
||||
**Key Types:**
|
||||
- `TextboxLayout` - Layout options for section headers ("default" | "split" | "split-actions" | "split-description" | "inline-image")
|
||||
- `InvertedBackground` - Background inversion options ("noInvert" | "invertDefault" | "invertCard")
|
||||
- `TitleSegment` - Type for inline image segments ({ type: "text" | "image", content/src, alt? })
|
||||
- `ButtonConfig` - Button configuration interface
|
||||
- `GridVariant` - Grid layout variants
|
||||
- `CardAnimationType` - Card animation types
|
||||
- `ContainerStyle` - Container styling options
|
||||
|
||||
## Overview
|
||||
|
||||
CardStack is an intelligent layout component that automatically switches between grid, carousel, and timeline layouts based on item count and configuration.
|
||||
|
||||
**Mode Selection (Automatic):**
|
||||
- **1-4 items**: Grid mode (displays as bento grid)
|
||||
- **5+ items**: Carousel mode (auto-scrolling or button-controlled)
|
||||
- **3-6 items with timeline variant**: Timeline layout (or carousel if 7+)
|
||||
|
||||
## Grid Variants
|
||||
|
||||
There are 9 bento grid layouts plus uniform layouts:
|
||||
|
||||
### Uniform Layouts
|
||||
- `uniform-all-items-equal` - All items same size (default)
|
||||
- `uniform-2-items` - Two equal columns
|
||||
- `uniform-3-items` - Three equal columns
|
||||
- `uniform-4-items` - Four equal columns
|
||||
|
||||
### Bento Layouts (Asymmetric)
|
||||
- `two-columns-alternating-heights` - Alternating tall/short columns
|
||||
- `asymmetric-60-wide-40-narrow` - 60% wide left, 40% narrow right
|
||||
- `three-columns-all-equal-width` - Three equal columns
|
||||
- `four-items-2x2-equal-grid` - Perfect 2x2 grid
|
||||
- `one-large-right-three-stacked-left` - Left: 3 items, Right: 1 large
|
||||
- `items-top-row-full-width-bottom` - Top: full width, Bottom: items
|
||||
- `full-width-top-items-bottom-row` - Full width top, items below
|
||||
- `one-large-left-three-stacked-right` - Left: 1 large, Right: 3 items
|
||||
- `timeline` - Zigzag timeline layout
|
||||
|
||||
## Height Control Pattern
|
||||
|
||||
### uniformGridCustomHeightClasses Prop
|
||||
|
||||
All CardStack-based components should accept this optional prop to control item heights in both grid and carousel modes.
|
||||
|
||||
```tsx
|
||||
interface SectionCardProps {
|
||||
items: ItemType[];
|
||||
gridVariant: GridVariant;
|
||||
uniformGridCustomHeightClasses?: string;
|
||||
carouselMode?: "auto" | "buttons";
|
||||
// ... other props
|
||||
}
|
||||
```
|
||||
|
||||
### Default Values by Component Type
|
||||
|
||||
**Most components (Feature, Product, Pricing, Team, Metrics, Blog):**
|
||||
```tsx
|
||||
uniformGridCustomHeightClasses = "min-h-80 2xl:min-h-90"
|
||||
```
|
||||
|
||||
**Testimonial components (need flexible heights):**
|
||||
```tsx
|
||||
uniformGridCustomHeightClasses = "min-h-none"
|
||||
```
|
||||
|
||||
**Hero carousel components (no minimum):**
|
||||
```tsx
|
||||
uniformGridCustomHeightClasses = "min-h-0"
|
||||
```
|
||||
|
||||
**Feature components (optimized for compact layout):**
|
||||
```tsx
|
||||
// Hardcoded in FeatureCardFour
|
||||
uniformGridCustomHeightClasses = "min-h-0"
|
||||
```
|
||||
|
||||
### Implementation Pattern
|
||||
|
||||
The prop flows: Section Component → CardStack → GridLayout/Carousel
|
||||
|
||||
```tsx
|
||||
// In section component (e.g., ProductCardOne.tsx)
|
||||
const ProductCardOne = ({
|
||||
products,
|
||||
gridVariant,
|
||||
uniformGridCustomHeightClasses = "min-h-80 2xl:min-h-90",
|
||||
// ... other props
|
||||
}: ProductCardOneProps) => {
|
||||
return (
|
||||
<CardStack
|
||||
gridVariant={gridVariant}
|
||||
uniformGridCustomHeightClasses={uniformGridCustomHeightClasses}
|
||||
// ... other props
|
||||
>
|
||||
{products.map((product) => (
|
||||
<div className="card">...</div>
|
||||
))}
|
||||
</CardStack>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
**Individual card elements must use `min-h-0`:**
|
||||
```tsx
|
||||
<div className={cls("card p-6 rounded-theme-capped h-full min-h-0")}>
|
||||
{/* Product content */}
|
||||
</div>
|
||||
```
|
||||
|
||||
This prevents height conflicts and ensures proper flex behavior.
|
||||
|
||||
## Carousel Modes
|
||||
|
||||
### carouselMode Prop
|
||||
|
||||
```tsx
|
||||
carouselMode?: "auto" | "buttons"
|
||||
```
|
||||
|
||||
- **`"auto"`** - Auto-scrolling carousel (uses AutoCarousel with embla-carousel-auto-scroll)
|
||||
- **`"buttons"`** - Button-controlled carousel (uses ButtonCarousel with prev/next buttons)
|
||||
|
||||
**Default is typically `"buttons"`** for better accessibility and user control.
|
||||
|
||||
## TextBox Integration
|
||||
|
||||
CardStack components integrate with TextBox for section headers.
|
||||
|
||||
### TextBox Layout Options
|
||||
|
||||
```tsx
|
||||
import type { TextboxLayout } from "@/providers/themeProvider/config/constants";
|
||||
|
||||
textboxLayout: TextboxLayout // "default" | "split" | "split-actions" | "split-description" | "inline-image"
|
||||
```
|
||||
|
||||
**Layout Modes:**
|
||||
|
||||
1. **`"default"`** - Title and description stacked vertically, centered or left-aligned
|
||||
```
|
||||
[Tag]
|
||||
Title
|
||||
Description
|
||||
[Buttons]
|
||||
```
|
||||
|
||||
2. **`"split"`** - Title and description on left (60%), buttons on right (40%)
|
||||
```
|
||||
[Tag]
|
||||
Title | Description | [Buttons]
|
||||
```
|
||||
|
||||
3. **`"split-actions"`** - Title and description on left, buttons on right (no description on right)
|
||||
```
|
||||
[Tag]
|
||||
Title | [Buttons]
|
||||
Description |
|
||||
```
|
||||
|
||||
4. **`"split-description"`** - Title on left, description on right, buttons below
|
||||
```
|
||||
[Tag]
|
||||
Title | Description
|
||||
[Buttons]
|
||||
```
|
||||
|
||||
5. **`"inline-image"`** - Centered heading with inline images between text segments, buttons below
|
||||
```
|
||||
Text [Image] Text [Image] Text
|
||||
[Buttons]
|
||||
```
|
||||
|
||||
**Special props for inline-image layout:**
|
||||
```tsx
|
||||
import type { TitleSegment } from "@/components/cardStack/types";
|
||||
|
||||
titleSegments?: TitleSegment[] // Array of text and image segments
|
||||
titleImageWrapperClassName?: string // Styling for image wrapper
|
||||
titleImageClassName?: string // Styling for the image itself
|
||||
```
|
||||
|
||||
**Example usage:**
|
||||
```tsx
|
||||
<FeatureCardOne
|
||||
titleSegments={[
|
||||
{ type: "text", content: "Discover" },
|
||||
{ type: "image", src: "/icon.png", alt: "Icon" },
|
||||
{ type: "text", content: "powerful features" }
|
||||
]}
|
||||
textboxLayout="inline-image"
|
||||
// ... other props
|
||||
/>
|
||||
```
|
||||
|
||||
**Inline Image Behavior:**
|
||||
- Images are styled with `primary-button` background
|
||||
- Automatic rotation alternation: 1st: -rotate-12, 2nd: rotate-12, etc.
|
||||
- Square aspect ratio (1.1em height)
|
||||
- Proper spacing with mx-1 margins
|
||||
- Supports both local paths and external URLs
|
||||
|
||||
### TextBox Props in CardStack
|
||||
|
||||
```tsx
|
||||
<CardStack
|
||||
title="Our Products"
|
||||
titleSegments={[
|
||||
{ type: "text", content: "Our" },
|
||||
{ type: "image", src: "/icon.png", alt: "Product Icon" },
|
||||
{ type: "text", content: "Products" }
|
||||
]} // Optional: use titleSegments for inline-image layout
|
||||
description="Discover our latest offerings"
|
||||
tag="Products"
|
||||
tagIcon={Package}
|
||||
buttons={[
|
||||
{ text: "View All", href: "/products" }
|
||||
]}
|
||||
textboxLayout="split" // or "inline-image" with titleSegments
|
||||
useInvertedBackground="noInvert" // "noInvert" | "invertDefault" | "invertCard"
|
||||
// TextBox className overrides
|
||||
textBoxClassName=""
|
||||
titleClassName=""
|
||||
titleImageWrapperClassName="" // For inline-image layout
|
||||
titleImageClassName="" // For inline-image layout
|
||||
descriptionClassName=""
|
||||
tagClassName=""
|
||||
buttonContainerClassName=""
|
||||
buttonClassName=""
|
||||
buttonTextClassName=""
|
||||
// ... other props
|
||||
>
|
||||
```
|
||||
|
||||
## Button System
|
||||
|
||||
### ButtonConfig Interface
|
||||
|
||||
```tsx
|
||||
interface ButtonConfig {
|
||||
text: string;
|
||||
onClick?: () => void;
|
||||
href?: string;
|
||||
props?: Partial<ButtonPropsForVariant<CTAButtonVariant>>;
|
||||
// NO variant property - controlled by ThemeProvider
|
||||
}
|
||||
```
|
||||
|
||||
### Button Rendering Logic
|
||||
|
||||
Buttons are rendered with automatic primary/secondary styling:
|
||||
|
||||
- **Index 0**: Primary button (`primary-button` class)
|
||||
- **Index 1+**: Secondary button (`secondary-button` class)
|
||||
|
||||
**Maximum 2 buttons** per section (enforced in TextBox component).
|
||||
|
||||
```tsx
|
||||
const buttons: ButtonConfig[] = [
|
||||
{ text: "Get Started", href: "/signup" }, // Primary
|
||||
{ text: "Learn More", onClick: () => {} } // Secondary
|
||||
];
|
||||
```
|
||||
|
||||
The `variant` is determined by ThemeProvider's `defaultButtonVariant` setting.
|
||||
|
||||
## Complete CardStack Section Example
|
||||
|
||||
```tsx
|
||||
"use client";
|
||||
|
||||
import React, { memo } from "react";
|
||||
import CardStack from "@/components/cardStack/CardStack";
|
||||
import { cls } from "@/lib/utils";
|
||||
import type { GridVariant, ButtonConfig } from "@/components/cardStack/types";
|
||||
import type { LucideIcon } from "lucide-react";
|
||||
|
||||
type Product = {
|
||||
title: string;
|
||||
description: string;
|
||||
price: string;
|
||||
image: string;
|
||||
};
|
||||
|
||||
interface ProductCardOneProps {
|
||||
products: Product[];
|
||||
carouselMode?: "auto" | "buttons";
|
||||
gridVariant: GridVariant;
|
||||
uniformGridCustomHeightClasses?: string;
|
||||
title: string;
|
||||
description: string;
|
||||
tag?: string;
|
||||
tagIcon?: LucideIcon;
|
||||
buttons?: ButtonConfig[];
|
||||
textboxLayout: "default" | "split" | "split-actions" | "split-description";
|
||||
ariaLabel?: string;
|
||||
// Main wrapper
|
||||
className?: string;
|
||||
// CardStack customization
|
||||
cardStackClassName?: string;
|
||||
// TextBox customization
|
||||
textBoxClassName?: string;
|
||||
titleClassName?: string;
|
||||
descriptionClassName?: string;
|
||||
tagClassName?: string;
|
||||
buttonsWrapperClassName?: string;
|
||||
// Card customization
|
||||
cardClassName?: string;
|
||||
cardTitleClassName?: string;
|
||||
cardDescriptionClassName?: string;
|
||||
cardPriceClassName?: string;
|
||||
cardImageClassName?: string;
|
||||
}
|
||||
|
||||
const ProductCardOne = ({
|
||||
products,
|
||||
carouselMode = "buttons",
|
||||
gridVariant,
|
||||
uniformGridCustomHeightClasses = "min-h-80 2xl:min-h-90",
|
||||
title,
|
||||
description,
|
||||
tag,
|
||||
tagIcon,
|
||||
buttons,
|
||||
textboxLayout,
|
||||
ariaLabel = "Product section",
|
||||
className = "",
|
||||
cardStackClassName = "",
|
||||
textBoxClassName = "",
|
||||
titleClassName = "",
|
||||
descriptionClassName = "",
|
||||
tagClassName = "",
|
||||
buttonsWrapperClassName = "",
|
||||
cardClassName = "",
|
||||
cardTitleClassName = "",
|
||||
cardDescriptionClassName = "",
|
||||
cardPriceClassName = "",
|
||||
cardImageClassName = "",
|
||||
}: ProductCardOneProps) => {
|
||||
return (
|
||||
<section
|
||||
aria-label={ariaLabel}
|
||||
className={cls("w-full py-20", className)}
|
||||
>
|
||||
<div className="w-content-width mx-auto">
|
||||
<CardStack
|
||||
mode={carouselMode}
|
||||
gridVariant={gridVariant}
|
||||
uniformGridCustomHeightClasses={uniformGridCustomHeightClasses}
|
||||
title={title}
|
||||
description={description}
|
||||
tag={tag}
|
||||
tagIcon={tagIcon}
|
||||
buttons={buttons}
|
||||
textboxLayout={textboxLayout}
|
||||
className={cardStackClassName}
|
||||
textBoxClassName={textBoxClassName}
|
||||
titleClassName={titleClassName}
|
||||
descriptionClassName={descriptionClassName}
|
||||
tagClassName={tagClassName}
|
||||
buttonsWrapperClassName={buttonsWrapperClassName}
|
||||
>
|
||||
{products.map((product, index) => (
|
||||
<div
|
||||
key={`${product.title}-${index}`}
|
||||
className={cls(
|
||||
"card p-6 rounded-theme-capped h-full min-h-0 flex flex-col",
|
||||
cardClassName
|
||||
)}
|
||||
>
|
||||
<img
|
||||
src={product.image}
|
||||
alt={product.title}
|
||||
className={cls("w-full h-48 object-cover mb-4", cardImageClassName)}
|
||||
/>
|
||||
<h3 className={cls("text-xl font-semibold mb-2", cardTitleClassName)}>
|
||||
{product.title}
|
||||
</h3>
|
||||
<p className={cls("text-foreground/75 flex-1", cardDescriptionClassName)}>
|
||||
{product.description}
|
||||
</p>
|
||||
<p className={cls("text-2xl font-bold mt-4", cardPriceClassName)}>
|
||||
{product.price}
|
||||
</p>
|
||||
</div>
|
||||
))}
|
||||
</CardStack>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
ProductCardOne.displayName = "ProductCardOne";
|
||||
|
||||
export default memo(ProductCardOne);
|
||||
```
|
||||
|
||||
## Animation Types
|
||||
|
||||
CardStack supports GSAP-powered scroll-triggered animations:
|
||||
|
||||
```tsx
|
||||
animationType?: "none" | "opacity" | "slide-up" | "scale-rotate" | "blur-reveal"
|
||||
```
|
||||
|
||||
**Animation Descriptions:**
|
||||
- **`none`** - No animation
|
||||
- **`opacity`** - Fade in
|
||||
- **`slide-up`** - Slide up from below with stagger
|
||||
- **`scale-rotate`** - Scale + rotate entrance with stagger
|
||||
- **`blur-reveal`** - Blur to clear reveal with stagger
|
||||
|
||||
Each animation uses GSAP ScrollTrigger with staggered effect on children.
|
||||
|
||||
## Best Practices
|
||||
|
||||
### ✅ DO:
|
||||
|
||||
- Accept `uniformGridCustomHeightClasses` as optional prop with sensible default
|
||||
- Use `min-h-0` on individual card elements for proper flex behavior
|
||||
- Pass through all CardStack customization props (className overrides)
|
||||
- Use appropriate default height for your component type
|
||||
- Document the default value in registry propsSchema
|
||||
- Provide className props for all card sub-elements
|
||||
- Use `card` class for consistent card styling (theme-aware)
|
||||
- Use `rounded-theme-capped` for card border radius
|
||||
- Set `carouselMode` default to `"buttons"` for accessibility
|
||||
|
||||
### ❌ DO NOT:
|
||||
|
||||
- Hardcode height classes in CardStack (let it be controlled via prop)
|
||||
- Remove the `uniformGridCustomHeightClasses` prop without specific reason
|
||||
- Use different height classes for grid vs carousel (they should match)
|
||||
- Forget to apply `min-h-0` on card wrapper divs
|
||||
- Specify button `variant` in ButtonConfig (controlled by ThemeProvider)
|
||||
- Create more than 2 buttons per section
|
||||
- Use `lg:` or `xl:` breakpoints for layout changes
|
||||
|
||||
## CardStack Component Checklist
|
||||
|
||||
When creating a new CardStack-based section component:
|
||||
|
||||
### Props & Configuration
|
||||
- [ ] Accept `uniformGridCustomHeightClasses` prop with appropriate default
|
||||
- [ ] Accept `carouselMode` prop (default: `"buttons"`)
|
||||
- [ ] Accept `gridVariant` as required prop (or hardcode if single variant)
|
||||
- [ ] Accept `textboxLayout` for TextBox configuration
|
||||
- [ ] Accept `animationType` for scroll animations (optional)
|
||||
|
||||
### CardStack Integration
|
||||
- [ ] Pass `uniformGridCustomHeightClasses` to CardStack
|
||||
- [ ] Pass all TextBox props (title, description, tag, tagIcon, buttons)
|
||||
- [ ] Pass all TextBox className overrides
|
||||
- [ ] Pass cardStackClassName for CardStack wrapper customization
|
||||
|
||||
### Card Implementation
|
||||
- [ ] Apply `min-h-0` to individual card wrapper divs
|
||||
- [ ] Use `card` class for card background/border styling
|
||||
- [ ] Use `rounded-theme-capped` for border radius
|
||||
- [ ] Provide className override props for all card sub-elements
|
||||
- [ ] Use `h-full` on cards for consistent heights within grid
|
||||
|
||||
### Button System
|
||||
- [ ] Use ButtonConfig type for buttons array
|
||||
- [ ] Do NOT specify variant in ButtonConfig
|
||||
- [ ] Maximum 2 buttons
|
||||
- [ ] Let ThemeProvider control button variant
|
||||
|
||||
### Section Structure
|
||||
- [ ] Use semantic `<section>` tag with aria-label
|
||||
- [ ] Use `w-full py-20` on section
|
||||
- [ ] Use `w-content-width mx-auto` wrapper
|
||||
- [ ] Provide section className override
|
||||
|
||||
### Documentation
|
||||
- [ ] Document `uniformGridCustomHeightClasses` default in registry
|
||||
- [ ] Document all grid variants supported
|
||||
- [ ] Document carousel mode options
|
||||
- [ ] Include usage example with typical props
|
||||
437
docs/COMPONENT_IMPLEMENTATION.md
Normal file
437
docs/COMPONENT_IMPLEMENTATION.md
Normal file
@@ -0,0 +1,437 @@
|
||||
# Component Implementation Standards
|
||||
|
||||
This document outlines the core implementation patterns for creating components in this library, optimized for AI website builders.
|
||||
|
||||
## Component Structure Template
|
||||
|
||||
Every component should follow this structure:
|
||||
|
||||
```tsx
|
||||
"use client";
|
||||
|
||||
import React from "react";
|
||||
import { cls } from "@/lib/utils";
|
||||
|
||||
interface ComponentProps {
|
||||
// Required props first
|
||||
text: string;
|
||||
// Optional props with explicit types
|
||||
onClick?: () => void;
|
||||
className?: string;
|
||||
disabled?: boolean;
|
||||
ariaLabel?: string;
|
||||
type?: "button" | "submit" | "reset";
|
||||
}
|
||||
|
||||
const Component = ({
|
||||
text,
|
||||
onClick,
|
||||
className = "",
|
||||
disabled = false,
|
||||
ariaLabel,
|
||||
type = "button",
|
||||
}: ComponentProps) => {
|
||||
return (
|
||||
<element
|
||||
type={type}
|
||||
onClick={onClick}
|
||||
disabled={disabled}
|
||||
aria-label={ariaLabel || text}
|
||||
className={cls("base-classes", "disabled-states", className)}
|
||||
>
|
||||
{text}
|
||||
</element>
|
||||
);
|
||||
};
|
||||
|
||||
Component.displayName = "Component";
|
||||
|
||||
export default React.memo(Component);
|
||||
```
|
||||
|
||||
**Key Requirements:**
|
||||
- `"use client"` directive when needed (interactive components, hooks)
|
||||
- Named exports with `displayName` for debugging
|
||||
- Wrap in `React.memo()` for performance optimization
|
||||
- Use `cls()` utility for class composition (never plain string concatenation)
|
||||
|
||||
## Prop Structure & Defaults
|
||||
|
||||
### Required Props
|
||||
Core content props should be **required** with no default values:
|
||||
- Section components: `title`, `description`
|
||||
- Button components: `text`
|
||||
- Media components: `imageSrc` or `videoSrc` (when applicable)
|
||||
|
||||
### Optional Props with Defaults
|
||||
|
||||
**Standard className defaults:**
|
||||
```tsx
|
||||
className = "",
|
||||
textClassName = "",
|
||||
iconClassName = "",
|
||||
containerClassName = "",
|
||||
```
|
||||
|
||||
Empty string defaults prevent undefined checks and are standard practice.
|
||||
|
||||
**Common optional props:**
|
||||
```tsx
|
||||
disabled = false,
|
||||
type = "button",
|
||||
ariaLabel, // No default, falls back to sensible value in component
|
||||
```
|
||||
|
||||
**Component-specific props:**
|
||||
Document defaults clearly in both code and registry:
|
||||
```tsx
|
||||
strengthFactor = 20,
|
||||
carouselMode = "buttons",
|
||||
uniformGridCustomHeightClasses = "min-h-80 2xl:min-h-90",
|
||||
```
|
||||
|
||||
## Naming Conventions
|
||||
|
||||
### Section Components (Hero, About, Feature, etc.)
|
||||
|
||||
**✅ CORRECT:**
|
||||
```tsx
|
||||
interface HeroProps {
|
||||
title: string; // Primary heading
|
||||
description: string; // Supporting text
|
||||
buttons?: ButtonConfig[];
|
||||
}
|
||||
```
|
||||
|
||||
**❌ WRONG:**
|
||||
```tsx
|
||||
interface HeroProps {
|
||||
heading: string; // Should be "title"
|
||||
subtitle: string; // Should be "description"
|
||||
text: string; // Ambiguous
|
||||
}
|
||||
```
|
||||
|
||||
### Button Components
|
||||
|
||||
**✅ CORRECT:**
|
||||
```tsx
|
||||
interface ButtonProps {
|
||||
text: string; // Button label
|
||||
onClick?: () => void;
|
||||
}
|
||||
```
|
||||
|
||||
**❌ WRONG:**
|
||||
```tsx
|
||||
interface ButtonProps {
|
||||
title: string; // Should be "text"
|
||||
label: string; // Should be "text"
|
||||
}
|
||||
```
|
||||
|
||||
### Button Config (for sections)
|
||||
|
||||
```tsx
|
||||
interface ButtonConfig {
|
||||
text: string; // Button label (not "title" or "label")
|
||||
href?: string;
|
||||
onClick?: () => void;
|
||||
props?: Partial<ButtonPropsForVariant<CTAButtonVariant>>;
|
||||
// NO variant property - controlled by ThemeProvider
|
||||
}
|
||||
```
|
||||
|
||||
**Consistency is critical:**
|
||||
- All hero sections must use the same prop names
|
||||
- All about sections must use the same prop names
|
||||
- Registry documentation must match component prop names exactly
|
||||
|
||||
## Component Customizability
|
||||
|
||||
Provide className props for **all major elements** to allow full styling control:
|
||||
|
||||
```tsx
|
||||
interface SectionProps {
|
||||
title: string;
|
||||
description: string;
|
||||
// Main wrapper
|
||||
className?: string;
|
||||
// Inner container
|
||||
containerClassName?: string;
|
||||
// Content areas
|
||||
textClassName?: string;
|
||||
mediaWrapperClassName?: string;
|
||||
imageClassName?: string;
|
||||
}
|
||||
|
||||
const Section = ({
|
||||
title,
|
||||
description,
|
||||
className = "",
|
||||
containerClassName = "",
|
||||
textClassName = "",
|
||||
mediaWrapperClassName = "",
|
||||
imageClassName = "",
|
||||
}: SectionProps) => {
|
||||
return (
|
||||
<section className={cls("base-section-styles", className)}>
|
||||
<div className={cls("base-container-styles", containerClassName)}>
|
||||
<div className={cls("base-text-styles", textClassName)}>
|
||||
{/* content */}
|
||||
</div>
|
||||
<div className={cls("base-media-wrapper-styles", mediaWrapperClassName)}>
|
||||
<img className={cls("base-image-styles", imageClassName)} />
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
**Naming convention:**
|
||||
- `className` - Main wrapper element
|
||||
- `containerClassName` - Inner container
|
||||
- `[element]ClassName` - Specific elements (e.g., `textClassName`, `imageClassName`)
|
||||
|
||||
## Component Composition & Base Styles
|
||||
|
||||
When composing higher-level components from base components, **set sensible base styles** while accepting className overrides:
|
||||
|
||||
```tsx
|
||||
interface HeroProps {
|
||||
title: string;
|
||||
description: string;
|
||||
titleClassName?: string;
|
||||
descriptionClassName?: string;
|
||||
textBoxClassName?: string;
|
||||
}
|
||||
|
||||
const Hero = ({
|
||||
title,
|
||||
description,
|
||||
titleClassName = "",
|
||||
descriptionClassName = "",
|
||||
textBoxClassName = "",
|
||||
}: HeroProps) => {
|
||||
return (
|
||||
<section>
|
||||
<TextBox
|
||||
title={title}
|
||||
description={description}
|
||||
// Set base styles, allow overrides
|
||||
className={cls("flex flex-col gap-3 md:gap-1", textBoxClassName)}
|
||||
titleClassName={cls("text-6xl font-medium", titleClassName)}
|
||||
descriptionClassName={cls("text-lg leading-[1.2]", descriptionClassName)}
|
||||
center={true}
|
||||
/>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
**Key principles:**
|
||||
- Base styles come first in `cls()`, overrides second
|
||||
- This ensures good defaults while maintaining full customizability
|
||||
- AI builders can use components without styling knowledge, but advanced users can override
|
||||
- Use `cls()` utility for proper class merging (prevents Tailwind conflicts)
|
||||
|
||||
## Type Safety
|
||||
|
||||
### Use Explicit Prop Interfaces
|
||||
```tsx
|
||||
// ✅ CORRECT - Clear and explicit
|
||||
interface ButtonProps {
|
||||
text: string;
|
||||
onClick?: () => void;
|
||||
variant?: "primary" | "secondary";
|
||||
}
|
||||
|
||||
// ❌ WRONG - Over-complicated
|
||||
interface ButtonProps extends React.ComponentPropsWithoutRef<'button'> {
|
||||
// ... harder for AI to understand
|
||||
}
|
||||
```
|
||||
|
||||
### Use Discriminated Unions for Variants
|
||||
```tsx
|
||||
type MediaProps =
|
||||
| {
|
||||
imageSrc: string;
|
||||
imageAlt?: string;
|
||||
videoSrc?: never;
|
||||
}
|
||||
| {
|
||||
videoSrc: string;
|
||||
videoAriaLabel?: string;
|
||||
imageSrc?: never;
|
||||
};
|
||||
```
|
||||
|
||||
### Export Reusable Types
|
||||
```tsx
|
||||
export type ButtonConfig = {
|
||||
text: string;
|
||||
href?: string;
|
||||
onClick?: () => void;
|
||||
};
|
||||
|
||||
export type GridVariant =
|
||||
| "uniform-all-items-equal"
|
||||
| "two-columns-alternating-heights"
|
||||
| "asymmetric-60-wide-40-narrow"
|
||||
// ... etc
|
||||
```
|
||||
|
||||
## Responsive Design
|
||||
|
||||
### Mobile-First Approach
|
||||
|
||||
**Default styles apply to mobile devices:**
|
||||
```tsx
|
||||
// ✅ CORRECT - Mobile until md breakpoint (768px)
|
||||
<div className="flex-col md:flex-row">
|
||||
<img className="w-full h-auto md:h-8 md:w-auto" />
|
||||
</div>
|
||||
|
||||
// ❌ WRONG - Using lg: breakpoint
|
||||
<div className="flex-col lg:flex-row">
|
||||
<img className="w-full h-auto lg:h-8 lg:w-auto" />
|
||||
</div>
|
||||
```
|
||||
|
||||
**Breakpoint Rules:**
|
||||
- **Mobile styles**: No prefix (default)
|
||||
- **Desktop styles**: `md:` prefix only (768px breakpoint)
|
||||
- **Never use**: `lg:`, `xl:`, `2xl:` breakpoints for layout changes
|
||||
|
||||
**Exceptions:** Only use larger breakpoints for minor tweaks:
|
||||
```tsx
|
||||
// Acceptable for minor adjustments
|
||||
className="min-h-80 2xl:min-h-90"
|
||||
```
|
||||
|
||||
## Content Width Pattern
|
||||
|
||||
All section content must follow this structure:
|
||||
|
||||
```tsx
|
||||
<section aria-label={ariaLabel || "Section name"} className="w-full py-20">
|
||||
<div className="w-content-width mx-auto">
|
||||
{/* content */}
|
||||
</div>
|
||||
</section>
|
||||
```
|
||||
|
||||
**Rules:**
|
||||
- Section: `w-full py-20` (full width with vertical padding)
|
||||
- Inner div: `w-content-width mx-auto` (centered content with max width)
|
||||
- `w-content-width` is controlled by ThemeProvider (small/medium/large)
|
||||
|
||||
**Exceptions:**
|
||||
- Heroes and footers do NOT use `py-20` (they have custom spacing)
|
||||
- Full-bleed sections may skip inner wrapper
|
||||
|
||||
## Vertical Spacing
|
||||
|
||||
**Standard sections:**
|
||||
```tsx
|
||||
className="w-full py-20"
|
||||
```
|
||||
|
||||
**Exceptions (NO py-20):**
|
||||
- Hero sections (custom spacing)
|
||||
- Footer sections (custom spacing)
|
||||
- Full-bleed sections with background colors
|
||||
|
||||
## Text Constraints
|
||||
|
||||
For button text and short labels:
|
||||
```tsx
|
||||
{
|
||||
"text": {
|
||||
"required": true,
|
||||
"example": "Get Started",
|
||||
"minChars": 2,
|
||||
"maxChars": 15
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Button text rules:**
|
||||
- Minimum: 2 characters
|
||||
- Maximum: 15 characters
|
||||
- Single-line only (no multiline support)
|
||||
|
||||
## Section Structure Pattern
|
||||
|
||||
```tsx
|
||||
<section
|
||||
aria-label={ariaLabel || "Default section label"}
|
||||
className={cls(
|
||||
"relative py-20",
|
||||
useInvertedBackground === "invertCard"
|
||||
? "w-content-width-expanded mx-auto rounded-theme-capped bg-foreground"
|
||||
: "w-full",
|
||||
useInvertedBackground === "invertDefault" && "bg-foreground",
|
||||
className
|
||||
)}
|
||||
>
|
||||
<div className="w-content-width mx-auto">
|
||||
<TextBox
|
||||
title={title}
|
||||
description={description}
|
||||
tag={tag}
|
||||
tagIcon={tagIcon}
|
||||
buttons={buttons}
|
||||
textboxLayout={textboxLayout}
|
||||
useInvertedBackground={useInvertedBackground}
|
||||
// ... className overrides
|
||||
/>
|
||||
|
||||
{/* Section-specific content */}
|
||||
</div>
|
||||
</section>
|
||||
```
|
||||
|
||||
**Key Pattern Notes:**
|
||||
- `useInvertedBackground` is a required prop: `"noInvert" | "invertDefault" | "invertCard"`
|
||||
- `"invertCard"` creates a card-style section with expanded width and rounded corners
|
||||
- `"invertDefault"` creates a full-width inverted section
|
||||
- `"noInvert"` is the standard section with no background
|
||||
- Always use explicit string equality checks (not truthy/falsy)
|
||||
- Text colors must check for both `"invertDefault"` and `"invertCard"` modes
|
||||
|
||||
## Implementation Checklist
|
||||
|
||||
### Core Requirements
|
||||
- [ ] Add `"use client"` directive if needed (hooks, interactivity)
|
||||
- [ ] Use explicit prop interfaces (no over-complicated types)
|
||||
- [ ] Set appropriate defaults for optional props
|
||||
- [ ] Add `displayName` for debugging
|
||||
- [ ] Wrap in `React.memo()` for performance
|
||||
- [ ] Use semantic HTML tags (`<section>`, `<button>`, etc.)
|
||||
|
||||
### Customizability
|
||||
- [ ] Provide className props for all major elements
|
||||
- [ ] Use `cls()` utility for class composition
|
||||
- [ ] Set base styles with override capability
|
||||
- [ ] Follow naming convention (className, containerClassName, [element]ClassName)
|
||||
|
||||
### Responsive Design
|
||||
- [ ] Mobile-first styles (no prefix)
|
||||
- [ ] Desktop styles with `md:` prefix only
|
||||
- [ ] Avoid `lg:`, `xl:`, `2xl:` for layout changes
|
||||
- [ ] Use `w-content-width mx-auto` pattern
|
||||
|
||||
### Naming Conventions
|
||||
- [ ] Section components: Use `title` and `description`
|
||||
- [ ] Button components: Use `text`
|
||||
- [ ] Button configs: Use `text` (not variant - controlled by theme)
|
||||
- [ ] Consistent naming across similar component types
|
||||
|
||||
### Structure
|
||||
- [ ] Required props first in interface
|
||||
- [ ] Optional props with defaults after
|
||||
- [ ] Empty string defaults for className props
|
||||
- [ ] Document default values clearly
|
||||
499
docs/PREVIEW_PAGE_STANDARDS.md
Normal file
499
docs/PREVIEW_PAGE_STANDARDS.md
Normal file
@@ -0,0 +1,499 @@
|
||||
# Preview Page Standards
|
||||
|
||||
This document outlines how to create preview pages for components in `/app/components/`.
|
||||
|
||||
## Purpose
|
||||
|
||||
Preview pages allow developers and AI builders to:
|
||||
- See components in isolation
|
||||
- Test component behavior and styling
|
||||
- Verify responsive design
|
||||
- Experiment with different prop configurations
|
||||
- Ensure smooth scrolling and theme integration
|
||||
|
||||
## File Structure
|
||||
|
||||
### Location Pattern
|
||||
|
||||
```
|
||||
/app/components/
|
||||
├── sections/
|
||||
│ ├── hero/
|
||||
│ │ ├── billboard/
|
||||
│ │ │ └── page.tsx // Preview for HeroBillboard
|
||||
│ │ ├── split/
|
||||
│ │ │ └── page.tsx // Preview for HeroSplit
|
||||
│ ├── feature/
|
||||
│ │ ├── card-one/
|
||||
│ │ │ └── page.tsx // Preview for FeatureCardOne
|
||||
├── buttons/
|
||||
│ ├── text-stagger/
|
||||
│ │ └── page.tsx // Preview for ButtonTextStagger
|
||||
└── page.tsx // Main components index
|
||||
```
|
||||
|
||||
**Pattern:** `/app/components/[category]/[component-name]/page.tsx`
|
||||
|
||||
**Component name formatting:**
|
||||
- Use kebab-case for folder names
|
||||
- `HeroBillboard` → `hero/billboard/`
|
||||
- `FeatureCardOne` → `feature/card-one/`
|
||||
- `ButtonTextStagger` → `buttons/text-stagger/`
|
||||
|
||||
## Preview Page Template
|
||||
|
||||
### Basic Template (Non-Section Components)
|
||||
|
||||
```tsx
|
||||
"use client";
|
||||
|
||||
import React from "react";
|
||||
import ReactLenis from "lenis/react";
|
||||
import { ThemeProvider } from "@/providers/themeProvider/ThemeProvider";
|
||||
import ComponentName from "@/components/category/ComponentName";
|
||||
|
||||
export default function ComponentPreviewPage() {
|
||||
return (
|
||||
<ThemeProvider
|
||||
defaultButtonVariant="text-stagger"
|
||||
defaultTextAnimation="entrance-slide"
|
||||
borderRadius="rounded"
|
||||
contentWidth="medium"
|
||||
sizing="medium"
|
||||
background="plain"
|
||||
cardStyle="glass-flat"
|
||||
primaryButtonStyle="gradient"
|
||||
secondaryButtonStyle="glass"
|
||||
headingFontWeight="medium"
|
||||
>
|
||||
<ReactLenis root>
|
||||
<ComponentName
|
||||
// Add realistic props here
|
||||
text="Example"
|
||||
onClick={() => console.log("clicked")}
|
||||
/>
|
||||
</ReactLenis>
|
||||
</ThemeProvider>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### Section Component Template
|
||||
|
||||
```tsx
|
||||
"use client";
|
||||
|
||||
import React from "react";
|
||||
import ReactLenis from "lenis/react";
|
||||
import { ThemeProvider } from "@/providers/themeProvider/ThemeProvider";
|
||||
import SectionName from "@/components/sections/category/SectionName";
|
||||
|
||||
export default function SectionPreviewPage() {
|
||||
return (
|
||||
<ThemeProvider
|
||||
defaultButtonVariant="icon-arrow"
|
||||
defaultTextAnimation="entrance-slide"
|
||||
borderRadius="pill"
|
||||
contentWidth="medium"
|
||||
sizing="medium"
|
||||
background="animatedGrid"
|
||||
cardStyle="glass-flat"
|
||||
primaryButtonStyle="gradient"
|
||||
secondaryButtonStyle="glass"
|
||||
headingFontWeight="medium"
|
||||
>
|
||||
<ReactLenis root>
|
||||
<SectionName
|
||||
title="Preview Section Title"
|
||||
description="This is a preview of the section component with example content."
|
||||
buttons={[
|
||||
{ text: "Get Started", href: "#" },
|
||||
{ text: "Learn More", onClick: () => console.log("Learn more") }
|
||||
]}
|
||||
// Add section-specific props
|
||||
/>
|
||||
</ReactLenis>
|
||||
</ThemeProvider>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### CardStack Section Template
|
||||
|
||||
```tsx
|
||||
"use client";
|
||||
|
||||
import React from "react";
|
||||
import ReactLenis from "lenis/react";
|
||||
import { ThemeProvider } from "@/providers/themeProvider/ThemeProvider";
|
||||
import { Package, Zap, Shield, Sparkles } from "lucide-react";
|
||||
import FeatureCardOne from "@/components/sections/feature/FeatureCardOne";
|
||||
|
||||
export default function FeatureCardOnePreviewPage() {
|
||||
const features = [
|
||||
{
|
||||
icon: Package,
|
||||
title: "Feature One",
|
||||
description: "Lorem ipsum dolor sit amet, consectetur adipiscing elit."
|
||||
},
|
||||
{
|
||||
icon: Zap,
|
||||
title: "Feature Two",
|
||||
description: "Sed do eiusmod tempor incididunt ut labore et dolore."
|
||||
},
|
||||
{
|
||||
icon: Shield,
|
||||
title: "Feature Three",
|
||||
description: "Ut enim ad minim veniam, quis nostrud exercitation."
|
||||
},
|
||||
{
|
||||
icon: Sparkles,
|
||||
title: "Feature Four",
|
||||
description: "Duis aute irure dolor in reprehenderit in voluptate."
|
||||
}
|
||||
];
|
||||
|
||||
return (
|
||||
<ThemeProvider
|
||||
defaultButtonVariant="icon-arrow"
|
||||
defaultTextAnimation="entrance-slide"
|
||||
borderRadius="rounded"
|
||||
contentWidth="medium"
|
||||
sizing="medium"
|
||||
background="animatedGrid"
|
||||
cardStyle="glass-flat"
|
||||
primaryButtonStyle="gradient"
|
||||
secondaryButtonStyle="glass"
|
||||
headingFontWeight="medium"
|
||||
>
|
||||
<ReactLenis root>
|
||||
<FeatureCardOne
|
||||
features={features}
|
||||
gridVariant="uniform-all-items-equal"
|
||||
textboxLayout="default"
|
||||
title="Our Features"
|
||||
description="Discover what makes us unique"
|
||||
tag="Features"
|
||||
tagIcon={Sparkles}
|
||||
buttons={[
|
||||
{ text: "View All", href: "#" }
|
||||
]}
|
||||
/>
|
||||
</ReactLenis>
|
||||
</ThemeProvider>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## Critical Requirements
|
||||
|
||||
### Wrapper Order
|
||||
|
||||
**MUST follow this order:**
|
||||
|
||||
```tsx
|
||||
<ThemeProvider>
|
||||
<ReactLenis root>
|
||||
<Component />
|
||||
</ReactLenis>
|
||||
</ThemeProvider>
|
||||
```
|
||||
|
||||
**❌ WRONG:**
|
||||
```tsx
|
||||
<ReactLenis root>
|
||||
<ThemeProvider>
|
||||
<Component />
|
||||
</ThemeProvider>
|
||||
</ReactLenis>
|
||||
```
|
||||
|
||||
ReactLenis must be **inside** ThemeProvider, not outside.
|
||||
|
||||
### "use client" Directive
|
||||
|
||||
All preview pages must include `"use client"` at the top:
|
||||
|
||||
```tsx
|
||||
"use client";
|
||||
|
||||
import React from "react";
|
||||
// ...
|
||||
```
|
||||
|
||||
This is required because:
|
||||
- ReactLenis uses client-side hooks
|
||||
- ThemeProvider uses React Context
|
||||
- Components may use interactive features
|
||||
|
||||
### ReactLenis Root Prop
|
||||
|
||||
Always include the `root` prop:
|
||||
|
||||
```tsx
|
||||
<ReactLenis root>
|
||||
{/* components */}
|
||||
</ReactLenis>
|
||||
```
|
||||
|
||||
This enables page-wide smooth scrolling.
|
||||
|
||||
## Theme Configuration
|
||||
|
||||
### Recommended Defaults
|
||||
|
||||
**For most previews:**
|
||||
```tsx
|
||||
<ThemeProvider
|
||||
defaultButtonVariant="icon-arrow"
|
||||
defaultTextAnimation="entrance-slide"
|
||||
borderRadius="rounded"
|
||||
contentWidth="medium"
|
||||
sizing="medium"
|
||||
background="animatedGrid"
|
||||
cardStyle="glass-flat"
|
||||
primaryButtonStyle="gradient"
|
||||
secondaryButtonStyle="glass"
|
||||
headingFontWeight="medium"
|
||||
>
|
||||
```
|
||||
|
||||
**For button previews:**
|
||||
```tsx
|
||||
<ThemeProvider
|
||||
defaultButtonVariant="text-stagger" // Match button being previewed
|
||||
defaultTextAnimation="entrance-slide"
|
||||
borderRadius="pill"
|
||||
contentWidth="medium"
|
||||
sizing="medium"
|
||||
background="plain"
|
||||
cardStyle="glass-flat"
|
||||
primaryButtonStyle="gradient"
|
||||
secondaryButtonStyle="glass"
|
||||
headingFontWeight="medium"
|
||||
>
|
||||
```
|
||||
|
||||
**For hero previews:**
|
||||
```tsx
|
||||
<ThemeProvider
|
||||
defaultButtonVariant="icon-arrow"
|
||||
defaultTextAnimation="entrance-slide"
|
||||
borderRadius="rounded"
|
||||
contentWidth="large" // Wider for heroes
|
||||
sizing="large" // Larger sizing for heroes
|
||||
background="aurora" // Visual background
|
||||
cardStyle="glass-flat"
|
||||
primaryButtonStyle="gradient"
|
||||
secondaryButtonStyle="glass"
|
||||
headingFontWeight="semibold" // Bolder for heroes
|
||||
>
|
||||
```
|
||||
|
||||
### When to Customize
|
||||
|
||||
Customize theme settings when:
|
||||
- Testing different button variants
|
||||
- Showcasing card styles
|
||||
- Demonstrating responsive behavior
|
||||
- Highlighting specific theme features
|
||||
|
||||
## Realistic Props
|
||||
|
||||
### Use Realistic Content
|
||||
|
||||
**✅ GOOD:**
|
||||
```tsx
|
||||
<HeroBillboard
|
||||
title="Build Amazing Websites Faster"
|
||||
description="Create stunning, responsive websites with our modern component library. Ship faster, iterate quicker."
|
||||
buttons={[
|
||||
{ text: "Get Started", href: "/signup" },
|
||||
{ text: "View Demo", onClick: () => window.open("/demo") }
|
||||
]}
|
||||
/>
|
||||
```
|
||||
|
||||
**❌ BAD:**
|
||||
```tsx
|
||||
<HeroBillboard
|
||||
title="Test"
|
||||
description="Test description"
|
||||
buttons={[
|
||||
{ text: "Click", href: "#" }
|
||||
]}
|
||||
/>
|
||||
```
|
||||
|
||||
### Sample Data Patterns
|
||||
|
||||
**Features:**
|
||||
```tsx
|
||||
const features = [
|
||||
{
|
||||
icon: Package,
|
||||
title: "Fast Shipping",
|
||||
description: "Get your order delivered within 2-3 business days."
|
||||
},
|
||||
// ... more features
|
||||
];
|
||||
```
|
||||
|
||||
**Products:**
|
||||
```tsx
|
||||
const products = [
|
||||
{
|
||||
title: "Premium Headphones",
|
||||
description: "Wireless noise-cancelling headphones with 30-hour battery life.",
|
||||
price: "$299",
|
||||
image: "/images/headphones.jpg"
|
||||
},
|
||||
// ... more products
|
||||
];
|
||||
```
|
||||
|
||||
**Testimonials:**
|
||||
```tsx
|
||||
const testimonials = [
|
||||
{
|
||||
name: "Sarah Johnson",
|
||||
role: "CEO, TechCorp",
|
||||
content: "This component library transformed our development workflow. Highly recommend!",
|
||||
image: "/images/avatar-1.jpg",
|
||||
rating: 5
|
||||
},
|
||||
// ... more testimonials
|
||||
];
|
||||
```
|
||||
|
||||
## Multiple Sections Example
|
||||
|
||||
Preview pages can show multiple components together:
|
||||
|
||||
```tsx
|
||||
"use client";
|
||||
|
||||
import React from "react";
|
||||
import ReactLenis from "lenis/react";
|
||||
import { ThemeProvider } from "@/providers/themeProvider/ThemeProvider";
|
||||
import HeroBillboard from "@/components/sections/hero/HeroBillboard";
|
||||
import FeatureCardOne from "@/components/sections/feature/FeatureCardOne";
|
||||
import Footer from "@/components/sections/footer/FooterBase";
|
||||
|
||||
export default function FullPagePreview() {
|
||||
return (
|
||||
<ThemeProvider
|
||||
defaultButtonVariant="icon-arrow"
|
||||
defaultTextAnimation="entrance-slide"
|
||||
borderRadius="rounded"
|
||||
contentWidth="medium"
|
||||
sizing="medium"
|
||||
background="animatedGrid"
|
||||
cardStyle="glass-flat"
|
||||
primaryButtonStyle="gradient"
|
||||
secondaryButtonStyle="glass"
|
||||
headingFontWeight="medium"
|
||||
>
|
||||
<ReactLenis root>
|
||||
<HeroBillboard {...heroProps} />
|
||||
<FeatureCardOne {...featureProps} />
|
||||
<Footer {...footerProps} />
|
||||
</ReactLenis>
|
||||
</ThemeProvider>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## Preview Page Checklist
|
||||
|
||||
### File Setup
|
||||
- [ ] Create in correct location: `/app/components/[category]/[component-name]/page.tsx`
|
||||
- [ ] Use kebab-case for folder names
|
||||
- [ ] Add `"use client"` directive at top
|
||||
- [ ] Export default function with descriptive name
|
||||
|
||||
### Wrapper Configuration
|
||||
- [ ] Wrap in ThemeProvider (outer)
|
||||
- [ ] Wrap in ReactLenis with `root` prop (inner)
|
||||
- [ ] Correct order: ThemeProvider > ReactLenis > Component
|
||||
- [ ] Import both wrappers
|
||||
|
||||
### Component Props
|
||||
- [ ] Use realistic, representative content
|
||||
- [ ] Include all required props
|
||||
- [ ] Test with typical prop combinations
|
||||
- [ ] Use proper TypeScript types (no `any`)
|
||||
|
||||
### Theme Settings
|
||||
- [ ] Configure appropriate theme settings for component type
|
||||
- [ ] Use sensible defaults that showcase the component well
|
||||
- [ ] Test with different theme configurations if needed
|
||||
|
||||
### Quality Checks
|
||||
- [ ] Component renders without errors
|
||||
- [ ] Smooth scrolling works
|
||||
- [ ] Responsive design functions correctly
|
||||
- [ ] Animations trigger properly
|
||||
- [ ] No console warnings or errors
|
||||
|
||||
## Common Patterns
|
||||
|
||||
### Background for Preview
|
||||
|
||||
If the component needs a background color to be visible:
|
||||
|
||||
```tsx
|
||||
<ReactLenis root>
|
||||
<div className="min-h-screen bg-background">
|
||||
<Component {...props} />
|
||||
</div>
|
||||
</ReactLenis>
|
||||
```
|
||||
|
||||
### Centered Preview
|
||||
|
||||
For small components that need centering:
|
||||
|
||||
```tsx
|
||||
<ReactLenis root>
|
||||
<div className="min-h-screen flex items-center justify-center bg-background">
|
||||
<Component {...props} />
|
||||
</div>
|
||||
</ReactLenis>
|
||||
```
|
||||
|
||||
### Multiple Variants
|
||||
|
||||
Show multiple variants of the same component:
|
||||
|
||||
```tsx
|
||||
<ReactLenis root>
|
||||
<div className="min-h-screen bg-background py-20 space-y-20">
|
||||
<ComponentName variant="primary" {...props} />
|
||||
<ComponentName variant="secondary" {...props} />
|
||||
<ComponentName variant="ghost" {...props} />
|
||||
</div>
|
||||
</ReactLenis>
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
### ✅ DO:
|
||||
|
||||
- Use realistic, production-quality content
|
||||
- Test responsive behavior
|
||||
- Include all ThemeProvider configuration
|
||||
- Use ReactLenis for smooth scrolling
|
||||
- Follow naming conventions (kebab-case folders)
|
||||
- Include required props only (let optional props use defaults)
|
||||
- Test with different theme settings
|
||||
|
||||
### ❌ DO NOT:
|
||||
|
||||
- Use placeholder text like "Lorem ipsum" (use realistic content)
|
||||
- Skip ThemeProvider or ReactLenis
|
||||
- Put ReactLenis outside ThemeProvider
|
||||
- Use hardcoded colors that break theming
|
||||
- Create overly complex multi-component demos (keep focused)
|
||||
- Forget "use client" directive
|
||||
- Use incorrect folder structure
|
||||
145
docs/README.md
Normal file
145
docs/README.md
Normal file
@@ -0,0 +1,145 @@
|
||||
# Component Library Documentation
|
||||
|
||||
This directory contains focused documentation for building components in this AI-optimized component library.
|
||||
|
||||
## Documentation Files
|
||||
|
||||
### 📋 [COMPONENT_IMPLEMENTATION.md](./COMPONENT_IMPLEMENTATION.md)
|
||||
**Core component implementation patterns**
|
||||
|
||||
Load when: Creating or modifying component code
|
||||
|
||||
Covers:
|
||||
- Component structure template
|
||||
- Prop structure & defaults
|
||||
- Naming conventions
|
||||
- Component customizability
|
||||
- Type safety
|
||||
- Responsive design
|
||||
- Content width pattern
|
||||
- Implementation checklist
|
||||
|
||||
---
|
||||
|
||||
### 🎴 [CARDSTACK_SECTIONS.md](./CARDSTACK_SECTIONS.md)
|
||||
**CardStack-based section components**
|
||||
|
||||
Load when: Creating Feature, Product, Pricing, Testimonial, Team, Blog, or Metrics sections
|
||||
|
||||
Covers:
|
||||
- CardStack pattern overview
|
||||
- Grid variants (10+ layouts)
|
||||
- Height control (`uniformGridCustomHeightClasses`)
|
||||
- Carousel modes
|
||||
- TextBox integration
|
||||
- Button system
|
||||
- Animation types
|
||||
- Complete examples & checklist
|
||||
|
||||
---
|
||||
|
||||
### 🎨 [THEME_AND_STYLING.md](./THEME_AND_STYLING.md)
|
||||
**Theme system and styling patterns**
|
||||
|
||||
Load when: Setting up themes, working with colors, or styling components
|
||||
|
||||
Covers:
|
||||
- Theme Provider configuration
|
||||
- Color theming (CSS variables)
|
||||
- Inverted background pattern
|
||||
- Content width pattern
|
||||
- Card styling
|
||||
- Border radius patterns
|
||||
- Button styling classes
|
||||
- Styling checklist
|
||||
|
||||
---
|
||||
|
||||
### ♿ [ACCESSIBILITY.md](./ACCESSIBILITY.md)
|
||||
**Accessibility (a11y) standards**
|
||||
|
||||
Load when: Adding interactive elements, media, or sections
|
||||
|
||||
Covers:
|
||||
- Interactive components (buttons, links)
|
||||
- Media components (images, videos)
|
||||
- Section components (semantic HTML)
|
||||
- Form components
|
||||
- Focus management
|
||||
- Keyboard navigation
|
||||
- ARIA roles
|
||||
- Accessibility checklist
|
||||
|
||||
---
|
||||
|
||||
### 📚 [REGISTRY_STANDARDS.md](./REGISTRY_STANDARDS.md)
|
||||
**Registry documentation rules**
|
||||
|
||||
Load when: Adding or updating component entries in `registry.json`
|
||||
|
||||
Covers:
|
||||
- Registry structure
|
||||
- Component entry format
|
||||
- Field descriptions
|
||||
- propsSchema rules
|
||||
- Constraints format
|
||||
- Usage examples
|
||||
- What to include/exclude
|
||||
- Validation checklist
|
||||
|
||||
---
|
||||
|
||||
### 📄 [PREVIEW_PAGE_STANDARDS.md](./PREVIEW_PAGE_STANDARDS.md)
|
||||
**Preview page setup and patterns**
|
||||
|
||||
Load when: Creating preview pages in `/app/components/`
|
||||
|
||||
Covers:
|
||||
- File structure & location
|
||||
- Preview page templates
|
||||
- Wrapper order (ThemeProvider > ReactLenis)
|
||||
- Theme configuration
|
||||
- Realistic props
|
||||
- Multiple sections example
|
||||
- Preview page checklist
|
||||
|
||||
---
|
||||
|
||||
## Quick Reference
|
||||
|
||||
### When creating a new component:
|
||||
1. Load `COMPONENT_IMPLEMENTATION.md` for structure
|
||||
2. Load `ACCESSIBILITY.md` for a11y requirements
|
||||
3. Load `THEME_AND_STYLING.md` for styling patterns
|
||||
4. Load `CARDSTACK_SECTIONS.md` if using CardStack
|
||||
|
||||
### When updating the registry:
|
||||
1. Load `REGISTRY_STANDARDS.md` only
|
||||
|
||||
### When creating a preview page:
|
||||
1. Load `PREVIEW_PAGE_STANDARDS.md` only
|
||||
|
||||
### When modifying themes:
|
||||
1. Load `THEME_AND_STYLING.md` only
|
||||
|
||||
---
|
||||
|
||||
## Documentation Principles
|
||||
|
||||
These docs are optimized for:
|
||||
- **AI builders** (Lovable, V0, Claude Code)
|
||||
- **Focused context** (load only what you need)
|
||||
- **Quick reference** (checklists and examples)
|
||||
- **Consistency** (standardized patterns across all components)
|
||||
|
||||
Each file is:
|
||||
- **Single-purpose** - covers one concern thoroughly
|
||||
- **Self-contained** - minimal cross-references
|
||||
- **Example-driven** - shows good/bad patterns
|
||||
- **Checklist-equipped** - actionable validation steps
|
||||
|
||||
---
|
||||
|
||||
## Legacy Documentation
|
||||
|
||||
The original `COMPONENT_STANDARDS.md` file has been split into these focused documents. Refer to these new files for all component development.
|
||||
487
docs/REGISTRY_STANDARDS.md
Normal file
487
docs/REGISTRY_STANDARDS.md
Normal file
@@ -0,0 +1,487 @@
|
||||
# Registry Documentation Standards
|
||||
|
||||
This document outlines how to document components in `registry.json` for AI website builders.
|
||||
|
||||
## Registry Structure
|
||||
|
||||
The registry is organized into two main sections:
|
||||
|
||||
```json
|
||||
{
|
||||
"componentRegistry": {
|
||||
"button": [...],
|
||||
"text": [...],
|
||||
"navbar": [...],
|
||||
"layout": [...]
|
||||
},
|
||||
"sectionRegistry": {
|
||||
"hero": [...],
|
||||
"about": [...],
|
||||
"feature": [...],
|
||||
"testimonial": [...],
|
||||
"pricing": [...],
|
||||
"team": [...],
|
||||
"product": [...],
|
||||
"metrics": [...],
|
||||
"blog": [...],
|
||||
"footer": [...],
|
||||
"contact": [...],
|
||||
"faq": [...],
|
||||
"socialProof": [...]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**componentRegistry** - Base/utility components (buttons, text, navbar, CardStack, etc.)
|
||||
**sectionRegistry** - Section components (hero, about, feature, etc.)
|
||||
|
||||
## Component Entry Format
|
||||
|
||||
Every component entry must follow this structure:
|
||||
|
||||
```json
|
||||
{
|
||||
"import": "import ComponentName from '@/components/category/ComponentName';",
|
||||
"name": "ComponentName",
|
||||
"path": "@/components/category/ComponentName",
|
||||
"description": "Brief one-line description of what the component is.",
|
||||
"details": "Longer description of when to use it, behavior, and constraints.",
|
||||
"constraints": {
|
||||
"textRules": {
|
||||
"text": {
|
||||
"required": true,
|
||||
"example": "Example text",
|
||||
"minChars": 2,
|
||||
"maxChars": 15
|
||||
}
|
||||
}
|
||||
},
|
||||
"propsSchema": {
|
||||
"text": "string",
|
||||
"onClick?": "() => void",
|
||||
"className?": "string",
|
||||
"disabled?": "boolean (default: false)",
|
||||
"ariaLabel?": "string",
|
||||
"type?": "'button' | 'submit' | 'reset' (default: 'button')"
|
||||
},
|
||||
"usage": "<ComponentName text=\"Example\" onClick={() => console.log('clicked')} />"
|
||||
}
|
||||
```
|
||||
|
||||
## Field Descriptions
|
||||
|
||||
### import
|
||||
The exact import statement AI should use.
|
||||
|
||||
**Format:**
|
||||
```json
|
||||
"import": "import ComponentName from '@/components/category/ComponentName';"
|
||||
```
|
||||
|
||||
**Examples:**
|
||||
```json
|
||||
"import": "import HeroBillboard from '@/components/sections/hero/HeroBillboard';"
|
||||
"import": "import ButtonTextStagger from '@/components/button/text-stagger/ButtonTextStagger';"
|
||||
"import": "import CardStack from '@/components/cardStack/CardStack';"
|
||||
```
|
||||
|
||||
### name
|
||||
Component name exactly as exported.
|
||||
|
||||
**Format:**
|
||||
```json
|
||||
"name": "ComponentName"
|
||||
```
|
||||
|
||||
### path
|
||||
File path without extension.
|
||||
|
||||
**Format:**
|
||||
```json
|
||||
"path": "@/components/category/ComponentName"
|
||||
```
|
||||
|
||||
### description
|
||||
One-line summary of what the component is. Focus on visual/behavioral characteristics.
|
||||
|
||||
**Format:** 1 sentence, under 100 characters
|
||||
|
||||
**Good Examples:**
|
||||
```json
|
||||
"description": "CTA button with character stagger animation on hover."
|
||||
"description": "Full-width hero section with centered text and billboard layout."
|
||||
"description": "Feature section with grid or carousel layout for feature cards."
|
||||
```
|
||||
|
||||
**Bad Examples:**
|
||||
```json
|
||||
"description": "A really cool button component with lots of features and animations that can be used anywhere." // Too verbose
|
||||
"description": "Button" // Too vague
|
||||
```
|
||||
|
||||
### details
|
||||
Longer description covering:
|
||||
- When to use it
|
||||
- Key behavior notes
|
||||
- Important constraints
|
||||
|
||||
**Format:** 2-4 sentences
|
||||
|
||||
**Good Example:**
|
||||
```json
|
||||
"details": "Use for primary or secondary CTAs where subtle text motion adds emphasis. On hover, the label's characters animate in sequence (stagger). Includes background styling and supports all standard button props."
|
||||
```
|
||||
|
||||
**Bad Example:**
|
||||
```json
|
||||
"details": "This is a button that you can click and it will do something when you click it. It has animations." // Too obvious, not helpful
|
||||
```
|
||||
|
||||
### constraints
|
||||
|
||||
Defines text length constraints for string props.
|
||||
|
||||
**Format:**
|
||||
```json
|
||||
"constraints": {
|
||||
"textRules": {
|
||||
"propName": {
|
||||
"required": true,
|
||||
"example": "Example value",
|
||||
"minChars": 2,
|
||||
"maxChars": 15
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Common Patterns:**
|
||||
|
||||
**Button text:**
|
||||
```json
|
||||
"constraints": {
|
||||
"textRules": {
|
||||
"text": {
|
||||
"required": true,
|
||||
"example": "Get Started",
|
||||
"minChars": 2,
|
||||
"maxChars": 15
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Section titles and descriptions:**
|
||||
```json
|
||||
"constraints": {
|
||||
"textRules": {
|
||||
"title": {
|
||||
"required": true,
|
||||
"example": "Welcome to Our Platform",
|
||||
"minChars": 5,
|
||||
"maxChars": 60
|
||||
},
|
||||
"description": {
|
||||
"required": true,
|
||||
"example": "Build amazing websites with our component library",
|
||||
"minChars": 10,
|
||||
"maxChars": 200
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### propsSchema
|
||||
|
||||
Documents all component props with types and defaults.
|
||||
|
||||
**Format Rules:**
|
||||
|
||||
**Required props:**
|
||||
```json
|
||||
"propName": "type"
|
||||
```
|
||||
|
||||
**Optional props:**
|
||||
```json
|
||||
"propName?": "type"
|
||||
```
|
||||
|
||||
**Optional props with defaults:**
|
||||
```json
|
||||
"propName?": "type (default: value)"
|
||||
```
|
||||
|
||||
**Examples:**
|
||||
|
||||
**Simple types:**
|
||||
```json
|
||||
"text": "string",
|
||||
"count": "number",
|
||||
"enabled": "boolean"
|
||||
```
|
||||
|
||||
**Functions:**
|
||||
```json
|
||||
"onClick?": "() => void",
|
||||
"onChange?": "(value: string) => void",
|
||||
"onSubmit?": "(data: FormData) => Promise<void>"
|
||||
```
|
||||
|
||||
**Union types:**
|
||||
```json
|
||||
"type?": "'button' | 'submit' | 'reset' (default: 'button')",
|
||||
"variant?": "'primary' | 'secondary' | 'ghost'",
|
||||
"size?": "'sm' | 'md' | 'lg' (default: 'md')"
|
||||
```
|
||||
|
||||
**Array types:**
|
||||
```json
|
||||
"items": "Array<{ title: string; description: string }>",
|
||||
"buttons?": "ButtonConfig[]",
|
||||
"features": "Feature[]"
|
||||
```
|
||||
|
||||
**Complex types:**
|
||||
```json
|
||||
"gridVariant": "'uniform-all-items-equal' | 'two-columns-alternating-heights' | 'asymmetric-60-wide-40-narrow' | ...",
|
||||
"icon?": "LucideIcon",
|
||||
"image?": "string (URL or path)"
|
||||
```
|
||||
|
||||
**With defaults:**
|
||||
```json
|
||||
"className?": "string",
|
||||
"disabled?": "boolean (default: false)",
|
||||
"carouselMode?": "'auto' | 'buttons' (default: 'buttons')",
|
||||
"uniformGridCustomHeightClasses?": "string (default: 'min-h-80 2xl:min-h-90')"
|
||||
```
|
||||
|
||||
### usage
|
||||
|
||||
Single-line example showing typical implementation.
|
||||
|
||||
**Format:** One line, realistic props, valid JSX
|
||||
|
||||
**Good Examples:**
|
||||
|
||||
**Button:**
|
||||
```json
|
||||
"usage": "<ButtonTextStagger text=\"Get Started\" onClick={() => console.log('clicked')} />"
|
||||
```
|
||||
|
||||
**Section with minimal props:**
|
||||
```json
|
||||
"usage": "<HeroBillboard title=\"Welcome\" description=\"Start building today\" buttons={[{ text: 'Get Started', href: '/signup' }]} />"
|
||||
```
|
||||
|
||||
**CardStack section:**
|
||||
```json
|
||||
"usage": "<FeatureCardOne features={featuresData} gridVariant=\"uniform-all-items-equal\" textboxLayout=\"default\" title=\"Features\" description=\"Our key features\" />"
|
||||
```
|
||||
|
||||
**Bad Examples:**
|
||||
```json
|
||||
"usage": "<Component />" // Missing required props
|
||||
"usage": "<Component\n prop1=\"value\"\n prop2=\"value\"\n/>" // Multi-line, hard to read
|
||||
"usage": "Component({ text: 'Hi' })" // Not JSX
|
||||
```
|
||||
|
||||
## What to Include
|
||||
|
||||
### ✅ DO Include:
|
||||
|
||||
**Default values in propsSchema** - Critical for AI to generate correct code
|
||||
```json
|
||||
"disabled?": "boolean (default: false)",
|
||||
"type?": "'button' | 'submit' | 'reset' (default: 'button')"
|
||||
```
|
||||
|
||||
**Usage examples** - Helps AI understand context
|
||||
```json
|
||||
"usage": "<ButtonTextStagger text=\"Click me\" onClick={() => alert('Hi')} />"
|
||||
```
|
||||
|
||||
**Text constraints** - Min/max character limits
|
||||
```json
|
||||
"constraints": {
|
||||
"textRules": {
|
||||
"text": {
|
||||
"required": true,
|
||||
"example": "Get Started",
|
||||
"minChars": 2,
|
||||
"maxChars": 15
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Accurate descriptions** - Ensure description matches actual behavior
|
||||
```json
|
||||
"description": "CTA button with character stagger animation on hover.",
|
||||
"details": "Use for primary or secondary CTAs where subtle text motion adds emphasis."
|
||||
```
|
||||
|
||||
**Use case guidance** - When to use this vs alternatives
|
||||
```json
|
||||
"details": "Use for hero sections with centered content and billboard layout. Best for landing pages and marketing sites."
|
||||
```
|
||||
|
||||
## What NOT to Include
|
||||
|
||||
### ❌ DO NOT Include:
|
||||
|
||||
**Metadata field** - Unnecessary complexity
|
||||
```json
|
||||
// Don't do this:
|
||||
"metadata": {
|
||||
"category": "button",
|
||||
"version": "1.0.0",
|
||||
"author": "..."
|
||||
}
|
||||
```
|
||||
|
||||
**Verbose descriptions** - Keep concise and obvious
|
||||
```json
|
||||
// Don't do this:
|
||||
"description": "This is an amazing button component that you can use to create buttons with stagger animations that look really cool and modern and will make your website stand out from the competition."
|
||||
```
|
||||
|
||||
**Dependencies** - AI builders can infer from imports
|
||||
```json
|
||||
// Don't do this:
|
||||
"dependencies": ["lucide-react", "framer-motion"]
|
||||
```
|
||||
|
||||
**Over-documentation** - If it's obvious from the name, skip it
|
||||
```json
|
||||
// Don't do this:
|
||||
"details": "This is a button. You can click it. It accepts text to display on the button."
|
||||
```
|
||||
|
||||
**Implementation details** - Focus on usage, not internals
|
||||
```json
|
||||
// Don't do this:
|
||||
"details": "Uses GSAP ScrollTrigger with stagger: 0.1 and uses React.memo for performance and has displayName set to ButtonTextStagger."
|
||||
```
|
||||
|
||||
## Complete Examples
|
||||
|
||||
### Button Component
|
||||
|
||||
```json
|
||||
{
|
||||
"import": "import ButtonTextStagger from '@/components/button/text-stagger/ButtonTextStagger';",
|
||||
"name": "ButtonTextStagger",
|
||||
"path": "@/components/button/text-stagger/ButtonTextStagger",
|
||||
"description": "CTA button with character stagger animation on hover.",
|
||||
"details": "Use for primary or secondary CTAs where subtle text motion adds emphasis. On hover, the label's characters animate in sequence (stagger). Includes background styling.",
|
||||
"constraints": {
|
||||
"textRules": {
|
||||
"text": {
|
||||
"required": true,
|
||||
"example": "Get Started",
|
||||
"minChars": 2,
|
||||
"maxChars": 15
|
||||
}
|
||||
}
|
||||
},
|
||||
"propsSchema": {
|
||||
"text": "string",
|
||||
"onClick?": "() => void",
|
||||
"href?": "string",
|
||||
"className?": "string",
|
||||
"textClassName?": "string",
|
||||
"disabled?": "boolean (default: false)",
|
||||
"ariaLabel?": "string",
|
||||
"type?": "'button' | 'submit' | 'reset' (default: 'button')"
|
||||
},
|
||||
"usage": "<ButtonTextStagger text=\"Get Started\" onClick={() => console.log('clicked')} />"
|
||||
}
|
||||
```
|
||||
|
||||
### Section Component (CardStack-based)
|
||||
|
||||
```json
|
||||
{
|
||||
"import": "import FeatureCardOne from '@/components/sections/feature/FeatureCardOne';",
|
||||
"name": "FeatureCardOne",
|
||||
"path": "@/components/sections/feature/FeatureCardOne",
|
||||
"description": "Feature section with grid or carousel layout for feature cards.",
|
||||
"details": "Displays feature cards in a responsive grid (1-4 items) or carousel (5+ items). Supports TextBox header with multiple layout options. Each card includes icon, title, and description.",
|
||||
"constraints": {
|
||||
"textRules": {
|
||||
"title": {
|
||||
"required": true,
|
||||
"example": "Our Features",
|
||||
"minChars": 5,
|
||||
"maxChars": 60
|
||||
},
|
||||
"description": {
|
||||
"required": true,
|
||||
"example": "Discover what makes us unique",
|
||||
"minChars": 10,
|
||||
"maxChars": 200
|
||||
}
|
||||
}
|
||||
},
|
||||
"propsSchema": {
|
||||
"features": "Array<{ icon: LucideIcon; title: string; description: string }>",
|
||||
"gridVariant": "'uniform-all-items-equal' | 'two-columns-alternating-heights' | 'asymmetric-60-wide-40-narrow' | ...",
|
||||
"title": "string",
|
||||
"description": "string",
|
||||
"tag?": "string",
|
||||
"tagIcon?": "LucideIcon",
|
||||
"buttons?": "ButtonConfig[]",
|
||||
"textboxLayout": "'default' | 'split' | 'split-actions' | 'split-description'",
|
||||
"carouselMode?": "'auto' | 'buttons' (default: 'buttons')",
|
||||
"uniformGridCustomHeightClasses?": "string (default: 'min-h-80 2xl:min-h-90')",
|
||||
"ariaLabel?": "string",
|
||||
"className?": "string"
|
||||
},
|
||||
"usage": "<FeatureCardOne features={featuresData} gridVariant=\"uniform-all-items-equal\" textboxLayout=\"default\" title=\"Features\" description=\"Our key features\" />"
|
||||
}
|
||||
```
|
||||
|
||||
## Registry Validation Checklist
|
||||
|
||||
When adding a new component to the registry:
|
||||
|
||||
### Required Fields
|
||||
- [ ] `import` - Exact import statement
|
||||
- [ ] `name` - Component name
|
||||
- [ ] `path` - File path without extension
|
||||
- [ ] `description` - One-line summary
|
||||
- [ ] `details` - When to use, behavior, constraints
|
||||
- [ ] `propsSchema` - All props documented
|
||||
- [ ] `usage` - Single-line example
|
||||
|
||||
### Constraints (if applicable)
|
||||
- [ ] Add `textRules` for text props
|
||||
- [ ] Set `minChars` and `maxChars`
|
||||
- [ ] Provide realistic `example` values
|
||||
- [ ] Mark `required: true` for required props
|
||||
|
||||
### propsSchema Format
|
||||
- [ ] Required props: `"prop": "type"`
|
||||
- [ ] Optional props: `"prop?": "type"`
|
||||
- [ ] Defaults: `"prop?": "type (default: value)"`
|
||||
- [ ] All types match component implementation
|
||||
- [ ] Union types use single quotes inside double quotes
|
||||
|
||||
### Quality Checks
|
||||
- [ ] Description is concise (under 100 chars)
|
||||
- [ ] Details provide use case guidance
|
||||
- [ ] Usage example is valid JSX
|
||||
- [ ] Usage example shows realistic props
|
||||
- [ ] Default values documented in propsSchema
|
||||
- [ ] No over-documentation or verbose descriptions
|
||||
- [ ] No unnecessary metadata or dependencies
|
||||
|
||||
### Consistency Checks
|
||||
- [ ] Component name matches file name
|
||||
- [ ] Path matches actual file location
|
||||
- [ ] Import statement is correct
|
||||
- [ ] Props match actual component interface
|
||||
- [ ] Defaults in registry match component defaults
|
||||
- [ ] Naming follows conventions (title/description for sections, text for buttons)
|
||||
545
docs/THEME_AND_STYLING.md
Normal file
545
docs/THEME_AND_STYLING.md
Normal file
@@ -0,0 +1,545 @@
|
||||
# Theme and Styling Standards
|
||||
|
||||
This document covers the centralized theme system, color theming, and styling patterns used throughout the component library.
|
||||
|
||||
## Theme Provider System
|
||||
|
||||
All sections and components use a centralized ThemeProvider to maintain consistent styling across the entire site.
|
||||
|
||||
### Location & Setup
|
||||
|
||||
**Import:**
|
||||
```tsx
|
||||
import { ThemeProvider } from "@/providers/themeProvider/ThemeProvider";
|
||||
```
|
||||
|
||||
**Usage:** Wrap the entire app/page (not individual sections) in a **single** ThemeProvider:
|
||||
|
||||
```tsx
|
||||
export default function Page() {
|
||||
return (
|
||||
<ThemeProvider
|
||||
defaultButtonVariant="text-stagger"
|
||||
defaultTextAnimation="entrance-slide"
|
||||
borderRadius="rounded"
|
||||
contentWidth="medium"
|
||||
sizing="medium"
|
||||
background="animatedGrid"
|
||||
cardStyle="glass-flat"
|
||||
primaryButtonStyle="gradient"
|
||||
secondaryButtonStyle="glass"
|
||||
headingFontWeight="medium"
|
||||
>
|
||||
<HeroBillboard {...props} />
|
||||
<FeatureSection {...props} />
|
||||
<Footer {...props} />
|
||||
</ThemeProvider>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### Theme Configuration Options
|
||||
|
||||
#### defaultButtonVariant
|
||||
Controls the button style for ALL buttons in sections.
|
||||
|
||||
**Options:**
|
||||
- `"text-stagger"` - Character stagger animation on hover (default)
|
||||
- `"shift-hover"` - Text shifts on hover
|
||||
- `"icon-arrow"` - Text + right arrow icon
|
||||
- `"hover-magnetic"` - Cursor-tracking magnetic effect
|
||||
- `"hover-bubble"` - Expanding circle on hover
|
||||
- `"expand-hover"` - Width expansion on hover
|
||||
|
||||
#### defaultTextAnimation
|
||||
Controls the text animation type for ALL text in sections.
|
||||
|
||||
**Options:**
|
||||
- `"entrance-slide"` - Slide up from below (default)
|
||||
- `"reveal-blur"` - Blur to clear reveal
|
||||
- `"background-highlight"` - Background highlight effect
|
||||
|
||||
#### borderRadius
|
||||
Controls border radius for buttons and cards.
|
||||
|
||||
**Options:**
|
||||
- `"rounded"` - Standard rounded corners
|
||||
- `"pill"` - Fully rounded (pill shape)
|
||||
- `"sharp"` - No border radius
|
||||
|
||||
#### contentWidth
|
||||
Controls the max width of section content.
|
||||
|
||||
**Options:**
|
||||
- `"small"` - Narrow content width
|
||||
- `"medium"` - Standard content width (default)
|
||||
- `"large"` - Wide content width
|
||||
|
||||
Maps to `w-content-width` CSS class.
|
||||
|
||||
#### sizing
|
||||
Controls spacing and size scale throughout components.
|
||||
|
||||
**Options:**
|
||||
- `"small"` - Compact spacing
|
||||
- `"medium"` - Standard spacing (default)
|
||||
- `"large"` - Generous spacing
|
||||
|
||||
#### background
|
||||
Default background pattern for the page.
|
||||
|
||||
**Options:**
|
||||
- `"plain"` - Solid background color
|
||||
- `"animatedGrid"` - Animated grid pattern
|
||||
- `"aurora"` - Aurora gradient effect
|
||||
- `"dotGrid"` - Dot grid pattern
|
||||
- And more...
|
||||
|
||||
#### cardStyle
|
||||
Visual style for all card components.
|
||||
|
||||
**Options:**
|
||||
- `"glass-flat"` - Flat glass effect (default)
|
||||
- `"glass-depth"` - Glass with depth/shadow
|
||||
- `"glass-outline"` - Outlined glass
|
||||
- `"solid-accent-light"` - Solid light accent
|
||||
- `"outline"` - Simple outline
|
||||
- `"elevated"` - Elevated shadow effect
|
||||
- `"frosted"` - Frosted glass
|
||||
- And more...
|
||||
|
||||
#### primaryButtonStyle
|
||||
Style for primary buttons (first button in array).
|
||||
|
||||
**Options:**
|
||||
- `"gradient"` - Gradient background
|
||||
- `"solid"` - Solid color background
|
||||
- `"glass"` - Glass effect
|
||||
- `"outline"` - Outlined button
|
||||
|
||||
#### secondaryButtonStyle
|
||||
Style for secondary buttons (second+ button in array).
|
||||
|
||||
**Options:**
|
||||
- `"glass"` - Glass effect (default)
|
||||
- `"outline"` - Outlined button
|
||||
- `"solid"` - Solid color background
|
||||
- `"gradient"` - Gradient background
|
||||
|
||||
#### headingFontWeight
|
||||
Font weight for all headings.
|
||||
|
||||
**Options:**
|
||||
- `"normal"` - Regular weight
|
||||
- `"medium"` - Medium weight (default)
|
||||
- `"semibold"` - Semi-bold weight
|
||||
- `"bold"` - Bold weight
|
||||
|
||||
### Using Theme in Components
|
||||
|
||||
#### useTheme Hook
|
||||
|
||||
```tsx
|
||||
import { useTheme } from "@/providers/themeProvider/ThemeProvider";
|
||||
|
||||
const Component = () => {
|
||||
const theme = useTheme();
|
||||
|
||||
// Access theme properties:
|
||||
// - theme.defaultButtonVariant
|
||||
// - theme.defaultTextAnimation
|
||||
// - theme.borderRadius
|
||||
// - theme.cardStyle
|
||||
// - theme.contentWidth
|
||||
// - theme.sizing
|
||||
// - theme.background
|
||||
// - theme.primaryButtonStyle
|
||||
// - theme.secondaryButtonStyle
|
||||
// - theme.headingFontWeight
|
||||
|
||||
return <div>{/* component */}</div>;
|
||||
};
|
||||
```
|
||||
|
||||
#### TextBox Component Example
|
||||
|
||||
TextBox automatically applies theme defaults:
|
||||
|
||||
```tsx
|
||||
const TextBox = ({ type, buttons, ...props }: TextBoxProps) => {
|
||||
const theme = useTheme();
|
||||
|
||||
// Use default text animation from theme if not specified
|
||||
const animationType = type || theme.defaultTextAnimation;
|
||||
|
||||
// Button variant comes from theme
|
||||
const variant = theme.defaultButtonVariant;
|
||||
|
||||
// ...
|
||||
};
|
||||
```
|
||||
|
||||
### Important Rules
|
||||
|
||||
❌ **DO NOT:**
|
||||
- Specify button `variant` in section button configs (controlled by ThemeProvider)
|
||||
- Wrap individual sections in ThemeProvider (use ONE provider per site/page)
|
||||
- Override theme defaults unless explicitly required by component design
|
||||
|
||||
✅ **DO:**
|
||||
- Wrap the entire app/page in a single ThemeProvider
|
||||
- Let all sections inherit theme defaults automatically
|
||||
- Use `useTheme()` hook to access theme configuration
|
||||
- Document when components don't follow theme defaults (with clear reason)
|
||||
|
||||
## Color & Theming
|
||||
|
||||
### CSS Custom Properties
|
||||
|
||||
**Always use CSS custom properties for colors** to ensure theme consistency:
|
||||
|
||||
```tsx
|
||||
// ✅ CORRECT - Uses theme variables
|
||||
<div className="bg-background text-foreground">
|
||||
<button className="bg-foreground text-background">Click me</button>
|
||||
</div>
|
||||
|
||||
// ❌ WRONG - Hardcoded colors break theming
|
||||
<div className="bg-white text-black">
|
||||
<button className="bg-black text-white">Click me</button>
|
||||
</div>
|
||||
```
|
||||
|
||||
### Standard Color Variables
|
||||
|
||||
**Background & Text:**
|
||||
- `bg-background` - Main background color
|
||||
- `text-foreground` - Main text color
|
||||
- `bg-foreground` - Inverse background (for buttons, accents)
|
||||
- `text-background` - Inverse text (for text on dark backgrounds)
|
||||
|
||||
**Cards & Surfaces:**
|
||||
- `card` - Card/surface background with border (theme-aware)
|
||||
- Maps to different styles based on `theme.cardStyle`
|
||||
|
||||
**Buttons:**
|
||||
- `primary-button` - Primary button styling (index 0)
|
||||
- `secondary-button` - Secondary button styling (index 1+)
|
||||
|
||||
**Opacity Modifiers:**
|
||||
```tsx
|
||||
text-foreground/75 // 75% opacity
|
||||
text-background/50 // 50% opacity
|
||||
bg-foreground/10 // 10% opacity
|
||||
```
|
||||
|
||||
### When to Use Theme Colors
|
||||
|
||||
✅ **Always prefer theme variables:**
|
||||
- Backgrounds, text, borders, shadows
|
||||
- Ensures proper dark mode support
|
||||
- Allows theme customization
|
||||
- Maintains visual consistency
|
||||
|
||||
❌ **Only use hardcoded colors:**
|
||||
- Very specific one-off cases with clear justification
|
||||
- Decorative elements that shouldn't change with theme
|
||||
- Must be documented in component comments
|
||||
|
||||
## Inverted Background Pattern
|
||||
|
||||
Section components support three modes for background styling, allowing flexible visual contrast and card-style layouts.
|
||||
|
||||
### Three Background Modes
|
||||
|
||||
**`"noInvert"`** - Standard background (default page background color)
|
||||
- No background color applied to section
|
||||
- Text uses standard `text-foreground` color
|
||||
- Full-width section
|
||||
|
||||
**`"invertDefault"`** - Full-width inverted background
|
||||
- Section gets `bg-foreground` background
|
||||
- Text uses `text-background` color for contrast
|
||||
- Full-width section with inverted colors
|
||||
|
||||
**`"invertCard"`** - Card-style inverted background
|
||||
- Section gets `bg-foreground` background
|
||||
- Section is constrained to `w-content-width-expanded` width
|
||||
- Centered with `mx-auto`
|
||||
- Rounded corners with `rounded-theme-capped`
|
||||
- Text uses `text-background` color for contrast
|
||||
|
||||
### Implementation Pattern
|
||||
|
||||
```tsx
|
||||
import { cls } from "@/lib/utils";
|
||||
import { useTheme } from "@/providers/themeProvider/ThemeProvider";
|
||||
|
||||
interface SectionProps {
|
||||
title: string;
|
||||
description: string;
|
||||
useInvertedBackground: "noInvert" | "invertDefault" | "invertCard"; // Required
|
||||
// ... other props
|
||||
}
|
||||
|
||||
const Section = ({
|
||||
title,
|
||||
description,
|
||||
useInvertedBackground,
|
||||
className = "",
|
||||
// ... other props
|
||||
}: SectionProps) => {
|
||||
const theme = useTheme();
|
||||
|
||||
return (
|
||||
<section
|
||||
className={cls(
|
||||
"relative py-20",
|
||||
useInvertedBackground === "invertCard"
|
||||
? "w-content-width-expanded mx-auto rounded-theme-capped bg-foreground"
|
||||
: "w-full",
|
||||
useInvertedBackground === "invertDefault" && "bg-foreground",
|
||||
className
|
||||
)}
|
||||
>
|
||||
<div className="w-content-width mx-auto">
|
||||
<h1 className={cls(
|
||||
"text-6xl font-medium",
|
||||
(useInvertedBackground === "invertDefault" || useInvertedBackground === "invertCard") && "text-background",
|
||||
titleClassName
|
||||
)}>
|
||||
{title}
|
||||
</h1>
|
||||
<p className={cls(
|
||||
"text-lg",
|
||||
(useInvertedBackground === "invertDefault" || useInvertedBackground === "invertCard")
|
||||
? "text-background/75"
|
||||
: "text-foreground/75",
|
||||
descriptionClassName
|
||||
)}>
|
||||
{description}
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
### Key Points
|
||||
|
||||
1. **Required Prop**: `useInvertedBackground` should be a required string union type (no `?`), forcing explicit choice
|
||||
2. **Three Modes**: `"noInvert"`, `"invertDefault"`, `"invertCard"`
|
||||
3. **Section Width**:
|
||||
- `"invertCard"` → `w-content-width-expanded mx-auto`
|
||||
- `"noInvert"` or `"invertDefault"` → `w-full`
|
||||
4. **Background Color**:
|
||||
- `"invertCard"` → `bg-foreground rounded-theme-capped`
|
||||
- `"invertDefault"` → `bg-foreground`
|
||||
- `"noInvert"` → no background
|
||||
5. **Text Colors**: Apply `text-background` or `text-background/75` for `"invertDefault"` and `"invertCard"` modes
|
||||
6. **Relative Positioning**: Section needs `relative` class for proper z-index stacking
|
||||
7. **Conditional Logic**: Use explicit string equality checks (not truthy/falsy)
|
||||
|
||||
### Section className Pattern
|
||||
|
||||
**Standard pattern for all sections:**
|
||||
|
||||
```tsx
|
||||
<section
|
||||
className={cls(
|
||||
"relative py-20",
|
||||
useInvertedBackground === "invertCard"
|
||||
? "w-content-width-expanded mx-auto rounded-theme-capped bg-foreground"
|
||||
: "w-full",
|
||||
useInvertedBackground === "invertDefault" && "bg-foreground",
|
||||
className
|
||||
)}
|
||||
>
|
||||
```
|
||||
|
||||
### Text Color Pattern
|
||||
|
||||
**For text elements:**
|
||||
|
||||
```tsx
|
||||
// Single check for both invert modes:
|
||||
(useInvertedBackground === "invertDefault" || useInvertedBackground === "invertCard") && "text-background"
|
||||
|
||||
// Ternary for opacity variants:
|
||||
(useInvertedBackground === "invertDefault" || useInvertedBackground === "invertCard")
|
||||
? "text-background/75"
|
||||
: "text-foreground/75"
|
||||
```
|
||||
|
||||
### For Components with Cards (Advanced)
|
||||
|
||||
When a section contains cards with light backgrounds, use the `shouldUseInvertedText` utility:
|
||||
|
||||
```tsx
|
||||
import { cls, shouldUseInvertedText } from "@/lib/utils";
|
||||
import { useTheme } from "@/providers/themeProvider/ThemeProvider";
|
||||
|
||||
const SectionWithCards = ({
|
||||
useInvertedBackground,
|
||||
// ... other props
|
||||
}: SectionProps) => {
|
||||
const theme = useTheme();
|
||||
const shouldUseLightText = shouldUseInvertedText(
|
||||
useInvertedBackground,
|
||||
theme.cardStyle
|
||||
);
|
||||
|
||||
return (
|
||||
<section
|
||||
className={cls(
|
||||
"relative py-20",
|
||||
useInvertedBackground === "invertCard"
|
||||
? "w-content-width-expanded mx-auto rounded-theme-capped bg-foreground"
|
||||
: "w-full",
|
||||
useInvertedBackground === "invertDefault" && "bg-foreground",
|
||||
className
|
||||
)}
|
||||
>
|
||||
{/* For elements inside cards or with card backgrounds */}
|
||||
<h3 className={cls(
|
||||
"text-xl",
|
||||
shouldUseLightText && "text-background",
|
||||
bulletTitleClassName
|
||||
)}>
|
||||
{point.title}
|
||||
</h3>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
**What it does:**
|
||||
The `shouldUseInvertedText` utility checks if:
|
||||
- `useInvertedBackground` is `"invertDefault"` or `"invertCard"` AND
|
||||
- Current `cardStyle` is a "light" style (e.g., `glass-elevated`, `outline`, etc.)
|
||||
|
||||
This ensures text inside cards remains readable regardless of theme configuration.
|
||||
|
||||
### Width Classes Explained
|
||||
|
||||
**`w-content-width`** - Content container width
|
||||
- Controlled by `theme.contentWidth` (small/medium/large)
|
||||
- Used for inner content wrapper in all sections
|
||||
|
||||
**`w-content-width-expanded`** - Expanded card-style width
|
||||
- Used only for `"invertCard"` mode on the `<section>` element
|
||||
- Wider than `w-content-width` for visual card effect
|
||||
- Combined with `mx-auto` and `rounded-theme-capped`
|
||||
|
||||
## Content Width Pattern
|
||||
|
||||
All section content must use the `w-content-width` class:
|
||||
|
||||
```tsx
|
||||
<section className="w-full py-20">
|
||||
<div className="w-content-width mx-auto">
|
||||
{/* content */}
|
||||
</div>
|
||||
</section>
|
||||
```
|
||||
|
||||
**What it does:**
|
||||
- Controlled by `theme.contentWidth` (small/medium/large)
|
||||
- Automatically adjusts max-width based on theme setting
|
||||
- Centers content with `mx-auto`
|
||||
|
||||
## Section Spacing
|
||||
|
||||
**Standard sections:**
|
||||
```tsx
|
||||
className="w-full py-20"
|
||||
```
|
||||
|
||||
Vertical padding of `py-20` (5rem = 80px) on top and bottom.
|
||||
|
||||
**Exceptions (NO py-20):**
|
||||
- Hero sections (custom spacing based on design)
|
||||
- Footer sections (custom spacing)
|
||||
- Full-bleed sections with background colors/images
|
||||
|
||||
## Card Styling Pattern
|
||||
|
||||
Use the `card` class for all card components:
|
||||
|
||||
```tsx
|
||||
<div className={cls("card p-6 rounded-theme-capped h-full min-h-0", cardClassName)}>
|
||||
{/* card content */}
|
||||
</div>
|
||||
```
|
||||
|
||||
**Classes explained:**
|
||||
- `card` - Theme-aware background/border (maps to `theme.cardStyle`)
|
||||
- `p-6` - Standard padding (1.5rem = 24px)
|
||||
- `rounded-theme-capped` - Border radius from theme (respects `theme.borderRadius`)
|
||||
- `h-full` - Fill parent height (for grid layouts)
|
||||
- `min-h-0` - Prevent height conflicts (important for flex layouts)
|
||||
|
||||
## Border Radius Pattern
|
||||
|
||||
**For cards:**
|
||||
```tsx
|
||||
className="rounded-theme-capped"
|
||||
```
|
||||
|
||||
**For buttons:**
|
||||
```tsx
|
||||
className="rounded-theme"
|
||||
```
|
||||
|
||||
Both respect `theme.borderRadius` setting (rounded/pill/sharp).
|
||||
|
||||
## Button Styling Classes
|
||||
|
||||
**Primary button (first in array):**
|
||||
```tsx
|
||||
className="primary-button"
|
||||
```
|
||||
|
||||
**Secondary button (second+ in array):**
|
||||
```tsx
|
||||
className="secondary-button"
|
||||
```
|
||||
|
||||
These classes are theme-aware and map to:
|
||||
- `theme.primaryButtonStyle` (gradient/solid/glass/outline)
|
||||
- `theme.secondaryButtonStyle` (glass/outline/solid/gradient)
|
||||
|
||||
## Styling Checklist
|
||||
|
||||
### Color Usage
|
||||
- [ ] Use `bg-background` and `text-foreground` for main colors
|
||||
- [ ] Use `bg-foreground` and `text-background` for inverted sections
|
||||
- [ ] Use `card` class for card backgrounds
|
||||
- [ ] Use `primary-button` and `secondary-button` for buttons
|
||||
- [ ] Avoid hardcoded colors (white, black, gray-X) unless justified
|
||||
|
||||
### Theme Integration
|
||||
- [ ] Wrap app/page in single ThemeProvider
|
||||
- [ ] Use `useTheme()` hook when needed
|
||||
- [ ] Don't specify button variants in ButtonConfig
|
||||
- [ ] Let TextBox inherit default text animation
|
||||
- [ ] Use `w-content-width mx-auto` for content
|
||||
|
||||
### Inverted Background (if applicable)
|
||||
- [ ] Accept `useInvertedBackground` as required string union: `"noInvert" | "invertDefault" | "invertCard"`
|
||||
- [ ] Add `relative` class to section
|
||||
- [ ] Implement three-mode section className pattern (invertCard with expanded width, invertDefault full-width, noInvert standard)
|
||||
- [ ] Apply `text-background` to text for `"invertDefault"` and `"invertCard"` modes (not `"noInvert"`)
|
||||
- [ ] Use explicit string equality checks (not truthy/falsy)
|
||||
- [ ] Use `shouldUseInvertedText` for card content if needed
|
||||
|
||||
### Card Styling
|
||||
- [ ] Use `card` class for theme-aware background
|
||||
- [ ] Use `rounded-theme-capped` for border radius
|
||||
- [ ] Include `min-h-0` for flex compatibility
|
||||
- [ ] Provide `cardClassName` override prop
|
||||
|
||||
### Spacing
|
||||
- [ ] Use `py-20` on sections (except hero/footer)
|
||||
- [ ] Use `w-content-width mx-auto` wrapper
|
||||
- [ ] Follow theme spacing guidelines
|
||||
Reference in New Issue
Block a user