Component Library (Storybook)¶
The portfolio uses Storybook 10 as an interactive component library and visual testing environment.
What's included¶
| Category | Stories |
|---|---|
| UI Primitives | Button, Card, Badge, Input, Tabs, Avatar, Alert, Dialog, SkillBar |
| Portfolio Sections | HeroSection (with role cycler variants), ProjectsSection, ExperienceSection, SkillsSection, AwardsSection, CertificationsSection, CVDownloadDialog |
| Layout | Navigation (with sliding pill variants), Footer |
OneDNA Design System¶
The stories document the portfolio's OneDNA-aligned design tokens and brand components:
| Token / Component | Value | Usage |
|---|---|---|
--onedna-pink |
hsl(334, 75%, 52%) (#E02878) |
Primary accent, progress bars, active nav pill |
--onedna-pink/5 |
5% opacity tint | Skill bar hover background |
| Roboto font | @font-face in index.css, files in public/ |
All body text |
SkillBar |
client/src/components/ui/skill-bar.tsx |
Proficiency visualisation (bar / segmented / dots variants) |
SkillList |
Same file | Renders a list of SkillBar items with staggered entrance animation |
SkillBar variants¶
SkillBar supports three visual variants and two data modes:
| Prop | Values | Description |
|---|---|---|
variant |
bar | segmented | dots |
Visual style of the proficiency track |
mode |
continuous | discrete |
continuous = 0–100 percentage; discrete = 1–N integer |
value |
number | Current proficiency value |
max |
number (default 5) | Max for discrete mode |
showCaption |
boolean | Shows auto-resolved label (Beginner → Expert) |
indexDelay |
ms | Staggered entrance delay for list animations |
Entrance animation triggers on scroll via IntersectionObserver — the bar fills from 0% once it enters the viewport.
Addons¶
| Addon | Purpose |
|---|---|
addon-a11y |
Accessibility audit on every story |
addon-docs |
Auto-generated component documentation from JSDoc + story args |
addon-vitest |
Run play function interaction tests inside the browser |
@chromatic-com/storybook |
Visual regression snapshot testing |
Running locally¶
bash
Via docker-compose (full stack):
Writing stories¶
Stories live in client/src/stories/. Each file follows the Component Story Format (CSF):
tsx
import type { Meta, StoryObj } from "@storybook/react-vite";
import { expect, fn, userEvent, within } from "storybook/test";
import { Button } from "@/components/ui/button";
const meta: Meta<typeof Button> = {
title: "UI/Button",
component: Button,
tags: ["autodocs"],
args: { onClick: fn() },
};
export default meta;
export const Default: StoryObj<typeof meta> = {
args: { children: "Click me" },
play: async ({ args, canvasElement }) => {
const canvas = within(canvasElement);
await userEvent.click(canvas.getByRole("button"));
await expect(args.onClick).toHaveBeenCalledOnce();
},
};
Portfolio section stories (HeroSection, ProjectsSection, etc.) wrap components in the required providers:
tsx
Page history
| Field | Value |
|---|---|
| Last updated | 2026-05-19 |
Changelog
| Date | PRs | Summary |
|---|---|---|
| 2026-05-19 | #390, #392, #393, #454 | Add SkillBar, AwardsSection, CertificationsSection to stories table; add OneDNA Design System section; update provider wrapper example to include role cycler flag |