Vesper
Vesper is a design system React component library, with apps and shared packages for development and documentation
Vesper is a design system React component library, with apps and shared packages for development and documentation
Links
Usage (test)
If you're using Tailwind, import @vesper/tailwind.css into your Tailwind config:
@import "tailwindcss";
/* import vesper's tailwind theme and utils */
@import "@vesper/tailwind.css";@repo/vesper
Vesper is a design-system package that publishes a small set of foundational entrypoints today and is expected to grow to include additional component surfaces over time.
Current published entrypoints include:
@repo/vesper/styles.css— global CSS token and component style imports@repo/vesper/tokens— token values as ESM exports@repo/vesper/tailwind.css— theme variables for first-class tailwind v4 integration support- component entrypoints such as
@repo/vesper/Icon,@repo/vesper/Typography, etc. - tree-shakeable component barrels such as
@repo/vesper/icons
Development workflow
Most day-to-day work in this package falls into one of two categories:
- Updating source assets
- Creating new components
- Building the package for local consumption or publishing
Typical flow:
- Update source assets or component source files
- Run the appropriate
generate:*command when asset-derived source needs to be refreshed - Review the generated files in
src/ - Run type checks / lint if needed
- Run
yarn devwhile iterating locally, oryarn buildwhen you want a clean publishable output indist/
Generated source lives in src/, while publishable artifacts live in dist/.
Component development
We use Storybook for component development. You can start the storybook dev server by running yarn dev, which is just an alias for storybook dev -p 3000 -c storybook.
Storybook is intended for local package development only. Workspaces that make use of the vesper package, such as apps/docs, should consume @repo/vesper from its built output.
Creating new components
Creating a new component can be done by running the command yarn scaffold:component from within the packages/vesper directory, or from the project root.
The scaffold:component script will prompt you for two things:
- The name of the component (required). The name should be written in PascalCase.
- The root element of the component (optional). If you decide to specify the root element, it should be an intrinsic html element like div, input, h1, etc.
When all prompts have been answered, turbo will generate a new folder for the component containing:
{component-name}.tsx- the component file{component-name}.css- the component css{component-name}.test.tsx- test file for the component{component-name}.stories.tsx- storybook file for the component
The scaffold command will also update src/styles/index.css to import the new css file, as well as modify the exports map in package.json. Note that the exports map points to built files in the dist folder so the package will need to be rebuilt via yarn build before the new exports can be used externally.
Testing components
Scaffolding a component from the command line will create a .test.tsx file in the component folder. We use vitest with playwright and axe for in-browser unit, snapshot, and a11y testing. The scaffolded test file contains three describe blocks:
component-name [unit]- this describe block should be used for writing unit tests related to prop behavior. Use this block to assert that CSS classes are being applied correctly, event handlers are firing as expected, and polymorphism is working as intended.component-name [snapshot]- this describe block should be used for writing snapshot tests. Many of our components have dozens of permutations, so we can use this describe block to create snapshots of them all so if something changes unexpectedly we get warned about it.component-name [a11y]- this describe block should be used for asserting that all component permutations comply with WCAG 2 AAA standards. We useaxeto confirm that all permutations for the component have no violations in both light and dark mode.
Updating snapshots
If a component's structure changes, we will need to update its snapshot tests. You can update snapshot tests across all test suites by running yarn test:vesper:update from the monorepo root, or yarn test:update from within the vesper package.
To update a specific test suite you can also supply a glob pattern to match whatever tests you would like to update the snapshots for:
yarn test -- -u src/components/menu/menu.test.tsxRegenerating icon components
Icon assets can be exported as SVGs from the UI Icon Library Figma File.
When files in assets/icons/ change, regenerate the icon source:
yarn generate:iconsThis updates the generated files in src/components/icon/, including:
- individual icon components
- the registry used by
@repo/vesper/icon - the tree-shakeable barrel used by
@repo/vesper/icons
Building
Build the package with:
yarn buildyarn build produces a clean publishable package in dist/.
It orchestrates a few smaller scripts:
yarn build:tscyarn build:aliasyarn build:css
Those steps perform the following work in order:
yarn build:tsccompiles source files to ESM plus declarationsyarn build:aliasrewrites emitted import specifiers to relative ESM-compatible paths with.jsextensionsyarn build:csscopies every side-effectful file file undersrc/into the matching location underdist/
The result is a dist/ directory that matches the package's export map and is ready for local consumption or publishing.
Why the build is structured this way
The build pipeline is designed around a few goals:
1. Publish ESM that is easy for consumers to tree-shake
The package is emitted as ESM and exported from dist/. Public entrypoints are intended to support both ergonomic convenience APIs and more tree-shakeable surfaces, so consumers can opt into smaller bundles without much extra work.
Some convenience entrypoints may trade bundle efficiency for usability, while barrel-style or more granular component exports are intended to be friendlier to downstream tree shaking.
2. Preserve module boundaries
tsc preserves module boundaries by compiling source files into matching files in dist/ instead of collapsing the package into a single bundle. That keeps the published output simple and gives downstream bundlers a better chance to eliminate unused component modules.
3. Treat CSS as side effects, but JS as side-effect free
styles.css is intentionally imported for side effects, so CSS files are preserved and copied into dist. JS modules are otherwise structured so bundlers can eliminate unused exports.
4. Keep build-specific TS behavior isolated
The package uses a single tsconfig.json for both type-checking and build output. Build orchestration stays minimal: clean dist/, run tsc, rewrite emitted import specifiers with tsc-alias, then mirror side-effectful files into matching paths under dist/.
5. Keep the published shape explicit
package.json exports point directly at built files in dist/, so the package contract is clear and stable for both local consumers and npm consumers.
Notes
src/contains authored and generated source filesdist/contains publishable build output- generated icon component files in
src/should be updated by scripts, not by hand