From 1eff23aca8e4a228ad9de02ecdeb9464e973a32d Mon Sep 17 00:00:00 2001 From: thePR0M3TH3AN <53631862+PR0M3TH3AN@users.noreply.github.com> Date: Tue, 21 Jul 2026 15:05:55 -0400 Subject: [PATCH] Expose widget theming as CSS custom properties, and a max-width override renders in a shadow root, but every color/radius/font value already flowed through a CSS custom property on :host -- these inherit through the shadow boundary, so a host page can already override the widget's look from ordinary light-DOM CSS. This adds the two properties that weren't yet exposed: - --bl-accent-fg (default: white): text color on top of --bl-accent. Was hardcoded on button.primary, so a host with a light accent color and its own dark-on-accent convention had no way to match it. - --bl-max-width (default: 380px): the widget stays a fixed-width card by default (unchanged for existing embedders), but a host page that wants it to fill a wider container can now override this instead of being stuck with the hardcoded cap. Also routes the hardcoded font-family through --bl-font-family so that's themeable too. Documented in the README with a worked example (BitRoad, which maps every one of these onto its own design tokens to render the widget in its own colors at full width). Co-Authored-By: Claude Sonnet 5 --- README.md | 36 +++++++++++++++++++++++++++++++++++ packages/widget/src/styles.ts | 13 ++++++++++--- 2 files changed, 46 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 3d66de9..0367773 100644 --- a/README.md +++ b/README.md @@ -90,6 +90,42 @@ its static shell HTML, only toggling `hidden` — see its `src/nostr/bitloginAdapter.mjs` and the `#bitloginMount` container in its `index.html` for a worked example of the pattern. +### Theming + +`` renders in a shadow root, but its colors, radius, font, and +max-width are all read from CSS custom properties on `:host` — and custom +properties inherit through the shadow boundary, so a host page can override +every one of them from ordinary light-DOM CSS (a rule targeting the element, +or an inline style), without touching the widget's internals: + +```css +bitlogin-auth { + --bl-accent: #3d9bff; /* primary button / link / focus ring color */ + --bl-accent-hover: #2f86e0; /* primary button hover */ + --bl-accent-fg: #04101f; /* text color on top of --bl-accent */ + --bl-bg: #12151f; /* card background */ + --bl-fg: #eaeef6; /* body text */ + --bl-muted: #98a2b6; /* secondary text */ + --bl-border: rgba(255, 255, 255, 0.10); + --bl-input-bg: #1a1e2b; /* input/credential-box background */ + --bl-danger: #ff6b6b; + --bl-danger-bg: rgba(255, 107, 107, 0.16); + --bl-warn: #ff6b6b; + --bl-warn-bg: rgba(255, 107, 107, 0.16); + --bl-radius: 14px; + --bl-font-family: Inter, ui-sans-serif, system-ui, sans-serif; + --bl-max-width: none; /* default 380px keeps it a fixed-width card; + override to fill a wider container */ +} +``` + +All of these have defaults (a light theme, with a `prefers-color-scheme: +dark` media-query fallback and a `data-theme="dark"|"light"` attribute +override for host pages with their own theme toggle), so overriding none of +them is a valid choice — the widget just uses its own look. See BitRoad's +`bitlogin-auth { ... }` rule in `src/styles.css` for a real integration that +maps every one of these to its own design tokens. + ## Deployment The demo site deploys to Vercel: `vercel.json` at the repo root sets the diff --git a/packages/widget/src/styles.ts b/packages/widget/src/styles.ts index baa1a71..fb58717 100644 --- a/packages/widget/src/styles.ts +++ b/packages/widget/src/styles.ts @@ -2,6 +2,7 @@ export const WIDGET_STYLES = /* css */ ` :host { --bl-accent: #6d28d9; --bl-accent-hover: #5b21b6; + --bl-accent-fg: white; --bl-bg: #ffffff; --bl-fg: #16151a; --bl-muted: #6b7280; @@ -12,9 +13,16 @@ export const WIDGET_STYLES = /* css */ ` --bl-warn: #92400e; --bl-warn-bg: #fffbeb; --bl-radius: 14px; + /* Caps the widget's own width; a host page that wants it to fill a wider + container (rather than stay a fixed-width card) overrides this to a + larger value or "none". */ + --bl-max-width: 380px; + --bl-font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; all: initial; display: inline-block; - font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; + width: 100%; + max-width: var(--bl-max-width); + font-family: var(--bl-font-family); color-scheme: light dark; } @media (prefers-color-scheme: dark) { @@ -47,7 +55,6 @@ export const WIDGET_STYLES = /* css */ ` * { box-sizing: border-box; } .card { width: 100%; - max-width: 380px; background: var(--bl-bg); color: var(--bl-fg); border: 1px solid var(--bl-border); @@ -122,7 +129,7 @@ button { width: 100%; margin-top: 14px; } -button.primary { background: var(--bl-accent); color: white; } +button.primary { background: var(--bl-accent); color: var(--bl-accent-fg); } button.primary:hover { background: var(--bl-accent-hover); } button.primary:disabled { opacity: 0.55; cursor: default; } button.secondary { background: transparent; color: var(--bl-fg); border: 1px solid var(--bl-border); }