Skip to content

Latest commit

 

History

History
51 lines (37 loc) · 5.56 KB

File metadata and controls

51 lines (37 loc) · 5.56 KB

Contributing to @workspace/ui

packages/ui is the shared component library consumed by apps/web (and any future app in this monorepo) via the @workspace/ui/* import alias. This guide covers adding, testing, documenting, and exporting a component here — see the root CONTRIBUTING.md for the general repo workflow (branch naming, commit format, PR checklist).

Does this belong in packages/ui, or in a feature?

Ask: would a second app in this monorepo (or a second unrelated feature in apps/web) plausibly reuse this exactly as-is?

  • Yes → packages/ui. Generic primitives with no knowledge of SO4's domain: buttons, inputs, dialogs, tooltips, tabs. They take data and callbacks as props; they never import from apps/web/src/features/*, call the indexer, or know what a "stream," "pool," or "position" is.
  • No → a feature directory (apps/web/src/features/<feature>/components/). Anything that renders domain data, calls a hook that fetches it, or is meaningfully specific to one page (e.g. StreamProgressBar, PoolTransactionDialog).

If you're unsure, default to the feature directory — it's a much smaller change to promote a component to packages/ui later (once a second real consumer exists) than to over-generalize one prematurely.

Adding a component

  1. File naming: kebab-case.tsx in packages/ui/src/components/ (e.g. date-picker.tsx), matching every existing file in that directory. One component (plus its tightly-coupled sub-parts, e.g. Tabs/TabsList/TabsTrigger/TabsContent in one file) per file.
  2. API:
    • Accept className and forward it into cn(yourVariants({ ...variantProps }), className) (see lib/utils.ts's cn) so consumers can extend, never fight, your styles.
    • Use class-variance-authority (cva) for variant props, matching button.tsx and badge.tsx — a variants object plus defaultVariants, exported alongside the component (export { Button, buttonVariants }) so consumers can compose the class list elsewhere if they need to.
    • Spread ...props onto the root element last, after your own computed props, so a consumer can still override anything you didn't explicitly design as a variant.
    • Only reach for design values from the token system in DESIGN.md (colors, radius, the micro-typography scale) — bun run check:tokens (from the repo root) enforces this in CI; see that doc for the escape hatch (ds-allow comments) for the rare genuinely-structural exception.
  3. Accessibility: this library is built on Base UI primitives (@base-ui/react/*), which already handle most ARIA/keyboard-interaction concerns — prefer wrapping a Base UI primitive over building interactive behavior (focus trapping, roving tabindex, etc.) from scratch. If you do add custom interactive behavior, it needs to be operable by keyboard alone and expose the correct role/state via ARIA attributes, not just visually.
  4. Testing: there's no unit-test runner wired up in this package yet — component behavior today is exercised indirectly through apps/web's test suite (which renders these components) and through the visual regression suite below. If you're adding non-trivial logic (not just markup + variants — e.g. a hook, a reducer, keyboard-navigation logic), add a proper test using the shared config:
    // packages/ui/vitest.config.ts (create if it doesn't exist yet)
    import { defineConfig, mergeConfig } from "vitest/config"
    import { reactConfig } from "@repo/vitest-config/react"
    
    export default mergeConfig(reactConfig, defineConfig({}))
    then add a "test": "vitest run" script to this package's package.json and a component-name.test.tsx alongside the component, following the render/assert patterns already used in apps/web/src/**/*.test.tsx.
  5. Export: add both the component and its prop-types export to whichever consumer imports it via @workspace/ui/components/<file> — no central barrel file exists (each component is its own subpath export per package.json's exports map), so there's no index to update.
  6. Document in the gallery: add your component to apps/web/src/features/gallery/components/gallery-page.tsx, rendering every variant/size it supports. This is how reviewers and future contributors discover what exists and see every state at a glance — see DESIGN.md for why it exists.

Commands

Run from the repo root (or --filter=@workspace/ui via turbo to scope to just this package):

bun run typecheck --filter=@workspace/ui   # tsc --noEmit
bun run lint --filter=@workspace/ui        # eslint
bun run check:tokens                        # design-token usage check (whole repo, not package-scoped)
bun run test:e2e -- design-system-visual   # visual regression, once your component is in the gallery

All four should be clean before opening a PR that touches this package — the same bar as the root CONTRIBUTING.md sets for the rest of the repo.

Further reading