57 KiB
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):
---
## 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
<script lang="ts">
import { cn } from '$shared/lib/cn';
import type { Snippet } from 'svelte';
import type { HTMLButtonAttributes } from 'svelte/elements';
interface Props extends HTMLButtonAttributes {
variant?: 'primary' | 'secondary';
children: Snippet;
class?: string;
}
let { variant = 'primary', children, class: className, ...rest }: Props = $props();
const classes = $derived(cn(baseStyles, variantStyles[variant], className));
</script>
<button class={classes} {...rest}>
{@render children()}
</button>
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)
- Svelte/SvelteKit built-ins (
svelte,$app/...) - FSD layer aliases (
$shared/...,$entities/..., etc.) - 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
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:
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
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:
"storybook": "storybook dev -p 6006",
"build-storybook": "storybook build"
If missing, add them.
Step 5: Run Storybook to confirm it starts
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
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:
@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
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
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
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:
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
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
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
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:
export { cn } from './cn';
Step 7: Commit
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
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:
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
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
<!--
Component: Badge
Inline label for tags and status indicators. Brutalist border, uppercase, no radius.
-->
<script lang="ts">
import { cn } from '$shared/lib/cn';
import type { Snippet } from 'svelte';
import type { BadgeVariant } from './types';
interface Props {
/** Visual style variant @default 'default' */
variant?: BadgeVariant;
children?: Snippet;
class?: string;
}
let { variant = 'default', children, class: className }: Props = $props();
const variantStyles: Record<BadgeVariant, string> = {
default: 'brutal-border bg-carbon-black text-ochre-clay',
primary: 'brutal-border bg-burnt-oxide text-ochre-clay',
secondary: 'brutal-border bg-slate-indigo text-ochre-clay',
outline: 'brutal-border bg-transparent text-carbon-black',
};
const classes = $derived(cn(
'inline-block px-3 py-1 text-xs uppercase tracking-wider',
variantStyles[variant],
className,
));
</script>
<span class={classes}>
{#if children}
{@render children()}
{/if}
</span>
Step 5: Create index.ts
export { default as Badge } from './Badge.svelte';
export type { BadgeVariant } from './types';
Step 6: Run test to confirm it passes
cd /home/ilia/Documents/Projects/allmywork
bun run test src/shared/ui/Badge/Badge.test.ts
Expected: PASS
Step 7: Write Badge.stories.svelte
<script module>
import { defineMeta } from '@storybook/addon-svelte-csf';
import Badge from './Badge.svelte';
const { Story } = defineMeta({
title: 'Shared/Badge',
component: Badge,
tags: ['autodocs'],
parameters: {
layout: 'centered',
docs: { description: { component: 'Brutalist inline label. Uppercase, 3px border, zero radius.' } },
},
argTypes: {
variant: {
control: 'select',
options: ['default', 'primary', 'secondary', 'outline'],
},
},
});
</script>
<Story name="Default">
{#snippet template(args)}
<Badge {...args}>Tag</Badge>
{/snippet}
</Story>
<Story name="Primary" args={{ variant: 'primary' }}>
{#snippet template(args)}
<Badge {...args}>Primary</Badge>
{/snippet}
</Story>
<Story name="Secondary" args={{ variant: 'secondary' }}>
{#snippet template(args)}
<Badge {...args}>Secondary</Badge>
{/snippet}
</Story>
<Story name="Outline" args={{ variant: 'outline' }}>
{#snippet template(args)}
<Badge {...args}>Outline</Badge>
{/snippet}
</Story>
<Story name="All variants">
{#snippet template()}
<div class="flex gap-3 flex-wrap">
<Badge variant="default">Default</Badge>
<Badge variant="primary">Primary</Badge>
<Badge variant="secondary">Secondary</Badge>
<Badge variant="outline">Outline</Badge>
</div>
{/snippet}
</Story>
Step 8: Commit
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
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:
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
cd /home/ilia/Documents/Projects/allmywork
bun run test src/shared/ui/Button/Button.test.ts
Expected: FAIL
Step 4: Implement Button.svelte
<!--
Component: Button
Brutalist button. 3px border, hard shadow on hover, uppercase, zero radius.
Hover: translates 2px down-right with reduced shadow for press feel.
-->
<script lang="ts">
import { cn } from '$shared/lib/cn';
import type { Snippet } from 'svelte';
import type { HTMLButtonAttributes } from 'svelte/elements';
import type { ButtonSize, ButtonVariant } from './types';
interface Props extends HTMLButtonAttributes {
/** Visual style @default 'primary' */
variant?: ButtonVariant;
/** Size preset @default 'md' */
size?: ButtonSize;
children?: Snippet;
class?: string;
}
let {
variant = 'primary',
size = 'md',
children,
class: className,
type = 'button',
disabled,
...rest
}: Props = $props();
const base =
'brutal-border transition-all duration-200 ' +
'hover:translate-x-[2px] hover:translate-y-[2px] hover:shadow-[6px_6px_0_var(--carbon-black)] ' +
'active:translate-x-[4px] active:translate-y-[4px] active:shadow-[4px_4px_0_var(--carbon-black)] ' +
'uppercase tracking-wider inline-flex items-center justify-center ' +
'disabled:opacity-50 disabled:cursor-not-allowed disabled:pointer-events-none';
const variantStyles: Record<ButtonVariant, string> = {
primary: 'brutal-shadow bg-burnt-oxide text-ochre-clay',
secondary: 'brutal-shadow bg-slate-indigo text-ochre-clay',
outline: 'brutal-shadow bg-transparent text-carbon-black',
ghost: 'bg-ochre-clay text-carbon-black shadow-none hover:shadow-[6px_6px_0_var(--carbon-black)]',
};
const sizeStyles: Record<ButtonSize, string> = {
sm: 'px-4 py-2 text-sm',
md: 'px-6 py-3 text-base',
lg: 'px-8 py-4 text-lg',
};
const classes = $derived(cn(base, variantStyles[variant], sizeStyles[size], className));
</script>
<button {type} {disabled} class={classes} {...rest}>
{#if children}
{@render children()}
{/if}
</button>
Step 5: Create index.ts
export { default as Button } from './Button.svelte';
export type { ButtonVariant, ButtonSize } from './types';
Step 6: Run test to confirm pass
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:
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
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:
cd /home/ilia/Documents/Projects/allmywork
bun run dev &
# visit http://localhost:5173 — page should load
kill %1
Step 10: Write Button.stories.svelte
<script module>
import { defineMeta } from '@storybook/addon-svelte-csf';
import Button from './Button.svelte';
const { Story } = defineMeta({
title: 'Shared/Button',
component: Button,
tags: ['autodocs'],
parameters: {
layout: 'centered',
docs: { description: { component: 'Brutalist CTA button. Hard shadow, uppercase, hover translates 2px.' } },
},
argTypes: {
variant: { control: 'select', options: ['primary', 'secondary', 'outline', 'ghost'] },
size: { control: 'select', options: ['sm', 'md', 'lg'] },
},
});
</script>
<Story name="Primary" args={{ variant: 'primary', size: 'md' }}>
{#snippet template(args)}
<Button {...args}>Primary</Button>
{/snippet}
</Story>
<Story name="Secondary" args={{ variant: 'secondary', size: 'md' }}>
{#snippet template(args)}
<Button {...args}>Secondary</Button>
{/snippet}
</Story>
<Story name="Outline" args={{ variant: 'outline', size: 'md' }}>
{#snippet template(args)}
<Button {...args}>Outline</Button>
{/snippet}
</Story>
<Story name="Ghost" args={{ variant: 'ghost', size: 'md' }}>
{#snippet template(args)}
<Button {...args}>Ghost</Button>
{/snippet}
</Story>
<Story name="All sizes">
{#snippet template()}
<div class="flex gap-4 items-center">
<Button size="sm">Small</Button>
<Button size="md">Medium</Button>
<Button size="lg">Large</Button>
</div>
{/snippet}
</Story>
<Story name="Disabled">
{#snippet template()}
<Button disabled>Disabled</Button>
{/snippet}
</Story>
Step 11: Commit
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
export type CardBackground = 'ochre' | 'slate' | 'white';
Step 2: Write failing test
Create /home/ilia/Documents/Projects/allmywork/src/shared/ui/Card/Card.test.ts:
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
cd /home/ilia/Documents/Projects/allmywork
bun run test src/shared/ui/Card/Card.test.ts
Expected: FAIL
Step 4: Implement Card.svelte
<!--
Component: Card
Brutalist content container. 3px border, 8px hard shadow, zero radius.
Backgrounds: ochre (default), slate (dark), white.
-->
<script lang="ts">
import { cn } from '$shared/lib/cn';
import type { Snippet } from 'svelte';
import type { CardBackground } from './types';
interface Props {
/** Background color preset @default 'ochre' */
background?: CardBackground;
/** Remove default padding @default false */
noPadding?: boolean;
children?: Snippet;
class?: string;
}
let { background = 'ochre', noPadding = false, children, class: className }: Props = $props();
const backgroundStyles: Record<CardBackground, string> = {
ochre: 'bg-ochre-clay text-carbon-black',
slate: 'bg-slate-indigo text-ochre-clay',
white: 'bg-white text-carbon-black',
};
const classes = $derived(cn(
'brutal-border brutal-shadow',
backgroundStyles[background],
!noPadding && 'p-6 md:p-8',
className,
));
</script>
<div class={classes}>
{#if children}
{@render children()}
{/if}
</div>
Step 5: Create index.ts
export { default as Card } from './Card.svelte';
export type { CardBackground } from './types';
Step 6: Run test to confirm pass
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:
export * from './Card';
Step 8: Write Card.stories.svelte
<script module>
import { defineMeta } from '@storybook/addon-svelte-csf';
import Card from './Card.svelte';
const { Story } = defineMeta({
title: 'Shared/Card',
component: Card,
tags: ['autodocs'],
parameters: {
layout: 'padded',
docs: { description: { component: 'Brutalist card. 3px border, hard shadow, three background presets.' } },
},
argTypes: {
background: { control: 'select', options: ['ochre', 'slate', 'white'] },
noPadding: { control: 'boolean' },
},
});
</script>
<Story name="Ochre (default)" args={{ background: 'ochre' }}>
{#snippet template(args)}
<Card {...args}>Card content goes here</Card>
{/snippet}
</Story>
<Story name="Slate" args={{ background: 'slate' }}>
{#snippet template(args)}
<Card {...args}>Card content goes here</Card>
{/snippet}
</Story>
<Story name="White" args={{ background: 'white' }}>
{#snippet template(args)}
<Card {...args}>Card content goes here</Card>
{/snippet}
</Story>
<Story name="No Padding" args={{ noPadding: true }}>
{#snippet template(args)}
<Card {...args}>
<div class="p-4 brutal-border-bottom">Header</div>
<div class="p-4">Body</div>
</Card>
{/snippet}
</Story>
Step 9: Commit
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
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:
import { render } from '@testing-library/svelte';
import { describe, expect, it } from 'vitest';
import Section from './Section.svelte';
describe('Section', () => {
it('renders a <section> 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
cd /home/ilia/Documents/Projects/allmywork
bun run test src/shared/ui/Section/Section.test.ts
Step 4: Implement Section.svelte
<!--
Component: Section
Full-width page section. Controls background and optional top/bottom brutal borders.
-->
<script lang="ts">
import { cn } from '$shared/lib/cn';
import type { Snippet } from 'svelte';
import type { SectionBackground } from './types';
interface Props {
background?: SectionBackground;
/** Add brutal border on top and bottom @default false */
bordered?: boolean;
children?: Snippet;
class?: string;
}
let { background = 'ochre', bordered = false, children, class: className }: Props = $props();
const backgroundStyles: Record<SectionBackground, string> = {
ochre: 'bg-ochre-clay text-carbon-black',
slate: 'bg-slate-indigo text-ochre-clay',
white: 'bg-white text-carbon-black',
};
const classes = $derived(cn(
backgroundStyles[background],
bordered && 'brutal-border-top brutal-border-bottom',
className,
));
</script>
<section class={classes}>
{#if children}
{@render children()}
{/if}
</section>
Step 5: Implement Container.svelte
<!--
Component: Container
Centered max-width wrapper. Three size presets for responsive layouts up to 4K.
-->
<script lang="ts">
import { cn } from '$shared/lib/cn';
import type { Snippet } from 'svelte';
import type { ContainerSize } from './types';
interface Props {
/** Max-width preset @default 'default' */
size?: ContainerSize;
children?: Snippet;
class?: string;
}
let { size = 'default', children, class: className }: Props = $props();
const sizeStyles: Record<ContainerSize, string> = {
'default': 'max-w-7xl',
'wide': 'max-w-[1920px]',
'ultra-wide': 'max-w-[2560px]',
};
const classes = $derived(cn('mx-auto px-6 md:px-12 lg:px-16', sizeStyles[size], className));
</script>
<div class={classes}>
{#if children}
{@render children()}
{/if}
</div>
Step 6: Create index.ts
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
cd /home/ilia/Documents/Projects/allmywork
bun run test src/shared/ui/Section/Section.test.ts
Step 8: Update shared/ui/index.ts — append:
export * from './Section';
Step 9: Write Section.stories.svelte
<script module>
import { defineMeta } from '@storybook/addon-svelte-csf';
import Container from './Container.svelte';
import Section from './Section.svelte';
const { Story } = defineMeta({
title: 'Shared/Section',
component: Section,
tags: ['autodocs'],
parameters: { layout: 'fullscreen' },
});
</script>
<Story name="Ochre (default)">
{#snippet template()}
<Section>
<Container>
<p class="py-16">Section content</p>
</Container>
</Section>
{/snippet}
</Story>
<Story name="Slate">
{#snippet template()}
<Section background="slate">
<Container>
<p class="py-16">Section content on slate</p>
</Container>
</Section>
{/snippet}
</Story>
<Story name="Bordered">
{#snippet template()}
<Section bordered>
<Container>
<p class="py-16">Section with brutal top and bottom borders</p>
</Container>
</Section>
{/snippet}
</Story>
Step 10: Commit
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
// 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:
import { render } from '@testing-library/svelte';
import { describe, expect, it } from 'vitest';
import Input from './Input.svelte';
describe('Input', () => {
it('renders an <input> 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
cd /home/ilia/Documents/Projects/allmywork
bun run test src/shared/ui/Input/Input.test.ts
Step 4: Implement Input.svelte
<!--
Component: Input
Brutalist text input. 3px border, white background, burnt-oxide focus ring.
Optional label (uppercase) and inline error message.
-->
<script lang="ts">
import { cn } from '$shared/lib/cn';
import type { HTMLInputAttributes } from 'svelte/elements';
interface Props extends HTMLInputAttributes {
/** Uppercase label rendered above the input */
label?: string;
/** Inline error message rendered below the input */
error?: string;
class?: string;
}
let { label, error, class: className, ...rest }: Props = $props();
const inputClasses = $derived(cn(
'brutal-border bg-white px-4 py-3 text-carbon-black w-full',
'focus:outline-none focus:ring-2 focus:ring-burnt-oxide focus:ring-offset-2 focus:ring-offset-ochre-clay',
'transition-all duration-150',
'disabled:opacity-50 disabled:cursor-not-allowed',
className,
));
</script>
<div class="flex flex-col gap-2">
{#if label}
<label class="text-carbon-black">{label}</label>
{/if}
<input class={inputClasses} {...rest} />
{#if error}
<span class="text-sm text-burnt-oxide">{error}</span>
{/if}
</div>
Step 5: Implement Textarea.svelte
<!--
Component: Textarea
Brutalist multi-line input. Same styling as Input but <textarea>.
Non-resizable by default (matches prototype).
-->
<script lang="ts">
import { cn } from '$shared/lib/cn';
import type { HTMLTextareaAttributes } from 'svelte/elements';
interface Props extends HTMLTextareaAttributes {
label?: string;
error?: string;
rows?: number;
class?: string;
}
let { label, error, rows = 4, class: className, ...rest }: Props = $props();
const textareaClasses = $derived(cn(
'brutal-border bg-white px-4 py-3 text-carbon-black w-full resize-none',
'focus:outline-none focus:ring-2 focus:ring-burnt-oxide focus:ring-offset-2 focus:ring-offset-ochre-clay',
'transition-all duration-150',
'disabled:opacity-50 disabled:cursor-not-allowed',
className,
));
</script>
<div class="flex flex-col gap-2">
{#if label}
<label class="text-carbon-black">{label}</label>
{/if}
<textarea {rows} class={textareaClasses} {...rest}></textarea>
{#if error}
<span class="text-sm text-burnt-oxide">{error}</span>
{/if}
</div>
Step 6: Create index.ts
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
cd /home/ilia/Documents/Projects/allmywork
bun run test src/shared/ui/Input/Input.test.ts
Step 8: Update shared/ui/index.ts — append:
export * from './Input';
Step 9: Write Input.stories.svelte
<script module>
import { defineMeta } from '@storybook/addon-svelte-csf';
import Input from './Input.svelte';
import Textarea from './Textarea.svelte';
const { Story } = defineMeta({
title: 'Shared/Input',
component: Input,
tags: ['autodocs'],
parameters: {
layout: 'padded',
docs: { description: { component: 'Brutalist text input with optional label and error state.' } },
},
});
</script>
<Story name="Default">
{#snippet template()}
<Input placeholder="Enter text..." />
{/snippet}
</Story>
<Story name="With Label">
{#snippet template()}
<Input label="Email address" type="email" placeholder="you@example.com" />
{/snippet}
</Story>
<Story name="With Error">
{#snippet template()}
<Input label="Email address" type="email" value="bad-email" error="Please enter a valid email address." />
{/snippet}
</Story>
<Story name="Disabled">
{#snippet template()}
<Input label="Read-only field" disabled value="Cannot edit this" />
{/snippet}
</Story>
<Story name="Textarea">
{#snippet template()}
<Textarea label="Message" placeholder="Write your message..." rows={5} />
{/snippet}
</Story>
<Story name="Textarea with Error">
{#snippet template()}
<Textarea label="Message" error="Message is required." />
{/snippet}
</Story>
Step 10: Commit
cd /home/ilia/Documents/Projects/allmywork
git add src/shared/ui/Input/ src/shared/ui/index.ts
git commit -m "feat(shared/ui): add Input and Textarea components with label and error states"
Task 9: Implement TechStackBrick component
Files:
- Create:
/home/ilia/Documents/Projects/allmywork/src/shared/ui/TechStackBrick/TechStackBrick.svelte - Create:
/home/ilia/Documents/Projects/allmywork/src/shared/ui/TechStackBrick/TechStackGrid.svelte - Create:
/home/ilia/Documents/Projects/allmywork/src/shared/ui/TechStackBrick/index.ts - Create:
/home/ilia/Documents/Projects/allmywork/src/shared/ui/TechStackBrick/TechStackBrick.stories.svelte
Reference prototype: /home/ilia/Downloads/High-End Portfolio Design System(1)/src/app/components/TechStackBrick.tsx
Step 1: Write failing test
Create /home/ilia/Documents/Projects/allmywork/src/shared/ui/TechStackBrick/TechStackBrick.test.ts:
import { render } from '@testing-library/svelte';
import { describe, expect, it } from 'vitest';
import TechStackBrick from './TechStackBrick.svelte';
describe('TechStackBrick', () => {
it('renders the skill name', () => {
const { getByText } = render(TechStackBrick, { props: { name: 'SvelteKit' } });
expect(getByText('SvelteKit')).toBeTruthy();
});
it('uppercases the skill name via CSS class', () => {
const { container } = render(TechStackBrick, { props: { name: 'React' } });
expect(container.querySelector('span')?.className).toContain('uppercase');
});
});
Step 2: Run test — expect FAIL
cd /home/ilia/Documents/Projects/allmywork
bun run test src/shared/ui/TechStackBrick/TechStackBrick.test.ts
Step 3: Implement TechStackBrick.svelte
<!--
Component: TechStackBrick
Single skill pill. Brutal border, white bg, hard shadow that collapses on hover (lifted press feel).
-->
<script lang="ts">
import { cn } from '$shared/lib/cn';
interface Props {
name: string;
class?: string;
}
let { name, class: className }: Props = $props();
const classes = $derived(cn(
'brutal-border brutal-shadow bg-white px-4 py-3 text-center',
'transition-all duration-200',
'hover:shadow-none hover:translate-x-[2px] hover:translate-y-[2px]',
className,
));
</script>
<div class={classes}>
<span class="text-sm uppercase tracking-wide">{name}</span>
</div>
Step 4: Implement TechStackGrid.svelte
<!--
Component: TechStackGrid
Responsive grid of TechStackBrick items. Adjusts columns from 2 to 6 across breakpoints.
-->
<script lang="ts">
import { cn } from '$shared/lib/cn';
import TechStackBrick from './TechStackBrick.svelte';
interface Props {
skills: string[];
class?: string;
}
let { skills, class: className }: Props = $props();
const classes = $derived(cn(
'grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 xl:grid-cols-6 gap-4',
className,
));
</script>
<div class={classes}>
{#each skills as skill (skill)}
<TechStackBrick name={skill} />
{/each}
</div>
Step 5: Create index.ts
export { default as TechStackBrick } from './TechStackBrick.svelte';
export { default as TechStackGrid } from './TechStackGrid.svelte';
Step 6: Run test — expect PASS
cd /home/ilia/Documents/Projects/allmywork
bun run test src/shared/ui/TechStackBrick/TechStackBrick.test.ts
Step 7: Update shared/ui/index.ts — append:
export * from './TechStackBrick';
Step 8: Write TechStackBrick.stories.svelte
<script module>
import { defineMeta } from '@storybook/addon-svelte-csf';
import TechStackBrick from './TechStackBrick.svelte';
import TechStackGrid from './TechStackGrid.svelte';
const { Story } = defineMeta({
title: 'Shared/TechStackBrick',
component: TechStackBrick,
tags: ['autodocs'],
parameters: {
layout: 'padded',
docs: { description: { component: 'Skill badge for the capabilities section. Hover removes shadow for a tactile press.' } },
},
});
</script>
<Story name="Single Brick">
{#snippet template()}
<TechStackBrick name="SvelteKit" />
{/snippet}
</Story>
<Story name="Grid">
{#snippet template()}
<TechStackGrid skills={['SvelteKit', 'TypeScript', 'Tailwind', 'Bun', 'Figma', 'Storybook', 'Vitest', 'Playwright']} />
{/snippet}
</Story>
Step 9: Commit
cd /home/ilia/Documents/Projects/allmywork
git add src/shared/ui/TechStackBrick/ src/shared/ui/index.ts
git commit -m "feat(shared/ui): add TechStackBrick and TechStackGrid components"
Task 10: Run all tests and validate Storybook
This is a verification gate — no new code is written, only validation.
Step 1: Run full unit test suite
cd /home/ilia/Documents/Projects/allmywork
bun run test
Expected: All tests pass. Note any failures and fix them in the relevant component task before continuing.
Step 2: Type-check
cd /home/ilia/Documents/Projects/allmywork
bun run check
Expected: Zero errors. If svelte-check reports errors in .stories.svelte files related to defineMeta, confirm @storybook/addon-svelte-csf types are installed:
bun add -D @storybook/addon-svelte-csf
Step 3: Lint all files
cd /home/ilia/Documents/Projects/allmywork
bun run lint
Expected: Zero errors. Fix any Biome lint warnings (unused imports, etc.) before continuing.
Step 4: Start Storybook and verify all stories render
cd /home/ilia/Documents/Projects/allmywork
bun run storybook
Visit http://localhost:6006. Confirm all stories appear in the sidebar:
- Shared/Badge — 5 stories
- Shared/Button — 6 stories
- Shared/Card — 4 stories
- Shared/Section — 3 stories
- Shared/Input — 6 stories
- Shared/TechStackBrick — 2 stories
Step 5: Commit any fixes
cd /home/ilia/Documents/Projects/allmywork
git add -A
git commit -m "fix(shared/ui): resolve type errors and lint warnings from verification pass"
Task 11: Update HomePage to use the design system
Replace the placeholder HomePage.svelte with a layout that demonstrates the design system is wired up and functional. This is not the final portfolio content — it's a foundation smoke test.
Files:
- Modify:
/home/ilia/Documents/Projects/allmywork/src/pages/home/ui/HomePage.svelte
Step 1: Write HomePage.svelte
<!--
Page: HomePage
Smoke test page that renders key shared/ui components to verify the design system.
To be replaced with real portfolio content in a future plan.
-->
<script lang="ts">
import { Badge } from '$shared/ui/Badge';
import { Button } from '$shared/ui/Button';
import { Card } from '$shared/ui/Card';
import { Container, Section } from '$shared/ui/Section';
import { TechStackGrid } from '$shared/ui/TechStackBrick';
</script>
<Section>
<Container>
<div class="py-20 space-y-12">
<!-- Heading -->
<div>
<h1>allmy.work</h1>
<div class="brutal-border-top pt-4 mt-4">
<p class="text-sm uppercase tracking-wider opacity-60">Portfolio Foundation — Design System Active</p>
</div>
</div>
<!-- Cards row -->
<div class="grid md:grid-cols-3 gap-6">
<Card background="slate" noPadding class="p-6">
<p class="text-sm opacity-60 mb-2">Stack</p>
<p>SvelteKit + Bun</p>
</Card>
<Card background="slate" noPadding class="p-6">
<p class="text-sm opacity-60 mb-2">Styling</p>
<p>Tailwind CSS v4</p>
</Card>
<Card background="slate" noPadding class="p-6">
<p class="text-sm opacity-60 mb-2">Components</p>
<p>Shared UI ready</p>
</Card>
</div>
<!-- Badges -->
<div class="flex gap-3 flex-wrap">
<Badge variant="default">SvelteKit</Badge>
<Badge variant="primary">Brutalist</Badge>
<Badge variant="secondary">FSD</Badge>
<Badge variant="outline">Open Source</Badge>
</div>
<!-- Buttons -->
<div class="flex gap-4 flex-wrap">
<Button variant="primary">View Work</Button>
<Button variant="secondary">About</Button>
<Button variant="outline">Contact</Button>
<Button variant="ghost">Learn More</Button>
</div>
<!-- Tech stack -->
<Card background="white">
<h4 class="mb-6">Technologies</h4>
<TechStackGrid skills={['SvelteKit', 'TypeScript', 'Tailwind', 'Bun', 'Storybook', 'Vitest']} />
</Card>
</div>
</Container>
</Section>
Step 2: Start dev server and verify
cd /home/ilia/Documents/Projects/allmywork
bun run dev
Visit http://localhost:5173. Confirm:
- Ochre-clay background renders
- Fraunces font loads for the h1
- Badge, Button, Card, TechStackGrid components render correctly
- No console errors
Step 3: Commit
cd /home/ilia/Documents/Projects/allmywork
git add src/pages/home/ui/HomePage.svelte
git commit -m "feat(pages/home): wire up design system components in HomePage smoke test"
Completion Checklist
Before declaring this plan complete, verify every item:
CLAUDE.mdincludes code style section- Storybook starts without errors
bun run test— all tests passbun run check— zero TypeScript errorsbun run lint— zero Biome errorsbun run dev— dev server compiles and page renders with ochre background and Fraunces font- Storybook shows stories for: Badge, Button, Card, Section, Input, TechStackBrick
- All components use Svelte 5 runes (
$props,$derived) — noexport let - All components import via FSD aliases (
$shared/...) - All commits follow
prefix(scope): descriptionconvention
Reference Paths
| Resource | Path |
|---|---|
| Figma prototype root | /home/ilia/Downloads/High-End Portfolio Design System(1)/ |
| Prototype theme.css | /home/ilia/Downloads/High-End Portfolio Design System(1)/src/styles/theme.css |
| Prototype components | /home/ilia/Downloads/High-End Portfolio Design System(1)/src/app/components/ |
| glyphdiff Button (reference) | /home/ilia/Documents/Projects/glyphdiff/src/shared/ui/Button/Button.svelte |
| glyphdiff stories (reference) | /home/ilia/Documents/Projects/glyphdiff/src/shared/ui/Button/Button.stories.svelte |
| Project root | /home/ilia/Documents/Projects/allmywork/ |
| Shared UI | /home/ilia/Documents/Projects/allmywork/src/shared/ui/ |
| App CSS | /home/ilia/Documents/Projects/allmywork/src/app/styles/app.css |
| Svelte config | /home/ilia/Documents/Projects/allmywork/svelte.config.js |
| FSD aliases | $shared, $pages, $features, $entities, $widgets |