{
  "aliases": [
    "tenanttheme",
    "customer-brand-color",
    "runtime-theme",
    "brand-color"
  ],
  "code": "// ─────────────────────────────────────────────────────────────────────────\n// A CUSTOMER's brand colour, at runtime, on ONE region. gh#861 / gh#868.\n//\n// The colour arrives as DATA (a hex an admin picked in your settings screen),\n// not as a stylesheet you author. AppSettingPicker `brand` is an ENUM PRESET\n// (brand|crm|logistics|partner|slate|dxs) and cannot take an arbitrary hex.\n//\n// ❌ DON'T hand-roll the conversion or the label. Two consumer repos each wrote\n//    their own 18-line hexToHslChannels and their own contrast rule; a wrong\n//    label makes nothing red — the text is just hard to read, for end users only.\n// ❌ DON'T set --primary alone. It does not travel alone: --primary-foreground\n//    does NOT follow it, and --ring is bound at :root where it FREEZES.\n// ✅ DO ask the package for the declarations.\n\nimport { tenantTheme, contrastRatio, hexToHsl } from \"@godxjp/ui/app\";\n\nconst brand = tenantTheme(tenant.primary_color);      // \"#0071bd\" · 3- or 6-digit\n\n<div data-tenant={tenant.slug} style={brand.vars}>    // a REGION, not <html>\n  <Topbar … />\n  <Button>Open app</Button>                            {/* rest, hover AND pressed follow */}\n</div>\n\n// brand.vars is exactly eight declarations:\n//   --primary  --primary-foreground  --ring  --primary-hover  --primary-active\n//   --text-link  --text-brand  --text-primary        ← the brand as INK (gh#887)\n// An unusable hex yields EMPTY vars, so the region falls back to your own theme\n// instead of breaking the page (same contract ColorPicker already has).\n\n// THE LABEL IS CHOSEN BY CONTRAST, and the ratio is reported so you can check it.\n// WCAG 2.2 SC 1.4.3, normal text, 4.5:1. White/black pivot at relative luminance\n// 0.179; both candidates measure 4.58:1 there, so the winner ALWAYS clears AA.\n//   #FFD400 → #000000 at 14.67:1   (\"always white\" would have been 1.29:1)\n//   #0A1F44 → #ffffff at 16.25:1\nbrand.foreground  // \"#ffffff\"\nbrand.contrast    // 5.13\nbrand.meetsAA     // true\n\n// ALREADY COMPUTED THE PAIR SERVER-SIDE? Pass it. It is used AS GIVEN — never\n// silently replaced — and the result still reports what it achieves.\nconst supplied = tenantTheme(hex, { foreground: tenant.theme_tokens.foreground });\nif (!supplied.meetsAA) {\n  // supplied.contrast is the number. The API says NO instead of returning white.\n}\n\n// NESTED TENANTS on one page: call it per region. Each region carries its own\n// pair, so nothing freezes from the region above it.\n\n// THE BRAND AS INK IS ALSO FLOORED (gh#887) — it used not to be, and that is the\n// half of the contract a consumer used to have to write. --primary is guaranteed\n// against --primary-foreground: a FILL and its label. Nothing guaranteed the same\n// colour as TEXT on a page surface, and a sidebar active item, a NavList item, a\n// MegaMenu trigger, an Anchor link, `Text link` and `Button variant=\"link\"` all do\n// exactly that. Measured on /showcase/theme-lab: #FFD400 gave 1.18–2.06:1.\n// So the three inks come back as LITERALS, each walked away from the surface until\n// the painted pixel clears 4.5:1 and no further — a seed whose ramp step already\n// clears is untouched. No call site of yours changes; the roles were always read.\n//   #FFD400  link ink 1.73:1 → 4.56:1     #E2564A  3.80:1 → 4.53:1\n//   #7C3AED / #2563EB / #0A1F44           byte-identical, they already cleared\n\n// WHICH SURFACE? The DARKEST one the ink lands on — the default is --accent\n// (#ebe9e5), not --background, because an ink clamped on the canvas measures\n// 3.78:1 the moment the row under it is hovered. Pass your own when the region is\n// darker than the package light theme, and ALWAYS for a region inside a dark theme\n// (--accent there is #3c3a34) or the ink is walked the wrong way:\nconst darkRegion = tenantTheme(tenant.primary_color, { surface: \"#3c3a34\" });\n\n// Still your call whether the raw seed is usable as ink somewhere the library does\n// not paint — contrastRatio is the same arithmetic, exported:\nif ((contrastRatio(tenant.primary_color, \"#ffffff\") ?? 0) >= 4.5) {\n  // …the seed ITSELF, unclamped, on a white page\n}\n\n// The raw conversion, if you need it on its own (the inverse of hslToHex):\nhexToHsl(\"#0071bd\");   // \"204.13 100% 37.06%\"\n\n// Worked screen: /showcase/tenant-brand-color · docs/CUSTOMER-THEMING.md\n// Why there is no <TenantTheme> component: docs/COMPOSITION-VS-COMPONENT.md §3.2\n",
  "name": "tenant-brand-color",
  "tagline": "Wear a CUSTOMER's own hex at runtime, scoped to one region — tenantTheme(hex) returns the token declarations plus the WCAG 2.2 AA label, so you never hand-roll hex→HSL or guess a foreground. There is no <TenantTheme> component and there should not be one.",
  "tags": [
    "theme",
    "tenant",
    "multi-tenant",
    "brand",
    "color",
    "colour",
    "contrast",
    "wcag",
    "a11y",
    "runtime",
    "scoped",
    "hex",
    "hsl",
    "white-label",
    "launcher"
  ]
}
