Layout
A top bar for up to 4 places, a sidebar for more and for admin; two page shapes, five widths, the page header and every measurement of the frame.
Every Bridge screen sits in one of two shells and takes one of two shapes. Pick the shape first, then a width by the page's job; the frame, edges and header come from the shell and the templates, so a screen draws only its own content.
The shell
How many places an app has decides its shell. Up to 4 top-level places (Activity, Network, Outcomes…) fit in a top bar. More than 4, or an admin or operator tool, and the places move to a sidebar under the top bar. On phones, places move to tabs along the bottom.
- Count the app's top-level places, not its pages: 4 or fewer go in the top bar; 5 or more go in a sidebar. Never squeeze a fifth into the top bar or hide it behind a “More” menu.
- On phones, an app with up to 4 places shows them as bottom tabs (Bridge OS: Activity, Network, Outcomes). An app with a sidebar opens it from a menu button in the top bar, as a panel sliding in from the side.
- The shell draws the top bar, the bottom tabs, the page background (dot grid and glows) and the page edges, once. A page never repeats them.
- Pages that live outside the app (sign in, public pages) still use the same edges, background and widths.
- Sticky things inside a page sit below the top bar:
top-14for a bar,top-20for a side rail. - A screen opened on top of another on a phone (a record, a thread) gets a top bar with a back arrow and its title, as the mobile app does.
Two page shapes
Decide the shape before drawing anything.
- A page has one scrolling region: the window. Never nest a scrolling box inside a scrolling page.
- A tool is a full-height column (
flex h-dvh min-h-0 flex-col) under the top bar; its panes scroll (min-h-0 flex-1 overflow-auto). Putmin-h-0on every flex child between the column and the pane. - A list and its detail sit side by side from 768px, the list about 40% wide (30 to 70% if the divider drags). On phones, one pane at a time: the list, then the detail with a back arrow.
- Don't centre a tool in a page width, and don't stretch a page edge to edge.
Page widths
Five widths, picked by the page's job. Import them from the package rather than writing the number.
| Width | Size | For |
|---|---|---|
PAGE_WIDTH.wide | 1600px | Tables, and record pages with a side rail: an organization, a profile, sharing, invites. |
PAGE_WIDTH.standard | 1280px | Card dashboards, grids and feeds: Outcomes, Network, Apps, Activity. |
PAGE_WIDTH.read | 800px | A single column of reading: help, an announcement, a long description. |
PAGE_WIDTH.form | 720px | Forms and settings. |
PAGE_WIDTH.narrow | 448px | A card outside the app: sign in, a consent screen, an invite. |
import { pageFrame } from "@brdgapp/ui/lib/layout";
// centred, 1280px, edges 16 → 24 → 32, 24/32px from the top bar
<main className={pageFrame("standard")}>…</main>The page header
Pages with a title follow the same order, the one raise, bridge-jobs and the record pages already use.
- The title (Page title, 22px) and one line under it. A back link sits 24px above it on a page you drilled into.
- Actions for the whole page, on the right: one primary button at most, then secondary or icon buttons. Hide them when there's nothing to do.
- Tabs under the title split a record or a big page into sections.
- A toolbar over a list: search on the left, filters, sort and the list's own actions on the right. Under tabs, it acts on the open tab only.
On phones the actions wrap under the title and the tabs scroll sideways.
Measurements
| Part | Size | How |
|---|---|---|
| Top bar | 56px, sticky, frosted glass | h-14 sticky top-0 |
| Bottom tab bar (phones) | 56px plus the phone's safe area, below 640px; 3 to 5 tabs | h-14 fixed bottom-0 sm:hidden |
| Sidebar | 240px, under the top bar, from 1024px; a drawer below. For apps with more than 4 places, and admin | w-60 sticky top-14 |
| Page edges | 16 → 24 → 32px | PAGE_EDGES |
| Page top | 24px under the top bar, 32px from 640px | pt-6 sm:pt-8 |
| Side rail | 340px, from 1024px, sticky 80px from the top | RAIL, lg:sticky lg:top-20 |
| Between sections | 32px | gap-8 |
| Card grid | 16px apart; 1, 2 and 3 columns | grid gap-4 sm:grid-cols-2 lg:grid-cols-3 |
- A side rail comes first in the code, so it's first for keyboard and screen-reader users, and on phones it sits above the main column.
- Cards in a row are the same height; push content that varies to the bottom with
mt-auto. - Never break out of the page edges with
w-screenor negative margins. If a page needs more room, it takes a wider width; a card that runs to its own edges uses Card'sflush. - Never a height worked out by hand (
h-[calc(100vh-56px)]). A tool's column fills what's left under the top bar.
Templates
Each template is a shape with its parts filled in. Start a new screen from the closest one.
| Template | Shape and width | What it adds |
|---|---|---|
| Card dashboard | Page, standard | Stat tiles and cards in a grid. |
| Feed | Page, standard | Items stacked in time order. |
| Table page | Page, wide | A title, a toolbar and a table. |
| Record page | Page, wide | One person or organization, tabs, and a side rail. |
| Settings form | Page, form | Settings in cards, saved with a button. |
| Admin table | Page, wide, admin shell | A table beside the admin sidebar. |
| Public page | Outside the app | A page anyone can open, with Display headlines. |
| Centred card | Outside the app, narrow | Sign in and other single-card screens. |
Rules
Glass, once
Cards, panels, empty states and the navigation are frosted glass over the page background, never flat white boxes. A glass card never sits inside another glass card: inside a card, group things with dividers or a light bordered box.
Reserve the space
Nothing that arrives later may push content down. A skeleton has the same height and shape as what it stands for, measured, not guessed. A visible jump after the first paint is a bug.