# Portfolio Foundation Implementation Plan > **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. **Goal:** Bootstrap the allmywork SvelteKit portfolio with a CLAUDE.md, a brutalist design system extracted from the Figma AI prototype, shared/ui components converted from React to Svelte 5, and Storybook stories for each component. **Architecture:** Tailwind CSS v4 custom properties carry the design tokens (colors, typography, spacing, shadows). All UI primitives live in `src/shared/ui/`, each as a PascalCase folder with `ComponentName.svelte`, `types.ts`, `index.ts`, and `ComponentName.stories.svelte`. The FSD directory structure is already in place; this plan fills in the foundation layer only. **Tech Stack:** SvelteKit 2 + Svelte 5 (runes), Tailwind CSS v4, Biome (lint/format), Vitest (unit tests), Storybook 10 with `@storybook/addon-svelte-csf`, Bun as package manager and runtime. --- ## Task 0: Write CLAUDE.md for the project **Files:** - Modify: `/home/ilia/Documents/Projects/allmywork/CLAUDE.md` The existing CLAUDE.md covers project purpose and tooling. We need to add the code-style section that mirrors glyphdiff's conventions. **Step 1: Read the existing CLAUDE.md** Read `/home/ilia/Documents/Projects/allmywork/CLAUDE.md` in full. **Step 2: Append the Code Style section** Add the following section to the end of the existing CLAUDE.md (before any trailing lines): ```markdown --- ## Code Style ### Formatting (Biome) - Indent: tabs - Quote style: double quotes for JS/TS, double quotes for Svelte attributes - Line width: 120 characters - Semicolons: always - Trailing commas: all ### TypeScript - Strict mode: on - Prefer `interface` over `type` for object shapes - Always type component props via an `interface Props { ... }` block - Use `$props()` rune — never `export let` - Use `$derived()` and `$effect()` — never Svelte 4 `$:` syntax - Use `$state()` for all reactive primitives ### Component Conventions - File name: PascalCase (`Button.svelte`, `ProjectCard.svelte`) - Store files: camelCase with `.svelte.ts` suffix (`sidebarStore.svelte.ts`) - Story files: `.stories.svelte` suffix (Storybook CSF with Svelte addon) - Test files: `.test.ts` for unit, `.svelte.test.ts` for component tests - Each slice exports a public API via `index.ts` ### Svelte 5 Patterns ```svelte ``` ### Comments - Multiline JSDoc (`/** ... */`) for exported functions and component description headers - Single-line (`//`) for inline logic remarks - Principle: "less is more" — explain the *why*, not the *what* ### Import Order (Biome organizeImports) 1. Svelte/SvelteKit built-ins (`svelte`, `$app/...`) 2. FSD layer aliases (`$shared/...`, `$entities/...`, etc.) 3. Relative imports (`./`, `../`) ### Commit Messages Format: `prefix(scope): short description` Prefixes: `feat`, `fix`, `chore`, `refactor`, `docs`, `style`, `test`, `perf`, `ci`, `build`, `revert` Example: `feat(shared/ui): add Button component with brutalist variants` ``` **Step 3: Verify the file looks correct** Read the first and last 20 lines of the updated CLAUDE.md to confirm the edit landed cleanly. **Step 4: Commit** ```bash cd /home/ilia/Documents/Projects/allmywork git add CLAUDE.md git commit -m "docs(CLAUDE.md): add code style section aligned with glyphdiff conventions" ``` --- ## Task 1: Install Storybook and required dev dependencies The project currently has no Storybook. This task wires it up before any stories are written. **Files:** - Modify: `/home/ilia/Documents/Projects/allmywork/package.json` (via bun add) - Create: `/home/ilia/Documents/Projects/allmywork/.storybook/main.ts` - Create: `/home/ilia/Documents/Projects/allmywork/.storybook/preview.ts` **Step 1: Install Storybook and addons** ```bash cd /home/ilia/Documents/Projects/allmywork bunx storybook@latest init --skip-install ``` Expected output: Storybook scaffolds `.storybook/` and adds scripts to package.json. Answer "yes" to any prompts. **Step 2: Verify generated .storybook/main.ts** Read `/home/ilia/Documents/Projects/allmywork/.storybook/main.ts`. It should reference `@storybook/svelte-vite` as the framework. If the `init` command did not produce a valid Svelte-Vite config, write `.storybook/main.ts` manually: ```typescript import type { StorybookConfig } from '@storybook/svelte-vite'; const config: StorybookConfig = { stories: ['../src/**/*.stories.svelte'], addons: [ '@storybook/addon-svelte-csf', '@storybook/addon-a11y', '@storybook/addon-docs', ], framework: { name: '@storybook/svelte-vite', options: {}, }, }; export default config; ``` **Step 3: Install missing addons if needed** ```bash cd /home/ilia/Documents/Projects/allmywork bun add -D @storybook/addon-svelte-csf @storybook/addon-a11y @storybook/addon-docs ``` **Step 4: Add Storybook scripts to package.json (if not already present)** Open `/home/ilia/Documents/Projects/allmywork/package.json` and confirm the `scripts` section contains: ```json "storybook": "storybook dev -p 6006", "build-storybook": "storybook build" ``` If missing, add them. **Step 5: Run Storybook to confirm it starts** ```bash cd /home/ilia/Documents/Projects/allmywork bun run storybook & ``` Expected: Storybook starts on http://localhost:6006 with no fatal errors. Kill it after confirming: `kill %1` or `Ctrl+C`. **Step 6: Commit** ```bash cd /home/ilia/Documents/Projects/allmywork git add .storybook package.json git commit -m "chore(storybook): install and configure Storybook 10 with svelte-vite" ``` --- ## Task 2: Set up the design system — CSS custom properties and Tailwind v4 tokens Extract the design tokens from the Figma prototype's `theme.css` into the project's `app.css` and register them as Tailwind v4 theme values. **Files:** - Modify: `/home/ilia/Documents/Projects/allmywork/src/app/styles/app.css` **Reference:** `/home/ilia/Downloads/High-End Portfolio Design System(1)/src/styles/theme.css` and `fonts.css` **Step 1: Open app.css and replace its contents** The file currently only contains `@import "tailwindcss";`. Replace it with the following: ```css @import "tailwindcss"; /* ============================================================ FONTS ============================================================ */ @import url('https://fonts.googleapis.com/css2?family=Fraunces:ital,opsz,wght,SOFT,WONK@0,9..144,100..900,0..100,0..1;1,9..144,100..900,0..100,0..1&display=swap'); @import url('https://fonts.googleapis.com/css2?family=Public+Sans:ital,wght@0,100..900;1,100..900&display=swap'); /* ============================================================ DESIGN TOKENS ============================================================ */ :root { /* Typography scale — Augmented Fourth (×1.414) */ --font-size-base: 16px; --text-xs: 0.707rem; /* 11.31px */ --text-sm: 0.840rem; /* 13.44px */ --text-base: 1rem; /* 16px */ --text-lg: 1.414rem; /* 22.62px */ --text-xl: 2rem; /* 32px */ --text-2xl: 2.828rem; /* 45.25px */ --text-3xl: 4rem; /* 64px */ --text-4xl: 5.657rem; /* 90.51px */ --text-5xl: 8rem; /* 128px */ /* Font families */ --font-heading: 'Fraunces', serif; --font-body: 'Public Sans', sans-serif; /* Font weights */ --font-weight-heading: 900; --font-weight-body: 600; --font-weight-normal: 400; /* Line heights */ --line-height-tight: 1.2; --line-height-normal: 1.5; /* Fraunces variable font axes */ --fraunces-wonk: 1; --fraunces-soft: 0; /* Palette — Sanzo Wada inspired */ --ochre-clay: #D9B48F; --slate-indigo: #3B4A59; --burnt-oxide: #A64B35; --carbon-black: #121212; /* Semantic color aliases */ --background: var(--ochre-clay); --foreground: var(--carbon-black); --card: var(--ochre-clay); --card-foreground: var(--carbon-black); --primary: var(--burnt-oxide); --primary-foreground: var(--ochre-clay); --secondary: var(--slate-indigo); --secondary-foreground: var(--ochre-clay); --muted: var(--slate-indigo); --muted-foreground: var(--ochre-clay); --accent: var(--burnt-oxide); --accent-foreground: var(--ochre-clay); --destructive: #d4183d; --destructive-foreground:#ffffff; --border: var(--carbon-black); --ring: var(--carbon-black); /* Spacing — 8pt linear scale */ --space-1: 0.5rem; /* 8px */ --space-2: 1rem; /* 16px */ --space-3: 1.5rem; /* 24px */ --space-4: 2rem; /* 32px */ --space-5: 2.5rem; /* 40px */ --space-6: 3rem; /* 48px */ --space-8: 4rem; /* 64px */ --space-10: 5rem; /* 80px */ --space-12: 6rem; /* 96px */ --space-16: 8rem; /* 128px */ --space-20: 10rem; /* 160px */ /* Borders */ --border-width: 3px; --radius: 0px; /* Brutalist hard shadows */ --shadow-brutal-sm: 4px 4px 0 var(--carbon-black); --shadow-brutal: 8px 8px 0 var(--carbon-black); --shadow-brutal-lg: 12px 12px 0 var(--carbon-black); } /* ============================================================ TAILWIND v4 THEME REGISTRATION ============================================================ */ @theme inline { /* Colors */ --color-background: var(--background); --color-foreground: var(--foreground); --color-card: var(--card); --color-card-foreground: var(--card-foreground); --color-primary: var(--primary); --color-primary-foreground: var(--primary-foreground); --color-secondary: var(--secondary); --color-secondary-foreground: var(--secondary-foreground); --color-muted: var(--muted); --color-muted-foreground: var(--muted-foreground); --color-accent: var(--accent); --color-accent-foreground: var(--accent-foreground); --color-destructive: var(--destructive); --color-border: var(--border); --color-ring: var(--ring); /* Palette direct access */ --color-ochre-clay: var(--ochre-clay); --color-slate-indigo: var(--slate-indigo); --color-burnt-oxide: var(--burnt-oxide); --color-carbon-black: var(--carbon-black); /* Border radius (zero for brutalist aesthetic) */ --radius-sm: var(--radius); --radius-md: var(--radius); --radius-lg: var(--radius); /* Box shadows */ --shadow-brutal-sm: var(--shadow-brutal-sm); --shadow-brutal: var(--shadow-brutal); --shadow-brutal-lg: var(--shadow-brutal-lg); /* Font families */ --font-heading: var(--font-heading); --font-body: var(--font-body); } /* ============================================================ BASE LAYER — GLOBAL ELEMENT DEFAULTS ============================================================ */ @layer base { html { font-size: var(--font-size-base); } body { background-color: var(--background); color: var(--foreground); font-family: var(--font-body); font-weight: var(--font-weight-body); line-height: var(--line-height-tight); overflow-x: hidden; position: relative; } /* Paper grain texture overlay */ body::before { content: ''; position: fixed; inset: 0; background-image: repeating-linear-gradient(0deg, transparent, transparent 2px, rgba(0,0,0,.02) 2px, rgba(0,0,0,.02) 4px), repeating-linear-gradient(90deg, transparent, transparent 2px, rgba(0,0,0,.02) 2px, rgba(0,0,0,.02) 4px); opacity: .4; pointer-events: none; z-index: 1; } /* Ensure page content renders above texture */ body > * { position: relative; z-index: 2; } h1, .h1 { font-size: var(--text-4xl); } h2, .h2 { font-size: var(--text-3xl); } h3, .h3 { font-size: var(--text-2xl); } h4, .h4 { font-size: var(--text-xl); } h5, .h5 { font-size: var(--text-lg); } h1, h2, h3, h4, h5, .h1, .h2, .h3, .h4, .h5 { font-family: var(--font-heading); font-weight: var(--font-weight-heading); line-height: var(--line-height-tight); font-variation-settings: 'WONK' var(--fraunces-wonk), 'SOFT' var(--fraunces-soft); color: var(--carbon-black); margin: 0; } p { font-family: var(--font-body); font-size: var(--text-base); font-weight: var(--font-weight-body); line-height: var(--line-height-tight); color: var(--carbon-black); margin: 0; } label { font-family: var(--font-body); font-size: var(--text-sm); font-weight: var(--font-weight-body); text-transform: uppercase; letter-spacing: .05em; line-height: var(--line-height-tight); } button { font-family: var(--font-body); font-size: var(--text-base); font-weight: var(--font-weight-body); line-height: var(--line-height-tight); cursor: pointer; } input, textarea, select { font-family: var(--font-body); font-size: var(--text-base); font-weight: var(--font-weight-body); line-height: var(--line-height-tight); } blockquote { font-family: var(--font-heading); font-size: var(--text-xl); font-weight: var(--font-weight-heading); font-variation-settings: 'WONK' var(--fraunces-wonk), 'SOFT' var(--fraunces-soft); line-height: var(--line-height-tight); border-left: var(--border-width) solid var(--carbon-black); padding-left: var(--space-4); margin: var(--space-6) 0; } a { color: var(--burnt-oxide); text-decoration: none; border-bottom: 2px solid var(--carbon-black); transition: border-bottom-width .15s ease; } a:hover { border-bottom-width: 4px; } } /* ============================================================ BRUTALIST UTILITY CLASSES ============================================================ */ .brutal-border { border: var(--border-width) solid var(--carbon-black); } .brutal-border-top { border-top: var(--border-width) solid var(--carbon-black); } .brutal-border-bottom { border-bottom: var(--border-width) solid var(--carbon-black); } .brutal-border-left { border-left: var(--border-width) solid var(--carbon-black); } .brutal-border-right { border-right: var(--border-width) solid var(--carbon-black); } .brutal-shadow { box-shadow: var(--shadow-brutal); } .brutal-shadow-sm { box-shadow: var(--shadow-brutal-sm); } .brutal-shadow-lg { box-shadow: var(--shadow-brutal-lg); } /* ============================================================ GRID UTILITIES ============================================================ */ .grid-muller { display: grid; gap: var(--space-3); grid-template-columns: repeat(4, 1fr); } @media (min-width: 1024px) { .grid-muller { grid-template-columns: repeat(12, 1fr); } } /* ============================================================ ANIMATION ============================================================ */ @keyframes fadeInUp { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } } .animate-fade-in-up { animation: fadeInUp .5s cubic-bezier(.4, 0, .2, 1); } ``` **Step 2: Verify dev server still compiles** ```bash cd /home/ilia/Documents/Projects/allmywork bun run dev & ``` Expected: No CSS errors in the terminal. Visit http://localhost:5173 — the page background should be ochre-clay (#D9B48F). Kill dev server: `kill %1` **Step 3: Commit** ```bash cd /home/ilia/Documents/Projects/allmywork git add src/app/styles/app.css git commit -m "feat(design-system): add brutalist design tokens and Tailwind v4 theme from Figma prototype" ``` --- ## Task 3: Add the `cn` utility to shared/lib Many components will use `cn` (clsx + tailwind-merge) to compose class names. This task adds the helper before any component uses it. **Files:** - Create: `/home/ilia/Documents/Projects/allmywork/src/shared/lib/cn.ts` - Modify: `/home/ilia/Documents/Projects/allmywork/src/shared/lib/index.ts` **Step 1: Install clsx and tailwind-merge** ```bash cd /home/ilia/Documents/Projects/allmywork bun add clsx tailwind-merge ``` Expected: Both packages appear in `package.json` under `dependencies`. **Step 2: Write failing test** Create `/home/ilia/Documents/Projects/allmywork/src/shared/lib/cn.test.ts`: ```typescript import { describe, expect, it } from 'vitest'; import { cn } from './cn'; describe('cn', () => { it('merges class strings', () => { expect(cn('foo', 'bar')).toBe('foo bar'); }); it('deduplicates conflicting Tailwind classes (last wins)', () => { expect(cn('p-4', 'p-8')).toBe('p-8'); }); it('ignores falsy values', () => { expect(cn('foo', false, undefined, null, 'bar')).toBe('foo bar'); }); }); ``` **Step 3: Run test to confirm it fails** ```bash cd /home/ilia/Documents/Projects/allmywork bun run test src/shared/lib/cn.test.ts ``` Expected: FAIL — `Cannot find module './cn'` **Step 4: Implement cn.ts** ```typescript import { clsx, type ClassValue } from 'clsx'; import { twMerge } from 'tailwind-merge'; /** Merge Tailwind classes without conflicts. Later classes win on collision. */ export function cn(...inputs: ClassValue[]): string { return twMerge(clsx(inputs)); } ``` **Step 5: Run test to confirm it passes** ```bash cd /home/ilia/Documents/Projects/allmywork bun run test src/shared/lib/cn.test.ts ``` Expected: PASS (3 tests) **Step 6: Export from shared/lib/index.ts** Open `/home/ilia/Documents/Projects/allmywork/src/shared/lib/index.ts` and add: ```typescript export { cn } from './cn'; ``` **Step 7: Commit** ```bash cd /home/ilia/Documents/Projects/allmywork git add src/shared/lib/cn.ts src/shared/lib/cn.test.ts src/shared/lib/index.ts package.json git commit -m "feat(shared/lib): add cn utility (clsx + tailwind-merge)" ``` --- ## Task 4: Implement `Badge` component **Files:** - Modify: `/home/ilia/Documents/Projects/allmywork/src/shared/ui/` — replace flat files with folder-per-component layout - Create: `/home/ilia/Documents/Projects/allmywork/src/shared/ui/Badge/types.ts` - Create: `/home/ilia/Documents/Projects/allmywork/src/shared/ui/Badge/Badge.svelte` - Create: `/home/ilia/Documents/Projects/allmywork/src/shared/ui/Badge/index.ts` - Create: `/home/ilia/Documents/Projects/allmywork/src/shared/ui/Badge/Badge.stories.svelte` **Reference prototype:** `/home/ilia/Downloads/High-End Portfolio Design System(1)/src/app/components/Badge.tsx` **Step 1: Create types.ts** ```typescript export type BadgeVariant = 'default' | 'primary' | 'secondary' | 'outline'; ``` **Step 2: Write failing component test** Create `/home/ilia/Documents/Projects/allmywork/src/shared/ui/Badge/Badge.test.ts`: ```typescript import { render } from '@testing-library/svelte'; import { describe, expect, it } from 'vitest'; import Badge from './Badge.svelte'; describe('Badge', () => { it('renders children text', () => { const { getByText } = render(Badge, { props: { children: () => 'hello' } }); expect(getByText('hello')).toBeTruthy(); }); it('applies default variant class', () => { const { container } = render(Badge, { props: { variant: 'default' } }); expect(container.querySelector('span')?.className).toContain('bg-carbon-black'); }); }); ``` **Step 3: Run test to confirm failure** ```bash cd /home/ilia/Documents/Projects/allmywork bun run test src/shared/ui/Badge/Badge.test.ts ``` Expected: FAIL — component file not found. **Step 4: Implement Badge.svelte** ```svelte {#if children} {@render children()} {/if} ``` **Step 5: Create index.ts** ```typescript export { default as Badge } from './Badge.svelte'; export type { BadgeVariant } from './types'; ``` **Step 6: Run test to confirm it passes** ```bash cd /home/ilia/Documents/Projects/allmywork bun run test src/shared/ui/Badge/Badge.test.ts ``` Expected: PASS **Step 7: Write Badge.stories.svelte** ```svelte {#snippet template(args)} Tag {/snippet} {#snippet template(args)} Primary {/snippet} {#snippet template(args)} Secondary {/snippet} {#snippet template(args)} Outline {/snippet} {#snippet template()}
Default Primary Secondary Outline
{/snippet}
``` **Step 8: Commit** ```bash cd /home/ilia/Documents/Projects/allmywork git add src/shared/ui/Badge/ git commit -m "feat(shared/ui): add Badge component with brutalist variants and Storybook story" ``` --- ## Task 5: Implement `Button` component (replacing the placeholder) **Files:** - Delete: `/home/ilia/Documents/Projects/allmywork/src/shared/ui/Button.svelte` (placeholder) - Create: `/home/ilia/Documents/Projects/allmywork/src/shared/ui/Button/types.ts` - Create: `/home/ilia/Documents/Projects/allmywork/src/shared/ui/Button/Button.svelte` - Create: `/home/ilia/Documents/Projects/allmywork/src/shared/ui/Button/index.ts` - Create: `/home/ilia/Documents/Projects/allmywork/src/shared/ui/Button/Button.stories.svelte` - Modify: `/home/ilia/Documents/Projects/allmywork/src/shared/ui/index.ts` **Reference prototype:** `/home/ilia/Downloads/High-End Portfolio Design System(1)/src/app/components/Button.tsx` **Step 1: Create types.ts** ```typescript export type ButtonVariant = 'primary' | 'secondary' | 'outline' | 'ghost'; export type ButtonSize = 'sm' | 'md' | 'lg'; ``` **Step 2: Write failing test** Create `/home/ilia/Documents/Projects/allmywork/src/shared/ui/Button/Button.test.ts`: ```typescript import { render } from '@testing-library/svelte'; import { describe, expect, it } from 'vitest'; import Button from './Button.svelte'; describe('Button', () => { it('renders children', () => { const { getByText } = render(Button, { props: { children: () => 'Click me' } }); expect(getByText('Click me')).toBeTruthy(); }); it('is disabled when disabled prop is true', () => { const { container } = render(Button, { props: { disabled: true } }); expect(container.querySelector('button')?.disabled).toBe(true); }); it('applies primary variant styles', () => { const { container } = render(Button, { props: { variant: 'primary' } }); expect(container.querySelector('button')?.className).toContain('bg-burnt-oxide'); }); }); ``` **Step 3: Run test to confirm failure** ```bash cd /home/ilia/Documents/Projects/allmywork bun run test src/shared/ui/Button/Button.test.ts ``` Expected: FAIL **Step 4: Implement Button.svelte** ```svelte ``` **Step 5: Create index.ts** ```typescript export { default as Button } from './Button.svelte'; export type { ButtonVariant, ButtonSize } from './types'; ``` **Step 6: Run test to confirm pass** ```bash cd /home/ilia/Documents/Projects/allmywork bun run test src/shared/ui/Button/Button.test.ts ``` Expected: PASS (3 tests) **Step 7: Update shared/ui/index.ts** Open `/home/ilia/Documents/Projects/allmywork/src/shared/ui/index.ts`. Replace the existing content that referenced the flat `Button.svelte` and `Input.svelte` with: ```typescript export * from './Badge'; export * from './Button'; ``` (More exports will be appended as components are added in subsequent tasks.) **Step 8: Remove the placeholder flat files** ```bash rm /home/ilia/Documents/Projects/allmywork/src/shared/ui/Button.svelte rm /home/ilia/Documents/Projects/allmywork/src/shared/ui/Input.svelte ``` Note: `Input.svelte` is removed here because Task 8 will add a proper `Input/` folder. **Step 9: Fix import in HomePage.svelte** Open `/home/ilia/Documents/Projects/allmywork/src/pages/home/ui/HomePage.svelte`. The existing import `import { Button } from '$shared/ui';` should still work because index.ts now re-exports Button. Verify the dev server compiles: ```bash cd /home/ilia/Documents/Projects/allmywork bun run dev & # visit http://localhost:5173 — page should load kill %1 ``` **Step 10: Write Button.stories.svelte** ```svelte {#snippet template(args)} {/snippet} {#snippet template(args)} {/snippet} {#snippet template(args)} {/snippet} {#snippet template(args)} {/snippet} {#snippet template()}
{/snippet}
{#snippet template()} {/snippet} ``` **Step 11: Commit** ```bash cd /home/ilia/Documents/Projects/allmywork git add src/shared/ui/Button/ src/shared/ui/index.ts git commit -m "feat(shared/ui): implement brutalist Button component replacing placeholder" ``` --- ## Task 6: Implement `Card` component **Files:** - Create: `/home/ilia/Documents/Projects/allmywork/src/shared/ui/Card/types.ts` - Create: `/home/ilia/Documents/Projects/allmywork/src/shared/ui/Card/Card.svelte` - Create: `/home/ilia/Documents/Projects/allmywork/src/shared/ui/Card/index.ts` - Create: `/home/ilia/Documents/Projects/allmywork/src/shared/ui/Card/Card.stories.svelte` **Reference prototype:** `/home/ilia/Downloads/High-End Portfolio Design System(1)/src/app/components/Card.tsx` **Step 1: Create types.ts** ```typescript export type CardBackground = 'ochre' | 'slate' | 'white'; ``` **Step 2: Write failing test** Create `/home/ilia/Documents/Projects/allmywork/src/shared/ui/Card/Card.test.ts`: ```typescript import { render } from '@testing-library/svelte'; import { describe, expect, it } from 'vitest'; import Card from './Card.svelte'; describe('Card', () => { it('renders with default ochre background', () => { const { container } = render(Card); expect(container.querySelector('div')?.className).toContain('bg-ochre-clay'); }); it('renders with slate background', () => { const { container } = render(Card, { props: { background: 'slate' } }); expect(container.querySelector('div')?.className).toContain('bg-slate-indigo'); }); it('renders with white background', () => { const { container } = render(Card, { props: { background: 'white' } }); expect(container.querySelector('div')?.className).toContain('bg-white'); }); }); ``` **Step 3: Run test to confirm failure** ```bash cd /home/ilia/Documents/Projects/allmywork bun run test src/shared/ui/Card/Card.test.ts ``` Expected: FAIL **Step 4: Implement Card.svelte** ```svelte
{#if children} {@render children()} {/if}
``` **Step 5: Create index.ts** ```typescript export { default as Card } from './Card.svelte'; export type { CardBackground } from './types'; ``` **Step 6: Run test to confirm pass** ```bash cd /home/ilia/Documents/Projects/allmywork bun run test src/shared/ui/Card/Card.test.ts ``` Expected: PASS (3 tests) **Step 7: Update shared/ui/index.ts** — append: ```typescript export * from './Card'; ``` **Step 8: Write Card.stories.svelte** ```svelte {#snippet template(args)} Card content goes here {/snippet} {#snippet template(args)} Card content goes here {/snippet} {#snippet template(args)} Card content goes here {/snippet} {#snippet template(args)}
Header
Body
{/snippet}
``` **Step 9: Commit** ```bash cd /home/ilia/Documents/Projects/allmywork git add src/shared/ui/Card/ src/shared/ui/index.ts git commit -m "feat(shared/ui): add Card component with background variants and Storybook story" ``` --- ## Task 7: Implement `Section` and `Container` components **Files:** - Create: `/home/ilia/Documents/Projects/allmywork/src/shared/ui/Section/types.ts` - Create: `/home/ilia/Documents/Projects/allmywork/src/shared/ui/Section/Section.svelte` - Create: `/home/ilia/Documents/Projects/allmywork/src/shared/ui/Section/Container.svelte` - Create: `/home/ilia/Documents/Projects/allmywork/src/shared/ui/Section/index.ts` - Create: `/home/ilia/Documents/Projects/allmywork/src/shared/ui/Section/Section.stories.svelte` **Reference prototype:** `/home/ilia/Downloads/High-End Portfolio Design System(1)/src/app/components/Section.tsx` **Step 1: Create types.ts** ```typescript export type SectionBackground = 'ochre' | 'slate' | 'white'; export type ContainerSize = 'default' | 'wide' | 'ultra-wide'; ``` **Step 2: Write failing test** Create `/home/ilia/Documents/Projects/allmywork/src/shared/ui/Section/Section.test.ts`: ```typescript import { render } from '@testing-library/svelte'; import { describe, expect, it } from 'vitest'; import Section from './Section.svelte'; describe('Section', () => { it('renders a
element', () => { const { container } = render(Section); expect(container.querySelector('section')).toBeTruthy(); }); it('applies ochre background by default', () => { const { container } = render(Section); expect(container.querySelector('section')?.className).toContain('bg-ochre-clay'); }); it('applies brutal border when bordered=true', () => { const { container } = render(Section, { props: { bordered: true } }); const cls = container.querySelector('section')?.className ?? ''; expect(cls).toContain('brutal-border-top'); expect(cls).toContain('brutal-border-bottom'); }); }); ``` **Step 3: Run test — expect FAIL** ```bash cd /home/ilia/Documents/Projects/allmywork bun run test src/shared/ui/Section/Section.test.ts ``` **Step 4: Implement Section.svelte** ```svelte
{#if children} {@render children()} {/if}
``` **Step 5: Implement Container.svelte** ```svelte
{#if children} {@render children()} {/if}
``` **Step 6: Create index.ts** ```typescript export { default as Section } from './Section.svelte'; export { default as Container } from './Container.svelte'; export type { SectionBackground, ContainerSize } from './types'; ``` **Step 7: Run test — expect PASS** ```bash cd /home/ilia/Documents/Projects/allmywork bun run test src/shared/ui/Section/Section.test.ts ``` **Step 8: Update shared/ui/index.ts** — append: ```typescript export * from './Section'; ``` **Step 9: Write Section.stories.svelte** ```svelte {#snippet template()}

Section content

{/snippet}
{#snippet template()}

Section content on slate

{/snippet}
{#snippet template()}

Section with brutal top and bottom borders

{/snippet}
``` **Step 10: Commit** ```bash cd /home/ilia/Documents/Projects/allmywork git add src/shared/ui/Section/ src/shared/ui/index.ts git commit -m "feat(shared/ui): add Section and Container components with layout presets" ``` --- ## Task 8: Implement `Input` and `Textarea` components **Files:** - Create: `/home/ilia/Documents/Projects/allmywork/src/shared/ui/Input/types.ts` - Create: `/home/ilia/Documents/Projects/allmywork/src/shared/ui/Input/Input.svelte` - Create: `/home/ilia/Documents/Projects/allmywork/src/shared/ui/Input/Textarea.svelte` - Create: `/home/ilia/Documents/Projects/allmywork/src/shared/ui/Input/index.ts` - Create: `/home/ilia/Documents/Projects/allmywork/src/shared/ui/Input/Input.stories.svelte` **Reference prototype:** `/home/ilia/Downloads/High-End Portfolio Design System(1)/src/app/components/Input.tsx` **Step 1: Create types.ts** ```typescript // No custom types needed beyond HTML attributes; file reserved for future extension. export type InputSize = 'sm' | 'md' | 'lg'; ``` **Step 2: Write failing test** Create `/home/ilia/Documents/Projects/allmywork/src/shared/ui/Input/Input.test.ts`: ```typescript import { render } from '@testing-library/svelte'; import { describe, expect, it } from 'vitest'; import Input from './Input.svelte'; describe('Input', () => { it('renders an element', () => { const { container } = render(Input); expect(container.querySelector('input')).toBeTruthy(); }); it('renders label when label prop is set', () => { const { getByText } = render(Input, { props: { label: 'Email' } }); expect(getByText('Email')).toBeTruthy(); }); it('renders error message when error prop is set', () => { const { getByText } = render(Input, { props: { error: 'Required' } }); expect(getByText('Required')).toBeTruthy(); }); }); ``` **Step 3: Run test — expect FAIL** ```bash cd /home/ilia/Documents/Projects/allmywork bun run test src/shared/ui/Input/Input.test.ts ``` **Step 4: Implement Input.svelte** ```svelte
{#if label} {/if} {#if error} {error} {/if}
``` **Step 5: Implement Textarea.svelte** ```svelte
{#if label} {/if} {#if error} {error} {/if}
``` **Step 6: Create index.ts** ```typescript export { default as Input } from './Input.svelte'; export { default as Textarea } from './Textarea.svelte'; export type { InputSize } from './types'; ``` **Step 7: Run test — expect PASS** ```bash cd /home/ilia/Documents/Projects/allmywork bun run test src/shared/ui/Input/Input.test.ts ``` **Step 8: Update shared/ui/index.ts** — append: ```typescript export * from './Input'; ``` **Step 9: Write Input.stories.svelte** ```svelte {#snippet template()} {/snippet} {#snippet template()} {/snippet} {#snippet template()} {/snippet} {#snippet template()} {/snippet} {#snippet template()}