BridgeHandbookDraft
For agents
Menu
Stable

Using the system

How to pick up the package in an app: imports, tokens, data states, feedback, theming, accessibility and where new code goes.

Setting up

Bridge’s components, tokens and styles ship as one package, @brdgapp/ui. An app installs it once and imports the stylesheet after Tailwind. That brings every token in light and dark, and tells Tailwind to read the package’s files.

/* The app's main CSS file, after Tailwind */
@import "tailwindcss";
@import "@brdgapp/ui/globals.css";
  • Needs React 18 or 19 and Tailwind CSS 4.
  • Load the Mulish font the way the app loads fonts. With next/font, name the variable --font-mulish.
  • The theme follows data-theme on <html> (see Theming below).
  • Mount <Toaster /> once in the root layout, so toast.success() works from any screen.

Importing

Import each part from its own file. There is no single index file to import from.

What you can import, read from the package’s package.json

Import pathPoints toHolds
@brdgapp/ui/components/*packages/ui/src/components/*.tsxOne component family per file.
@brdgapp/ui/hooks/*packages/ui/src/hooks/*.tsBehavior without markup. useMockQuery is for the handbook and prototypes only.
@brdgapp/ui/lib/*packages/ui/src/lib/*.tscn, the number and date formatters, ElementProps.
@brdgapp/ui/globals.csspackages/ui/src/styles/globals.cssEvery token in light and dark, the base styles and the custom utilities.
@brdgapp/ui/package.jsonpackages/ui/package.jsonThe version, for tools.

Typical imports

import { Button } from "@brdgapp/ui/components/button";
import { DataState } from "@brdgapp/ui/components/data-state";
import { formatCount, formatDate } from "@brdgapp/ui/lib/format";
import { cn } from "@brdgapp/ui/lib/utils";

Always import cn from @brdgapp/ui/lib/utils, never clsx or a copy in the app, so a class you pass can override the component’s own.

Styling with tokens

Style with Bridge’s role tokens, never raw colors. A token names a job (--text-muted, --surface-secondary), so the same class is right in light and dark.

text-[var(--text-muted)], bg-[var(--surface-secondary)], border-[var(--border-default)]

text-gray-500, bg-white, border-[#ECEDF0]: they break in dark mode and drift from the system.

The type scale

SizeUse for
22px boldPage headings and single stat numbers
16px boldSection headings
15px boldCard titles (14px in a header strip)
14pxSub-labels, tabs, buttons
13pxBody text, rows, card copy
12pxMetadata: dates, counts, helper text
11px bold uppercaseEyebrows only. Nothing is smaller, except the Badge sticker.
  • Every number that changes or gets compared uses tabular-nums. Write it with formatCount, formatMoney, formatPercent or formatDate, never by hand.
  • A package component’s look comes from its props (variant, size). Use className on it only for layout: mt-4, w-full, shrink-0.

Every token with its light and dark value is in Design tokens; the custom utilities, such as liquid-glass, are in Styles and utilities.

Your first screen

Build a screen builds a whole Bridge OS page from the package, in the order you’d build any page: pick the app, pick the template, lay out the parts, then fill each one. Start there once the package is set up.

Data and its states

Every card, table and panel that loads data owns its own loading, empty and error states. DataState takes the query’s status and data and shows a skeleton, an empty state, an error with Retry, or your content.

A card that owns its states

Try Slow and Fail in the sidebar’s demo controls.

Recent intros

  • Never hold up a whole page while one part loads. The card and its title always show; only the inside waits.
  • Shape the skeleton like the loaded content, so nothing moves when the data arrives.
  • An empty state says what will be here and how to fill it. An error says what failed and offers Retry.
  • useMockQuery is for handbook examples and prototypes. In an app, pass the real query’s status and data.

The full rules are in Loading, empty and error.

Feedback

Every action shows that it worked. If the result is visible where the person clicked (a row says Copied, a switch turns on), that is the feedback. If it isn’t, because a menu closed or the result happens somewhere else, a toast says what happened.

A toast

One call to toast.success(). The Toaster in the app layout shows it for 3 seconds.

  • A toast is past tense, under 10 words, with no period: “Invite sent”.
  • An error people must act on stays next to the problem. It is never a toast that fades.
  • Bridge has no undo. Anything that removes or stops something asks first with ConfirmDialog.

See Feedback and undo and Destructive actions.

Theming

Light and dark come from one set of tokens. data-theme="dark" on <html> switches every token at once; data-theme="light" on an element keeps that area light inside a dark page. Bridge follows the member’s choice, not the device setting.

  • Bridge OS stores the choice in localStorage as bridge-theme and sets it before the page draws, so there is no flash of the wrong theme.
  • A screen built from tokens needs no dark: classes. Use dark: only for something a token can’t say.
  • Every example on this site has a Light and Dark switch. Check both before you ship.

Accessibility

Base UI gives the package’s components their roles, focus handling and keyboard behavior. Naming things and choosing the right element stay with you.

  • Every control has a name. An icon-only button has an aria-label that says the action.
  • Focus always shows: a 2px ring with a 2px gap. Never remove it.
  • Nothing clickable is smaller than 28px.
  • Everything a pointer can do, the keyboard can do. Every dialog has a title.
  • Status is never shown by color alone; a word always goes with it.
  • Motion respects reduced motion.

The full checklist is in Accessibility.

Finding things

Every component, pattern and template has a page. Press / anywhere on this site to search the sidebar by title and description.

Looking forGo to
A component for a jobAll components
How to solve a problem that comes up again and againPatterns, starting with Loading, empty and error
The shape of a whole pagePicking a template
What an app’s frame, widths and habits areAll apps
A token’s valueDesign tokens
Everything as plain text, for agentsFor agents

Working together

DESIGN.md at the root of the design-system repo is the visual spec. The handbook explains it and shows it running; no app reinterprets it. When code and DESIGN.md disagree, DESIGN.md wins.

  • Each app gets its character from layout, density and behavior, not from new colors, fonts or corner sizes.
  • If the system can’t do something a screen needs, change the system first (Component lifecycle), not the one screen.

Where new code goes

What you’re writingWhere it goes
A screen or a routeThe app. In Bridge OS: src/app/(shell)/<area>/, with its parts in _components/ next to it.
Loading data and its helpersThe app, next to the area that uses it. In Bridge OS: src/lib/<area>/.
A control or layout part used in a second placepackages/ui, with a handbook page (Component lifecycle → Share at two).
A hook or helper that knows nothing about the productpackages/ui/src/hooks or packages/ui/src/lib.
A different look of an existing componentA new option on that component, not a copy in the app.

New to the code itself? See Repository and Component lifecycle.

Rules

Fix the system first

If the system can't express something a screen genuinely needs, extend the component (a new size, a new option) and document it in the same change, then use it. Never work around a gap at the call site. A 1–2px change from the old design is the expected cost of joining the system, not a reason to stay hand-made.