HETIBANK SDK Read the docs

Theming guide

Brand the kit for a brokerage without breaking meaning or accessibility.

Two layers

  1. Tokens (@hetibank/theme) — a typed ThemeTokens object: colors, typography, radius,

spacing, shadows, brand identity.

  1. CSS variables — the only styling interface components read. You can set them directly and

skip the React theme entirely.

:root {
  --hb-primary: #5b3a29;
  --hb-primary-fg: #ffffff;
  --hb-accent: #c2703d;
  --hb-accent-fg: #1a0d06;
  --hb-bg: #faf8f6;
  --hb-card: #ffffff;
  --hb-radius-card: 0.75rem;
  --hb-font-sans: "Inter", system-ui, sans-serif;
}

createBrandTheme

import { createBrandTheme } from "@hetibank/theme";

const theme = createBrandTheme({
  brand: {
    name: "Desert Bloom Realty",
    primary: "#5b3a29",   // branded surfaces, primary buttons
    accent: "#c2703d",    // marks, rules, emphasis
    logoUrl: "/brand/desert-bloom.svg",
    markUrl: "/brand/desert-bloom-mark.svg",
  },
  mode: "light",          // or "dark"
});

The returned theme embeds a contrast report. Creation throws when:

  • body text on the page background is below 4.5:1
  • card text is below 4.5:1
  • button foreground on primary/accent is below 4.5:1
  • the brand mark is below 3:1
  • any chart color is below 3:1 against the background

The canonical audit found two real brokerage accents failing this (eXp #F58220 at 2.59:1, Realty ONE #A58C58 at 3.23:1 for text). Those hex values are kept as fixtures in tools/verify/contrast.mjs proving the gate still rejects them.

If you must ship a failing brand during a design review, pass allowInaccessible: true — the theme then carries contrast.ok === false and the reason, so it cannot silently reach production.

Dark mode

Render both themes and switch with an attribute:

<ThemeProvider theme={isDark ? darkTheme : lightTheme} as="section">

ThemeProvider sets data-hb-theme="light|dark" and the full variable set inline, so a page can have multiple independently themed regions (e.g. a dark assistant panel in a light site). The generated @hetibank/theme/styles.css also supports a global data-hb-theme="dark" selector.

What may change, what may not

May changeMay never change
logo, mark, name--hb-danger / --hb-warning / --hb-success meanings
primary/accent color, type, radius, densitycertainty labels or evidence text
chart palette (contrast-checked)permissions, actions or calculations
light/dark modefigure values or units

A branded theme that changes what a number means is a defect: theme is presentation only.

Branding a sub-tree

<ThemeProvider theme={brokerageTheme} as="section" className="hb-brand-region">
  <SearchResults />
</ThemeProvider>

The nested provider overrides variables for that subtree. Use it for a white-label funnel embedded in a partner site.

Brokerage-first identity, required attribution

Consumer branding leads with the brokerage. MLS/licensing attribution stays visible in the footer:

<ShellFooter attribution="Listing data © Las Vegas REALTORS®. HETIBANK figures are estimates." />

Do not hide attribution to make a theme look cleaner; it is a licensing requirement.

Typography and density

  • --hb-text-base sets the base size (default 16px).
  • --hb-leading sets line-height (1.5).
  • --hb-space is the 4px spacing unit the components multiply.
  • --hb-radius-* controls control/card/chip rounding.
  • Changing density is supported; changing font family is supported. Do not introduce a display

font that fails contrast for small labels.

Testing a theme

node tools/verify/contrast.mjs

Or in code:

import { checkContrast, auditThemeColors, contrastRatio } from "@hetibank/theme";

const ratio = contrastRatio("#c2703d", "#faf8f6"); // 3.48
checkContrast("brand-mark", "#c2703d", "#faf8f6", "largeText").pass; // true

Run the theme at 360px, 768px and 1280px with long copy. The kit's states are contrast-checked, but layout is your composition.