Getting started
Getting started
Consuming the system
Three steps, and the first two are what stop the tokens drifting between repos again.
Extend the preset — never copy the theme
// tailwind.config.ts
import btsPreset from "bts-storybook/tailwind-preset";
export default {
presets: [btsPreset],
content: ["./src/**/*.{ts,tsx}"],
};Import the stylesheet once
// main.tsx — once, at the application root
import "bts-storybook/styles.css";Import components from the package root
import { Button } from "bts-storybook";
export function CheckoutFooter() {
return (
<div className="flex gap-3">
<Button variant="tertiary">Back</Button>
<Button>Continue to payment</Button>
</div>
);
}You need both the preset and the stylesheet
The preset supplies the Tailwind theme; the stylesheet supplies the custom properties the theme points at, plus the hand-written classes (.btn-white, .nav-item, .stat-card) that several components depend on. The preset alone produces utilities that resolve to nothing.
Running Lantern
Storybook is where components are built. This site is where they are explained.
# from the monorepo root — starts Storybook on 6006 and Lantern on 3100
bun run start
# or start either process separately
bun run start:storybook
bun run start:lanternLantern runs on port 3100 so it can sit alongside Storybook on 6006 without a clash. The two are not alternatives — Storybook drives a component through its states in isolation; Lantern says what it is for, when to reach for it, and what the rules around it are.
How this site is wired
Worth understanding before you change it, because two of the decisions are load-bearing.
Storybook renders every catalogue preview
Lantern embeds the maintained Storybook story from localhost:6006 (or NEXT_PUBLIC_STORYBOOK_URL). It stores generated catalogue and story-id metadata, not copied component code or hand-written duplicate demos.
The Lantern shell uses a narrow source seam
Lantern itself still needs buttons, cards and other primitives for its documentation UI. Pages import them through ~/ds, which targets individual sibling Storybook modules and never the root barrel, so app-only components are not compiled.
// lantern/src/ds/index.ts
// Lantern's own shell uses narrow imports from sibling Storybook source.
export { Button, buttonVariants } from "@/components/ui/button";
export { cn } from "@/lib/utils";
// PENDING — stand-ins, awaiting promotion.
export * from "./pending";Adding a finished component
Maintain the component and its examples in Storybook. Lantern's catalogue is generated and its preview remains the live Storybook iframe.
// 1. Export the component from bts-storybook/src/index.ts
export { Card } from "./components/ui/card";
// 2. Add or update its maintained Storybook story
// bts-storybook/src/components/ui/card.stories.tsx
// 3. From the monorepo root, update Lovable and Lantern metadata
bun run component-sync:storybook
// Lantern receives generated catalogue/story metadata only.
// The rendered preview stays at http://localhost:6006.Do not recreate the demo in Lantern
The Storybook story is the maintained example. Component sync refreshes Lantern's generated catalogue and story-id metadata; it does not copy component source, assets or a second implementation of the demo.
What makes a good story
Keep Storybook stories deterministic and cover meaningful states. Lantern shows those same stories, so there is only one example surface to maintain.
Cover both breakpoints
If the component carries a max-md: / md: pair, cover both in Storybook. Lantern's iframe viewport control renders the actual responsive tree.