Skip to content

Components

@nimblebrain/synapse/ui is a React component library for embedded Synapse apps. You get a token contract, layout primitives, a handful of data-display and interactive components, and three responsive layout scaffolds. Import them and your app inherits the look of whatever host it renders in: no theme wiring, no brand baked into your bundle.

import { Card, Table, Badge, Button } from "@nimblebrain/synapse/ui";

These are the durable decisions behind the library. They rarely change, and they explain why the components behave the way they do.

  • The library holds no brand. Every color, font, radius, and shadow is a var(--token, neutralFallback) reference, never a hex value. The host injects the real palette and fonts at runtime through the ext-apps hostContext.styles.variables, so the same app adopts the NimbleBrain host, a ChatGPT surface, or your own runtime without changing a line. Standalone, with no host to inject anything, the components render in neutral fallbacks: system fonts, neutral grays, a generic blue.

  • Theme via CSS, not React. Components style with token-driven inline-style objects whose values are those var() references. When the host swaps the :root variables (including a light-to-dark flip), every var() re-resolves in CSS and the pixels change with no React re-render. Keyframes and pseudo-state rules inject once; brand values are never captured in component state.

  • Scaffold only genuinely-complex layouts. AppFrame, SidebarLayout, and ListDetailLayout exist because each one encapsulates real responsive and stateful complexity worth owning once. Boards, grids, and simple lists are not components. They are primitives plus a recipe. The library codifies the shapes apps actually take, not a general-purpose layout engine.

  • Responsive to the pane, not the device. Layouts observe their own width with a ResizeObserver (via useBreakpoint), never the viewport. An app’s iframe might be fullscreen, split down the middle, or docked in a narrow rail regardless of screen size, so device media queries would be measuring the wrong box.

  • Lean on the platform. Drawer is built on the native <dialog> element, which brings focus-trapping, Escape-to-close, and scroll behavior for free rather than re-implemented in JavaScript. Where the browser already solves a problem, the library uses it.

Every component has its own page with live previews, props, and copy-paste code.

Foundations: the contract everything else reads from.

  • Tokens: the var(--token, fallback) model, every token group, and how the host fills them.
  • Fonts: the two side-effect imports that load the brand faces and establish the root-height chain.

Layout primitives: the low-level flow and spacing pieces.

  • Stack: vertical flow with a token-sized gap.
  • Inline: horizontal flow that wraps and aligns.
  • Spacer: a flexible gap that pushes siblings apart.
  • Divider: a hairline rule between sections.

Layout scaffolds: the responsive shells for whole views.

  • AppFrame: the outer app shell (header, body, root-height chain, base reset applied automatically).
  • SidebarLayout: a sidebar beside a main pane that collapses when the pane gets narrow.
  • ListDetailLayout: a list and a detail view side by side, stacking on a narrow pane.

Typography: the type scale in prose form.

  • Heading: the display scale (sm, md, lg) in the heading face.
  • Text: body copy at four sizes with muted and faint tones.
  • Prose: a container that styles a block of rich or Markdown-rendered content.

Data display: the components that render your data.

  • Card: a surface that groups related content.
  • ListRow: a single list row with leading and trailing slots.
  • Table: a token-styled data table.
  • Avatar: a user or entity image with an initials fallback.
  • Badge: a small status or category label.
  • StatusDot: a colored dot for compact status.
  • Pagination: page controls for long collections.

Interactive: the components users act on.

  • Button: a pressable action in three variants.
  • TextLink: a chrome-less text button for inline actions.
  • SearchField: a search / filter input.
  • SegmentedControl: single-select among a few options.
  • Drawer: an overlay panel built on native <dialog>.

Feedback: the components that report state.

  • Spinner: an indeterminate loading indicator.
  • EmptyState: a placeholder for empty or zero-result views.