Rime

Patterns

Several primitives with one job. Each is how the job is always done, so screens never reinvent it.

List page

One glass panel holding the filter row, the table and the pagination. Nothing above it.

Every list page is this, and only this. The FilterBar's search grows to fill the row, dropdown chips sit at the end, "Clear filters" appears while anything is applied, and the list's own action goes last after a hairline. The table follows, then Pagination centred beneath.

Sort, filter and page state live with the screen. Reset to page one when a filter changes. The empty state says whether filters caused it and offers to clear them.

Invoices
InvoiceActions
INV-1042Jane Cooper29 Sep 2026$1,280PaidView
INV-1041Wade Warren27 Sep 2026$640SentView
INV-1040Esther Howard24 Sep 2026$2,150OverdueView
INV-1039Cameron Williamson22 Sep 2026$395PaidView
  • No heading or "n of m shown" line above the panel.
  • Never a row of toggle chips for a single choice: use a Select chip.
  • One table look everywhere: theme --table-* tokens, never restyle a table.

Frosted glass shell

The signed-in frame: the page field painted once, a frosted sidebar and top bar over it, and glass panels inside.

The field (--page-background) is never white: a coloured base with soft blooms, so white glass reads as panels floating on colour. The sidebar and top bar are whitened glass over the same field, so the chrome shares its colour, and the logo corner is that same glass by default: one continuous frosted L. A brand that requires a set colour behind its logo opts in with logoCorner="fill" and --logo-corner-bg. Content sits in GlassPanels; the one block that needs the user's action can be a Card tone="highlight".

A motif must derive from the product. The blooms here are light, not a picture; another site should choose its own field and justify any device it adds.

Logo corner: match glass (the default)

Projects

Page content scrolls here, under the frosted top bar.

Logo corner: brand fill (opt in, for brands that require it)

Projects

Page content scrolls here, under the frosted top bar.

  • Gate inside page.tsx, never the layout.
  • No bento grids; no left accent bars; no hover scale.

Shell: sidebar expanded

The default signed-in frame: a full-height frosted sidebar and top bar forming one glass L, the logo corner in the same glass.

Use it for products with more than five sections, or sections people move between all day. The sidebar holds the primary nav with counts; the top bar names the area and holds search and the profile menu. Only the page scrolls.

Projects

Page content scrolls here, under the frosted top bar.

  • One gradient on the chrome: the active nav pill.
  • The logo corner is glass unless the brand requires a fill (logoCorner="fill").

Shell: collapsible icon rail

The same frame with a panel button that folds the sidebar to a 68px icon rail, remembered across visits.

For dense work screens (tables, boards) where width matters more than labels. The toggle sits at the start of the top bar; storageKey keeps the choice in localStorage, read after mount so the first render matches the server. In the rail each item keeps its title as a tooltip and its count on the icon.

Projects

Press the panel button in the top bar: the sidebar folds to an icon rail, and the choice is remembered on reload.

  • Every nav item needs an icon in a collapsible shell.
  • Give the rail a compact mark when the logo is a wide wordmark.

Shell: header only

No sidebar: the logo, a short horizontal nav and the profile in one frosted top bar, the page below at full width.

For products with five or fewer top-level sections and no deep navigation: settings sites, portals, single-purpose tools. The bar is the same chrome glass and shadow as the sidebar frame, so a product can move between the two without a visual seam.

Acme

Page content scrolls here, under the frosted top bar.

  • Five sections at most in the bar; past that, use the sidebar.
  • No title in the bar: the page's own h1 names it.

Shell: mobile drawer

On narrow screens the sidebar becomes an off-canvas drawer from a menu button, with a scrim, a focus hold and Esc to close.

Automatic below 900px of the frame's own width (mobileBelow changes it), for the sidebar and rail layouts. The menu button replaces the collapse toggle; opening moves focus into the drawer, the page behind goes inert, and Esc, the scrim or choosing a link closes it and returns focus to the button. mobileDrawer={false} keeps the icon rail instead.

Projects

On a narrow screen the sidebar is a drawer from the menu button. Esc or the scrim closes it.

  • The drawer is for narrow frames only, never a desktop pattern.
  • Picking a link closes the drawer.

Summary card with cells

One card of key figures: per cell a label, a big number and a muted line of context, separated by hairlines.

This month at a glance: four or five cells in one row, each a figure and a sentence of context, not a grid of separate cards. The whole set is one surface; hairlines separate the cells. The label sits beside nothing and above only its own figure, never as a stacked title.

This month

Updated hourly

Invoices paid
32

+4 on last month

Active projects
14

3 due this week

Revenue
$48.2k

Target: $45k

Median time to pay
6 days

Down from 9

3/5Seats
  • One surface with cells, never a bento of cards.
  • Every figure has a line that says what it means.
  • Never a JavaScript scrollbar.
  • No shadow when there is nothing under the edge.

Terms with scroll-gated accept

A terms dialog whose Accept stays disabled until the reader has scrolled to the end.

The body scrolls; the footer says "Scroll to the end to accept" and the Accept button is disabled. When the scroll listener sees the far edge (useScrollEdges clears data-more and calls onEnd, surfaced on Modal as onScrollEnd), the hint turns into "You have reached the end" and Accept unlocks. If the terms are short enough not to scroll, it unlocks at once.

The hint is a status region and the disabled button is described by it, so screen reader users hear why it is locked and when it unlocks. Reopening the dialog locks it again.

  • Say why the button is disabled, next to it, and announce the change.
  • Gate on reaching the end, never on a timer.
  • Keep a way out (Not now) that is always enabled.

Sign-in with multi-factor authentication

A corporate sign-in: brand mark in the header, the form centred, a shield treatment and a security note in the footer, across the password, MFA code and authenticator setup steps.

AuthLayout paints the deep field and carries the header (the brand mark, a help link) and the footer ("Protected by multi-factor authentication"). One AuthCard sits in the middle per step: the password step with a lock tile, the code step with a shield tile and OtpInput, and the authenticator setup step with a QR code, the text key and a confirming code.

Each step has one full-width primary. The code step auto-verifies on onComplete; the setup step is reached from "Lost your device?" or after a reset. Use the switcher above the preview to see each step.

  • One primary per step, full width, size lg.
  • Errors in an ErrorSummary above the form on submit.
  • Keep an admin sign-in on its own route, never a toggle on the public one.
  • Never ask for the code anywhere but this screen; say so in the footer.