Skip to content

Bridge components ship with optional default styles. Import them for a ready-to-use appearance, or skip the import entirely for headless usage where you control all styling yourself.

Add this import to your app entry or global CSS:

import '@nebulr-group/bridge-react/styles';

The stylesheet provides two layers:

  • Structural CSS: layout, spacing, and sizing that components need to render correctly.
  • Visual defaults: a minimal but complete out-of-the-box appearance so forms look reasonable with no extra work. These include a visible input border/focus ring, an indigo primary button, and colored error/success alert banners.

The --bridge-* custom properties are the supported way to restyle every Bridge page and component: the sign-in pages, the subscription pages, the plan picker, dialogs, banners and the team page. Set them in your own stylesheet on :root, or on a wrapper to theme one area.

/* index.css or any global stylesheet */
:root {
  --bridge-primary: #0f766e;
  --bridge-border-radius: 10px;
}

The plugin declares its defaults on :where(:root), which has zero specificity, so your :root wins whichever stylesheet loads first. No rule in the stylesheet paints a colour except through a token.

TokenDefaultStyles
--bridge-primary#4f46e5Primary buttons, the selected tab and plan interval, the active workspace
--bridge-primary-hoverprimary, 15% darkerPrimary button hover
--bridge-primary-fg#ffffffText on primary surfaces
--bridge-primary-lightprimary at 10%Tint behind the active workspace
--bridge-input-focusprimaryInput focus ring
--bridge-bg#ffffffDialogs, menus, panels
--bridge-foreground#111827Text on those surfaces, workspace names
--bridge-muted#6b7280Secondary text, hints, table headings
--bridge-muted-bg#f3f4f6Subtle fills: tab tracks, hovers, the MFA backup code
--bridge-border#d1d5dbInput, table, card and secondary button borders
--bridge-border-radius6pxCorners of inputs, buttons, alerts, cards and dialogs
--bridge-overlayrgba(15, 23, 42, 0.45)Backdrop behind dialogs
--bridge-alert-error-{bg,fg,border}#fef2f2 / #991b1b / #fca5a5Errors, critical billing notices, cancelled plan badge
--bridge-alert-success-{bg,fg,border}#f0fdf4 / #166534 / #86efacConfirmations, active plan badge
--bridge-alert-info-{bg,fg,border}#eff6ff / #1e40af / #bfdbfeInfo notices, trial badge
--bridge-alert-warning-{bg,fg,border}#fffbeb / #92400e / #fcd34dWarnings, a quota nearing its limit, past-due badge
--bridge-auth-page-padding3rem 1remPadding of the default sign-in page container
--bridge-billing-page-width60remWidth of the subscription pages and <BridgePaywallPage>
--bridge-billing-page-padding3rem 1remPadding of the subscription pages and <BridgePaywallPage>
--bridge-paywall-bgrgba(15, 23, 42, 0.72)Backdrop of the <BridgePaywall> overlay and the billing lockscreen
--bridge-paywall-panel-bg--bridge-bgPanel of the <BridgePaywall> overlay

The derived tokens (hover, light, focus) follow --bridge-primary wherever you set it, unless you set them too. Two older names still work as deprecated aliases: --bridge-primary-foreground for --bridge-primary-fg, and --bridge-bg-muted for --bridge-muted-bg; they will be removed in a later major.

Fonts and body text colour are not tokens: Bridge pages render inside your layout and inherit them.

Tailwind. Point the tokens at your theme, e.g. :root { --bridge-primary: theme('colors.teal.700'); } (Tailwind 3) or :root { --bridge-primary: var(--color-teal-700); } (Tailwind 4).

All visual-default rules use the :where() pseudo-class, which has zero specificity. This means any class or element selector in your own CSS wins automatically, no !important needed.

For example, the default primary button is styled as:

:where(.bridge-btn-primary) {
  background-color: var(--bridge-primary);
  /* ... */
}

Your own .bridge-btn-primary { background: red; } or even a simple .my-button { background: red; } will override it without specificity battles.

All components forward className and style props to their root element, so you can target them directly:

<LoginForm className="my-login-form" />
.my-login-form input {
  border-radius: 0;   /* square inputs */
}

You can also use the style prop for inline overrides:

<LoginForm style={{ '--bridge-primary': '#7c3aed' } as React.CSSProperties} />

Bridge components expose data attributes that you can use as CSS selectors for state-based styling:

AttributeValuesDescription
[data-active="true"]"true"Active tab
[data-loading="true"]"true"Loading state
[data-state="active"]"active"Active status
[data-state="disabled"]"disabled"Disabled status
[data-variant="error"]"error"Error variant (alerts)
[data-variant="info"]"info"Info variant (alerts)
[data-variant="success"]"success"Success variant (alerts)
[data-variant="danger"]"danger"Danger variant (alerts)
[data-bridge-plan-selector](none)Plan selector root
[data-bridge-plan-card](none)Individual plan card
[data-current="true"]"true" / "false"Current plan card
[data-bridge-alert](none)Alert component
[data-bridge-auth-form](none)Auth form wrapper
[data-bridge-passkey-login](none)Passkey login button
[data-bridge-sso-button](none)SSO button
[data-bridge-spinner](none)Loading spinner
[data-bridge-api-tokens](none)API token management root
[data-bridge-workspace-selector](none)Workspace selector root
[data-bridge-workspace-item](none)Individual workspace item

Example: style the active workspace differently.

[data-bridge-workspace-item][data-active="true"] {
  background: var(--my-highlight);
  border-color: var(--my-accent);
}

If you manage all styling yourself (e.g. you use Tailwind), skip the import entirely:

  // src/main.tsx
  import { BridgeProvider } from '@nebulr-group/bridge-react';
- import '@nebulr-group/bridge-react/styles';

Components render as plain, unstyled HTML. Use the className prop and the data attributes listed above to apply your own styles. The structural layout (flexbox, grid) is embedded in the stylesheet, so without it you have full control over how components are laid out.