Skip to content

Configurable widget theme (scheme, accent, position, label) #8

Description

@ezzcodeezzlife

Summary

Let owners theme the widget so it does not always look like feedback2code’s orange terminal chip. The marketing FAQ already promises this: “The widget auto-detects light and dark mode. … Custom theming options are coming soon.” (components/seo/home-next-seo.tsx).

Today colors are hardcoded in the iframe script from prefers-color-scheme (lib/widget-embed.ts). There is no configure UI and no way to force light on a dark marketing site (or vice versa).

Current behavior

Inside buildFrameWidgetInlineScript:

  • isDark = matchMedia("(prefers-color-scheme: dark)")
  • Two palettes C with accent #ff6b00, monospace stack Fira/JetBrains, FAB label [ feedback ], bottom-right only (safe-area-inset-*).
  • Parent iframe is a large transparent hit-rect, also bottom-right (buildParentEmbedScript).

Configure (configure/page.tsx) cannot set appearance. Widget script cache is max-age=300; iframe HTML is no-store.

hello (lib/widget-hello.ts) does not return theme.

Problem

Agencies will not paste a widget that fights the client’s brand (wrong orange, wrong corner covering a chat widget like Intercom/Crisp, dark FAB on a dark footer). This is a sales blocker, not polish.

Proposed design

v1 knobs (keep the list short)

Setting Values Default
Color scheme system / light / dark system (today)
Accent hex (we validate) #ff6b00
Position bottom-right / bottom-left bottom-right
Label string, max 24 chars feedback

Explicitly not v1: arbitrary CSS injection, custom fonts, border-radius scales, hiding the brand entirely (keep a discreet “f2c” or “feedback2code” in the panel footer for trust; paid “remove branding” can be Pro later).

Config storage + delivery

Store on RepositoryConfig (JSON widgetTheme or columns). Same hello/config channel as path allowlist so changing theme does not require recopying the snippet.

Flow:

  1. Parent script requests config (GET hello//h) with widget id + origin.
  2. Response includes { theme: { scheme, accent, position, label } }.
  3. Parent sizes/positions the iframe (left vs right).
  4. Parent posts theme into the iframe with the existing f2c: "parent" message (extend payload) or the iframe fetches hello too. Prefer one fetch in the parent, then postMessage, so the iframe does not race.

If hello fails: fall back to today’s hardcoded defaults (widget still works).

Live preview on the configure page: a static mini-FAB using the same tokens. Do not iframe production feedback2code.dev into the dashboard unless easy.

Contrast / a11y

  • If they pick a light accent, FAB text should flip to #000 vs #fff (we already use btnFg black on orange). Compute contrast; if accent is too close to bg, show a configure warning.
  • Keep existing :focus outlines; do not drop aria-label on close.
  • Left position must still respect safe-area-inset-left on iOS.

Plan gating (assumption)

v1 un-gated (or gate only “remove branding”). Theme is why agencies embed. If we need a Pro hook, “custom accent” is the upsell — but shipping it on Free will convert more than locking it. Recommendation: all v1 knobs on Free.

Update FAQ from “coming soon” to the real list when this ships.

Assumptions

  • We stay a designed widget (terminal-ish), not a fully white-label CSS soup. Accent + scheme + corner is 90% of Intercom-style requests.
  • No runtime window.Feedback2Code('theme', …) API in v1; configure UI is the source of truth (avoids clients checking theme into git and drifting).
  • label is owner-controlled and appears on someone else’s site — strip RTL override chars / XSS; it is textContent, never innerHTML.
  • Dark/light follows widget setting, not the dashboard user’s OS. Client visitors get the owner’s choice when not system.

Acceptance criteria

  • Configure: scheme, accent (color input + hex), position, label; save without regenerating widget id.
  • Live site updates within ~1 minute (hello cache), no snippet recopy.
  • bottom-left FAB does not cover typical left-nav; iframe hit-rect moves with it.
  • light on a dark OS still renders the light palette (and vice versa).
  • Invalid hex rejected at save; missing config uses defaults.
  • FAQ copy updated when shipped.
  • Accent used for FAB, header label, and the 4px accent bar (current C.accent usages).

Out of scope

  • Per-page theme overrides.
  • Matching Intercom/Crisp collision avoidance (z-index is already 2147483647; document “put others lower” rather than building a collider).
  • Uploading a custom FAB icon.

Implementation notes

  • lib/widget-embed.ts — parameterize C, FAB side, label; stop closing over hardcoded orange only.
  • Parent iframe cssText position currently bottom:0;right:0 — must switch to left.
  • Shared types with hello payload and Prisma JSON.
  • app/embed/frame/route.ts could accept ?accent= as a cache-buster; prefer postMessage so we do not leak a huge query cache surface.

See also

  • Page-path allowlist (shared hello/config endpoint — implement one config API, two features)
  • Element picker (pick overlay color should use the same accent)
  • Use-case landings (show a themed widget screenshot on the agency page)

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:dashboardDashboard, repo configure, accountarea:widgetEmbeddable widget, iframe, parent scriptenhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions