Read first:
claude.mdfor universal development standards. This file contains project-specific configuration and context.
- Framework: TanStack Start v1.134.9
- Runtime: React 19.2.0
- Language: TypeScript 5.9 (strict mode)
- Styling: CSS Modules + PostCSS
- Package Manager: pnpm 10.16.1
- Deployment: Docker + Vercel
src/
├── components/
│ ├── common/ # Reusable UI components (Button, Link, TerminalWindow,
│ │ # TechStack, Typography, List, Callout, AccentBar,
│ │ # ThemeToggle, CookieBanner, Heading, Text, Divider)
│ ├── layout/ # Layout primitives (Header, Footer, Section, Container,
│ │ # Stack, Grid)
│ ├── home/ # Homepage sections (Hero, ClientTrust, FeaturedWork,
│ │ # Services, Approach, CTA + subcomponents)
│ ├── privacy/ # Privacy policy content component
│ ├── style-guide/ # Storybook documentation (layout-system.mdx, logos.mdx)
│ └── work/ # Case study content components (sonic-equipment-case-study)
├── css/ # Design tokens (color, typography, spacing, layout, etc.)
├── routes/ # Route files (__root.tsx, index.tsx, privacy.tsx,
│ # work/sonic-equipment/index.tsx)
└── utils/ # Utilities (analytics.ts)
Dark Theme (default):
--bg-primary: #0a0a0a --bg-secondary: #1a1a1a --text-primary: #fafafa
--text-secondary: rgba(250, 250, 250, 0.7) --accent-primary: #ff4500
/* orange-red */ --accent-secondary: #95c5ef /* blue */
--border-default-color: #333;Light Theme:
--bg-primary: #f5f3f0 --bg-secondary: #fff --text-primary: #1a1a1a
--accent-primary: #ff4500 --accent-secondary: #1f70b7;Full definitions: src/css/color-tokens.css
The typography system uses a Perfect Fourth modular scale (1.333 ratio) for mathematical harmony and visual rhythm. Each size is approximately 33% larger than the previous, creating predictable, pleasing hierarchy.
Font Families:
--font-family: 'Instrument Sans', sans-serif;
--mono-font-family: 'Recursive', monospace;
--accent-font-family: 'Schibsted Grotesk', sans-serif;Font Sizes (9 sizes, down from 12):
/* Display Sizes - Hero sections and page titles */
--font-size-5xl: 95px; /* Hero desktop */
--font-size-4xl: 71px; /* Hero tablet */
--font-size-3xl: 53px; /* Hero mobile, page titles (H1) */
--font-size-2xl: 40px; /* Major section headers (H2) */
--font-size-xl: 30px; /* Subsection headers (H3) */
/* Body & UI Sizes */
--font-size-lg: 23px; /* Large body text, callouts, featured content */
--font-size-base: 18px; /* Default body text */
--font-size-sm: 14px; /* Small UI text, terminal, captions */
--font-size-xs: 12px; /* Meta text, labels, footer fine print */When to use each size:
- 5xl, 4xl, 3xl: Hero sections and page titles (responsive)
- 2xl: Major section headers (H2)
- xl: Subsection headers (H3)
- lg: Large body text, callouts, featured content
- base: Default body text (18px)
- sm: Terminal, code, captions, small UI elements
- xs: Meta text, labels, fine print
Full definitions and rationale: src/css/typography-tokens.css
--spacing-0-1: 1px --spacing-1: 4px --spacing-2: 8px --spacing-4: 16px
--spacing-8: 32px --spacing-16: 64px --spacing-24: 96px... up to
--spacing-96: 384px;Full scale: src/css/spacing-tokens.css
xs: 0-639px (Mobile)
sm: 640-767px (Large mobile)
md: 768-1023px (Tablet)
lg: 1024-1279px (Laptop)
xl: 1280-1535px (Desktop)
2xl: 1536-1919px (Large desktop)
3xl: 1920px+ (Ultra wide)
Usage:
@media (--md-n-above) {
/* 768px and up */
}
@media (--lg-n-below) {
/* below 1024px */
}
@media (--xl-only) {
/* 1280-1535px */
}Full definitions: src/css/breakpoints.css
We use 5 semantic content widths instead of arbitrary pixel values:
--content-narrow: 600px /* Forms, CTAs, focused content */
--content-base: 900px /* Articles, case studies, privacy policy */
--content-comfortable: 1000px /* Slightly wider content */
--content-wide: 1200px /* Multi-column layouts, service grids */
--content-full: 1400px /* Hero sections, full-width layouts */Full definitions: src/css/layout-tokens.css
- Borders:
src/css/border-tokens.css - Animations:
src/css/animation-tokens.css - Shadows:
src/css/shadow-tokens.css - Z-index:
src/css/z-index-tokens.css
# Development
pnpm dev # Start dev server (Vite)
# Linting
pnpm lint # Check TypeScript/ESLint
pnpm lint:fix # Auto-fix errors
pnpm stylelint # Check CSS
pnpm stylelint:fix # Auto-fix CSS
pnpm test:types # Verify typescript types
pnpm test:all # Run test:type, lint, and stylelint
# Build
pnpm build # Production build
pnpm build:ci # Storybook + build
# Storybook
pnpm storybook:dev # Start Storybook (port 6006)
pnpm storybook:build # Build Storybook to public/storybook
# Docker
pnpm docker:build # Build image
pnpm docker:run # Run container (port 3000)
pnpm docker:build:run # Build and run
pnpm docker:rebuild:run # Remove, rebuild, and run- TypeScript:
tsconfig.json(strict mode, path aliases@/) - ESLint:
eslint.config.js(comprehensive rules, auto-fix enabled) - Stylelint:
.stylelintrc.cjs(idiomatic order, CSS Modules) - Vite:
vite.config.ts(React, TanStack Start) - PostCSS: Custom media queries, nested CSS, autoprefixer
- Prettier:
.prettierrc.cjs(code formatting)
IMPORTANT: We use a composition-based layout system following industry best practices from Material-UI, Chakra UI, and Shopify Polaris.
- Composition over configuration - Combine simple primitives to create complex layouts
- Semantic HTML - Use proper elements (
<section>,<h1>-<h6>,<p>) - Responsive by default - Mobile-first with progressive enhancement
- Design tokens only - No hardcoded values
Horizontal centering with semantic max-width constraints.
<Container size="base">
<p>Centered content with 900px max-width</p>
</Container>Props: size (narrow/base/comfortable/wide/full), className
Vertical spacing between elements using CSS flexbox gap.
<Stack space="large">
<Heading level={2}>Title</Heading>
<Text>First paragraph</Text>
<Text>Second paragraph</Text>
</Stack>
{/* With dividers */}
<Stack space="medium" dividers>
<Section1 />
<Section2 />
</Stack>Props: space (small/medium/large/xlarge), dividers (boolean), className
Semantic page sections with vertical padding.
<Section>
<Container size="wide">
<h2>Section Content</h2>
</Container>
</Section>Props: spacing (normal/large/none), id (for anchors), className
Responsive grid layouts.
<Grid columns={{ mobile: 1, tablet: 2, desktop: 3 }} gap="medium">
<Card />
<Card />
<Card />
</Grid>Props: columns (mobile/tablet/desktop), gap (small/medium/large), className
Separates semantic HTML level from visual size. Supports fluid typography for smooth responsive scaling.
{/* Semantic h2, visual 3xl */}
<Heading level={2} size="3xl" align="center">
Page Title
</Heading>
{/* Uppercase accent heading */}
<Heading level={2} variant="accent">
FEATURED WORK
</Heading>
{/* Auto-sized (h1 → 3xl) */}
<Heading level={1}>Main Title</Heading>
{/* Fluid hero heading - scales smoothly 30px → 95px */}
<Heading level={1} size="5xl" fluid>
Deploy with confidence
</Heading>Props: level (1-6), size (xs/sm/md/lg/xl/2xl/3xl/4xl/5xl), variant (default/accent/display), align, fluid (boolean), className
Note: The PageTitle component is deprecated. Use <Heading level={1} size="5xl" fluid> for hero headings instead.
Body text, captions, labels with semantic variants.
<Text>Standard paragraph</Text>
<Text variant="caption" color="tertiary">Last updated: Nov 2025</Text>
<Text variant="label" weight="bold">Form Label</Text>
<Text variant="code">const x = 1</Text>Props: variant (body/caption/label/code), size (xs/sm/base/lg/xl), color (primary/secondary/tertiary/accent), weight, align, className
Visual separation between content.
<Divider />
<Divider variant="gradient" spacing="large" />Props: variant (default/gradient), spacing (none/small/medium/large), className
<Section>
<Container size="wide">
<Stack space="large">
<Heading level={2} variant="accent" align="center">
FEATURED WORK
</Heading>
<Grid columns={{ mobile: 1, desktop: 2 }} gap="large">
<ProjectCard />
<ProjectCard />
</Grid>
</Stack>
</Container>
</Section><Section>
<Container size="base">
<Stack space="large">
<header>
<Stack space="small">
<Heading level={1} size="2xl">Privacy Policy</Heading>
<Text variant="caption" color="tertiary">Last updated: Nov 2025</Text>
</Stack>
</header>
<Typography>
<Stack dividers space="medium">
<section>...</section>
<section>...</section>
</Stack>
</Typography>
</Stack>
</Container>
</Section>Route File (src/routes/[page-name].tsx):
import { createFileRoute } from '@tanstack/react-router'
import { Section } from '@/components/layout/section/section'
import { Header } from '@/components/layout/header/header'
import { Footer } from '@/components/layout/footer/footer'
import { ContentComponent } from '@/components/[category]/content-component'
export const Route = createFileRoute('/page-name')({
component: RouteComponent,
preload: true,
})
function RouteComponent() {
return (
<>
<Header />
<Section>
<ContentComponent />
</Section>
<Footer />
</>
)
}Content Component (src/components/[category]/content-component.tsx):
import { Container } from '@/components/layout/container/container'
import { Stack } from '@/components/layout/stack/stack'
import { Heading } from '@/components/common/heading/heading'
import { Text } from '@/components/common/text/text'
export function ContentComponent() {
return (
<Container size="base">
<Stack space="large">
<Heading level={1}>Page Title</Heading>
<Text>Content...</Text>
</Stack>
</Container>
)
}Rules:
- ✅ Routes handle Header, Footer, Section wrappers
- ✅ Content components handle Container, Stack, Grid composition
- ✅ Keep routes minimal (~20 lines)
- ✅ All content/styling in components, not routes
- ✅ Components are reusable and testable
For complete documentation with all props, examples, and migration guides, see:
- Storybook:
pnpm storybook:dev→ Style Guide → Layout System - Source:
src/components/style-guide/layout-system.mdx
- 30% terminal aesthetic, 70% clean typography
- Terminal components:
TerminalWindow(dots, border, monospace) - Used primarily in Hero section, subtle elsewhere
- Theme toggle in footer (system/dark/light cycle)
- Stored in localStorage
- Implemented via
data-themeattribute on<html> - Component:
src/components/common/theme-toggle/theme-toggle.tsx
Auto-detects link type (external, anchor, protocol, internal) and applies correct attributes/routing.
<Link href="https://...">External</Link> {/* Auto: target="_blank" rel="noopener" */}
<Link href="#section">Anchor</Link> {/* Auto: smooth scroll */}
<Link href="/about">Internal</Link> {/* Auto: TanStack Router */}
<Link href="mailto:...">Email</Link> {/* Auto: protocol link */}Components show different text on mobile vs desktop:
<span className={styles['desktop-text']}>Long descriptive text...</span>
<span className={styles['mobile-text']}>Short</span>- Cookie banner on first visit
- Google Analytics only loads after consent
- Implementation:
src/components/common/cookie-banner/ - Docs:
docs/COOKIE_BANNER_IMPLEMENTATION.md
docs/CRAFITY_HOMEPAGE_MASTER_CONTENT.md: All copy, content, SEOdocs/CRAFITY_IMPLEMENTATION_GUIDE.md: Technical specs, chunk breakdowndocs/CRAFITY_BEST_PRACTICES.md: Deep-dive best practicesdocs/COOKIE_BANNER_IMPLEMENTATION.md: Cookie/analytics setup
- Boutique engineering firm showcase
- Bold typography + geometric accents
- Asymmetric layouts (Dutch design influence)
- Professional but approachable tone
- Terminal aesthetic as accent (not dominant)
Create .env file (see .env.example):
VITE_GA4_MEASUREMENT_ID=G-XXXXXXXXXXdocker build -t crafity-start .
docker run -p 3000:3000 crafity-start- Auto-deploy from
mainbranch - Framework preset: TanStack Start (auto-detected)
- Environment variables: Add
VITE_GA4_MEASUREMENT_ID
- ✅ Homepage implemented (all sections)
- ✅ Privacy policy page
- ✅ Cookie consent + GA4 integration
- ✅ Dark/light theme toggle
- ✅ Design token system complete
- ✅ Storybook for common components
- ⏳ Case study pages (planned)
- ⏳ Performance optimization (target: Lighthouse 100)
- Routing best practices (TanStack Start patterns)
- Data fetching strategy
- SSR optimization techniques
For development standards, see claude.md in root.