Skip to content

Component Library (Storybook)

The portfolio uses Storybook 10 as an interactive component library and visual testing environment.

Open Component Library


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
npm run storybook          # dev server at http://localhost:6006
npm run storybook:build    # static build → storybook-static/

Via docker-compose (full stack):

bash
docker-compose up storybook
# → http://localhost:6006

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
decorators: [
  (Story) => (
    <QueryClientProvider client={queryClient}>
      <FeatureFlagsContext.Provider value={{ flags: { cvGeneration: true, portfolioContentApi: true, roleCycler: true }, refresh: () => {} }}>
        <Router><Story /></Router>
      </FeatureFlagsContext.Provider>
    </QueryClientProvider>
  ),
],

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