Skip to content

Component Library

Guidelines for using and creating components.

Design System

Colors

The design system defines CSS custom properties in frontend/app/globals.css under :root, and re-exposes them to Tailwind 4 via @theme inline (so --primary is usable as e.g. text-primary). The hex values below are current brand values (several were darkened for WCAG AA contrast):

--primary: #15808d;      /* Brand teal (darkened for WCAG AA) */
--primary-dark: #126b75; /* Hover */
--primary-darker: #0e565e; /* Active/pressed */
--primary-light: #b1e0e8;
--accent: #dc2626;       /* CTA red (darkened for WCAG AA) */
--foreground: #131117;   /* Dark navy text */
--muted-foreground: #525960; /* AA-compliant on white */

/* Frost / Ice / Coral theme tokens, defined as numbered scales */
--frost-50: #f0f9ff;     /* Lightest frost background */
--frost-100: #e0f2fe;
--frost-300: #7dd3fc;    /* Medium frost accent (scale runs 50–600) */
--ice-100: #e0f7fa;      /* Ice highlight (scale runs to --ice-400) */
--coral-400: #fb7185;    /* Coral warm accent (also --coral-500) */

Typography

--text-sm: 0.875rem;     /* 14px */
--text-base: 1rem;       /* 16px */
--text-lg: 1.125rem;     /* 18px */
--font-semibold: 600;
--font-bold: 700;

Spacing

--space-2: 0.5rem;       /* 8px */
--space-4: 1rem;         /* 16px */
--space-6: 1.5rem;       /* 24px */

Component Patterns

Props Interface

Always define typed props:

interface ComponentProps {
  /** Primary content */
  children: React.ReactNode;
  /** Visual variant */
  variant?: 'primary' | 'secondary';
  /** Additional CSS classes */
  className?: string;
}

Default Props

Use destructuring for defaults:

export function Component({
  variant = 'primary',
  className = '',
  children,
}: ComponentProps) {
  // ...
}

Composition

Prefer composition over configuration:

// Good
<Card>
  <CardHeader>
    <CardTitle>Title</CardTitle>
  </CardHeader>
  <CardContent>Content</CardContent>
</Card>

// Avoid
<Card
  title="Title"
  content="Content"
  showHeader={true}
/>

Creating Stories

Every component should have a story:

// Component.stories.tsx
import type { Meta, StoryObj } from '@storybook/nextjs';
import { Component } from './Component';

const meta: Meta<typeof Component> = {
  title: 'Components/Category/Component',
  component: Component,
  tags: ['autodocs'],
};

export default meta;
type Story = StoryObj<typeof Component>;

export const Default: Story = {
  args: {
    children: 'Example',
  },
};

export const Secondary: Story = {
  args: {
    children: 'Example',
    variant: 'secondary',
  },
};

Accessibility

All components must:

  1. Support keyboard navigation
  2. Have appropriate ARIA attributes
  3. Meet WCAG 2.1 AA contrast ratios
  4. Include data-testid for testing
<button
  aria-label="Increase quantity"
  data-testid="quantity-increment"
>
  +
</button>