Skip to content

Comments

feat: add custom storybook for design system#784

Open
edwche10 wants to merge 7 commits intocjpais:mainfrom
edwche10:feat/storybook
Open

feat: add custom storybook for design system#784
edwche10 wants to merge 7 commits intocjpais:mainfrom
edwche10:feat/storybook

Conversation

@edwche10
Copy link

Adds a lightweight custom storybook showcasing Handy's UI components, icons, shared components, and design tokens (colors + typography). Supports light and dark mode. Run with bun run storybook.

Before Submitting This PR

Please confirm you have done the following:

If this is a feature or change that was previously closed/rejected:

  • I have explained in the description below why this should be reconsidered
  • I have gathered community feedback (link to discussion below)

Summary

Adds a custom storybook under storybook/ to preview Handy's UI components in isolation.

Human Written Description

I noticed the project didn't have a way to preview UI components in isolation, which makes it hard to work on the design system. I built a custom storybook that shows all the UI components, icons, and design tokens (colors + typography) with light and dark mode support. This makes it easier for the team to reference and work with the components without needing to run the full app.

How to run

bun install
bun run storybook
Then open http://localhost:1422

What's included

  • Design Tokens — Colors and Typography
  • Icons — All 6 icons
  • UI — All 16 UI components with variants
  • Shared — ProgressBar

Related Issues/Discussions

Fixes #
Discussion:

Community Feedback

Testing

Screenshots/Videos (if applicable)

AI Assistance

  • No AI was used in this PR
  • AI was used (please describe below)
  • Claude code. I am not known how to write code. I am a designer. So I did everything with Claude Code, aka Vibe Coding.

If AI was used:

  • Tools used:
  • Claude Code
  • How extensively:
    Helped build the storybook structure, fix Tailwind configuration, and set up the Vite config

Adds a lightweight custom storybook showcasing Handy's UI components,
icons, shared components, and design tokens (colors + typography).
Supports light and dark mode. Run with `bun run storybook`.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Copy link
Contributor

@VirenMohindra VirenMohindra left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hey edward, appreciate the effort here — having a component playground is really valuable for the team. i have some structural feedback that i think will make this much easier to maintain long-term. the main themes are~

  1. using the actual storybook library instead of a custom one,
  2. colocating stories next to components, and
  3. only show tokens / styles that actually exist in the app today

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is a custom ~750-line react app reimplementing what https://storybook.js.org/ gives us out of the box. with real storybook we get: interactive controls panel for tweaking props, auto-generated prop docs from typescript interfaces, a11y auditing, viewport testing, and a standard story format (CSF) that's well-documented

for a tauri app, the setup involves configuring vite aliases to mock the native bridge, but that's straightforward and you've already written the mock files (they just aren't wired up)

i would recommend using @storybook/react-vite with the existing vite config as a base. each component then gets a ComponentName.stories.tsx file colocated next to it like so~

  src/components/ui/
  ├── Button.tsx
  ├── Button.stories.tsx
  ├── Input.tsx
  ├── Input.stories.tsx
  └── ...

this makes stories discoverable, keeps them in sync with component changes, and scales much better than a single monolith file

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is a new component introduced in this PR but it's not used anywhere in the actual app — only in the react storybook. a storybook PR shouldn't introduce new components that aren't consumed by the app yet. i'd recommend either removing this from this PR (and adding it in a separate PR where app code actually uses it) or updating existing app code to use it here

leaning towards the latter

screenshots would be super important here!

Copy link
Author

@edwche10 edwche10 Feb 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed IconButton from this PR to keep the scope focused. I noticed the existing ResetButton does a similar job — once the real Storybook setup is in place, we can revisit consolidating them into IconButton in a follow-up PR.

I had an image of the ResetButton that exists today in the app, but at the end, it's an icon bottom.

Image

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this file defines ~50+ CSS custom properties (--color-error, --color-warning, --color-info, --color-success, all the --color-primary-alpha-, --color-gray-alpha-, --color-text-alpha-* variants) that don't exist in the app's actual App.css. no component in src/components/ui/ uses these tokens either — they use hardcoded tailwind classes (e.g. Alert.tsx uses bg-red-500/10, not var(--color-error-bg))

showing these in the storybook's color palette is misleading — someone would think these tokens exist in the design system when they don't. the storybook should either

  1. @import the app's actual CSS and only add its own chrome tokens (--sb-*), or
  2. just use tailwind classes

if we want to adopt semantic design tokens, that should be a separate effort where we update App.css and migrate components to use them

Comment on lines 174 to 190
id: "typography",
name: "Typography",
group: "Design Tokens",
render: () => {
const typeScale = [
{ label: "Display", size: "28px", weight: "700", lineHeight: "1.2", sample: "Handy — Voice to Text" },
{ label: "Heading 1", size: "22px", weight: "600", lineHeight: "1.3", sample: "Settings & Preferences" },
{ label: "Heading 2", size: "18px", weight: "600", lineHeight: "1.3", sample: "General Settings" },
{ label: "Heading 3", size: "15px", weight: "600", lineHeight: "1.4", sample: "Recording Options" },
{ label: "Body", size: "14px", weight: "400", lineHeight: "1.5", sample: "Select your preferred microphone and configure how recordings are handled." },
{ label: "Body Medium", size: "14px", weight: "500", lineHeight: "1.5", sample: "Push to talk is enabled" },
{ label: "Small", size: "13px", weight: "400", lineHeight: "1.5", sample: "Choose an audio input device from the list below." },
{ label: "Small Medium", size: "13px", weight: "500", lineHeight: "1.5", sample: "Model downloaded successfully" },
{ label: "Caption", size: "12px", weight: "400", lineHeight: "1.4", sample: "Last updated 3 minutes ago" },
{ label: "Label", size: "11px", weight: "600", lineHeight: "1.4", sample: "AUDIO INPUT", style: "uppercase" as const, letterSpacing: "0.05em" },
{ label: "Mono", size: "13px", weight: "400", lineHeight: "1.5", sample: "sk-ant-api03-xxxxxxxxxxxxxx", fontFamily: "monospace" },
];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

similar to the color tokens issue — this type scale (display 28px/700, heading 1 22px/600, etc) isn't defined or enforced anywhere in the codebase. there are no tailwind config entries, css classes, or utility tokens backing these values. this presents aspirational design as the current reality, which can cause confusion

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this file (along with settingsStore.ts, modelStore.ts, and all 9 tauri-plugin-*.ts mock files) is never imported anywhere in the storybook. that's ~270 lines of dead code across 11 files. the only mock files actually used are data.ts and bindings.ts

i'd remove all the unused ones to keep the PR lean

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. Removed all unused Storybook mock files and kept only data.ts and bindings.ts.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we only removed a single mock, we need to remove all the unused ones

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed all 13 unused mock files. Only data.ts and bindings.ts remain — these are the only two actually used by the storybook.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the tauri plugin mock files exist in storybook/mocks/ but they're never aliased here, so they don't actually intercept any tauri imports. if any component further down the import chain tries to call @tauri-apps/api/core or @tauri-apps/plugin-os, it'll hit the real (missing) native bridge and crash. with real storybook + proper vite aliases, this would look something like~

  alias: {
    "@tauri-apps/api/core": resolve(__dirname, "./storybook/mocks/tauri-apps-api-core.ts"),
    "@tauri-apps/api/event": resolve(__dirname, "./storybook/mocks/tauri-apps-api-event.ts"),
    // ... etc
  }

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this references http://localhost:1422/HandyMain/storybook/ but the actual URL is http://localhost:1422/

minor but would confuse someone opening the html file directly

this will anyway get removed once we use storybook properly

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed! The URL now correctly points to http://localhost:1422/

"test:playwright:ui": "playwright test --ui",
"check:translations": "bun scripts/check-translations.ts"
"check:translations": "bun scripts/check-translations.ts",
"storybook": "vite --config vite.storybook.config.ts"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

README.md and CLAUDE.md should be updated to document this new command and how to use the storybook. right now the only mention is in the PR description

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added the bun run storybook command to both README.md and CLAUDE.md with a short description of what it does and the URL to open.

},
];
return (
<div style={{ display: "flex", flexDirection: "column", gap: 32 }}>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the color swatches and typography grid use heavy inline style={{}} props while the rest of the file (and the app) uses tailwind. worth keeping consistent — with real storybook this is less of an issue since each story file is small and self-contained

this will also be fixed once we move to storybook!

Removes 13 unused mock files from storybook/mocks/. Only data.ts and
bindings.ts are actually imported by the storybook — the rest were dead code.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
edwche10 and others added 5 commits February 13, 2026 19:09
URL was pointing to /HandyMain/storybook/ instead of the correct /.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
IconButton is not yet used in the app. Removing from this PR to keep
scope focused — will be added in a follow-up PR once the real Storybook
setup is in place and the component is adopted in the app (e.g. replacing
ResetButton).

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Removed Semantic, Primary Alpha, Gray Alpha and Text Alpha color groups
from the storybook color palette and storybook.css. These variables do
not exist in App.css or anywhere in the app — only the 7 real tokens
(text, background, background-ui, logo-primary, logo-stroke, text-stroke,
mid-gray) are now shown.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
The type scale (Display, Heading 1-3, Body, Caption, Label, Mono) is not
defined or enforced anywhere in the app. Components use standard Tailwind
utilities (text-xs, text-sm etc.) directly. Removing to avoid presenting
aspirational design as current reality.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
@cjpais
Copy link
Owner

cjpais commented Feb 16, 2026

@VirenMohindra @virenpepper is this more or less good to go? I'm focusing mainly on issues/fixes right now, or tiny obvious mergable changes

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants