Skip to main content

Component conventions

Where components live

  • Reusable, presentational components: packages/ui/src/ (@enode/ui) — Button, TagPill, Sheet, SortableList, BottomSheetCard, LottieCircle, … These are the components worth a Storybook story.
  • App-specific feature components: under each app's src/app/ — composed from co-located use-* hooks. Mostly tied to live session state, so generally not good Storybook candidates without a mock.

Storybook

Stories document the reusable @enode/ui layer, co-located with the component as ComponentName.stories.tsx. Storybook renders them with the real design tokens (Tailwind v4 + @enode/ui/styles/design-system.css, wired in docs-site/.storybook).

Currently covered:

  • packages/ui/src/button.stories.tsx — all variants, sizes, disabled, rounded pill, leading/trailing icon.
  • packages/ui/src/tag-pill.stories.tsx — contrast swap, no-colour fallback, interactive, long-content truncation.

Adding a story

  1. Pick a reusable @enode/ui component with meaningful visual states. Skip provider-only wrappers, route-only pages, and components that need live backend state without a safe mock.

  2. Create ComponentName.stories.tsx next to the component.

  3. Use Component Story Format with TypeScript:

    import type { Meta, StoryObj } from "@storybook/react";
    import { fn } from "storybook/test";
    import { MyComponent } from "./my-component";

    const meta = {
    title: "UI/MyComponent",
    component: MyComponent,
    tags: ["autodocs"],
    args: { onChange: fn() },
    } satisfies Meta<typeof MyComponent>;

    export default meta;
    type Story = StoryObj<typeof meta>;

    export const Default: Story = { args: { /* realistic props */ } };
  4. For ReactNode/callback props, set argTypes: { prop: { control: false } } and add dedicated stories — editable controls produce broken values.

  5. Only document states the component actually supports. Use realistic args, mock callbacks with fn(), never call real APIs, never invent props.

  6. Run npm run storybook to preview, npm run docs:storybook to build.

TODO: Sheet / BottomSheetCard / SortableList are reusable but need a small amount of mock wiring (portals, drag context). Add stories when those mocks are in place — do not force a story that fakes behavior.