Repository navigation
Configurable widget theme (scheme, accent, position, label) #8
Description
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
Cwith 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:
- Parent script requests config (
GEThello//h) with widget id + origin. - Response includes
{ theme: { scheme, accent, position, label } }. - Parent sizes/positions the iframe (left vs right).
- 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
#000vs#fff(we already usebtnFgblack on orange). Compute contrast; if accent is too close to bg, show a configure warning. - Keep existing
:focusoutlines; do not droparia-labelon close. - Left position must still respect
safe-area-inset-lefton 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). labelis 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-leftFAB does not cover typical left-nav; iframe hit-rect moves with it. -
lighton 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.accentusages).
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— parameterizeC, FAB side, label; stop closing over hardcoded orange only.- Parent iframe
cssTextposition currentlybottom:0;right:0— must switch to left. - Shared types with hello payload and Prisma JSON.
app/embed/frame/route.tscould 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)
Activity
ezzcodeezzlife commentedon Aug 18, 2026
Related: path allowlist #5 (shared config channel), element picker #1, landings #4