# Give feedback

People share ideas and vote on the roadmap without leaving Visualli. The **Give feedback** button is ours; the panel it opens is **QuackBack** (together.visualli.ai), embedded as an iframe and dressed in the Visualli theme through its custom CSS. Feature **VI-016** · PRODUCT.

## The button
- **Web app:** `Button` secondary, size `sm`, icon `message-square`, label **Give feedback** (sentence case), floating bottom-right, 16px (`space-4`) from the window edges. It hides while a right-hand panel (Sources, Go deeper) is open — that panel owns the right edge. The chat dock keeps 168px clear on both sides so the Composer stays centered and never runs under the button.
- **Phones:** no floating button — the bottom edge belongs to the Composer. **Give feedback** sits in the Vault footer as an icon button, next to Comfort (`Vault` `onFeedback`).
- **MCP widget, Chrome extension, desktop viewer:** none — people share ideas after *Open in Visualli*.
- It opens the panel with `Quackback('open')` (QuackBack's own launcher stays off: `Quackback('init', { launcher: false })`). If the SDK didn't load (ad blockers), the button stays but does nothing harmful.

```tsx
import { Button } from '@visualli/design-system/components';

<Button variant="secondary" size="sm" icon="message-square" onClick={() => window.Quackback?.('open')}>
  Give feedback
</Button>
```

## The panel
- **Frame (host page):** 360 × 560px, `radius-lg` corners, `shadow-pop`, anchored above the button 16px from the edge; on phones a bottom sheet (75% of the height). Paste `themes/quackback-host.css` into the page that loads the QuackBack SDK.
- **Inside (QuackBack's iframe):** QuackBack can't read our `tokens.css` — it lives on its own domain — so `themes/quackback-theme.css` carries our light and dark token values as QuackBack's own variables:

| QuackBack | Visualli token | |
| --- | --- | --- |
| `--background` · `--card` · `--popover` | `canvas` · `surface` · `surface-overlay` | grounds |
| `--foreground` · `--muted-foreground` | `ink` · `ink-muted` | text (≥ 5.6:1 in both themes) |
| `--primary` / `--primary-foreground` | `action` / `on-action` | the one primary button — ink, never a brand color |
| `--secondary` · `--muted` · `--accent` | `surface-sunken` | quiet fills and hovers |
| `--border` · `--input` · `--ring` | `line` · `line-strong` · `focus` | borders, inputs (3:1), focus ring |
| `--destructive` · `--success` | `danger` · `success` | |
| `--radius` · `--font-sans` | `radius-md` · Atkinson Hyperlegible Next | |

- **Votes** look like `UpvoteCard`: outlined in `line-strong`; voted = `topic-teal` with `node-ink` text (12:1). **Tags** look like a neutral `Badge`. The **selected tab** looks like `SegmentedControl` — never a coral or brand fill behind text.
- **Statuses** are colored in QuackBack's admin, not in CSS: *Inbox* `ink-subtle` · *Up Next* `info` · *Build* `warning` · *Live* `success` (light values: `#5f647c` · `#2f5a87` · `#8a5600` · `#1d7a4e`).

## Keeping it in sync
Both files are generated from the tokens (`npm run build:quackback-theme`, part of `npm run build`), so they change whenever a token does — then paste the new theme into QuackBack. The theme replaces the old *variables*, *Planned status*, *tags & badges*, *filter tabs* and *upvote* sections of QuackBack's custom CSS; keep the functional overrides (hide powered-by, sign-in labels, admin redirect, password and name cards) below it. The old `span:contains(…)` rule wasn't valid CSS and is gone.

### `themes/quackback-theme.css` — QuackBack › Settings › Branding › Custom CSS
```css
/* ==========================================================================
   VISUALLI THEME FOR QUACKBACK — generated from the Visualli design system v0.1.0
   (design-system/scripts/build-quackback-theme.mjs). Don't edit by hand: change the
   tokens or the script, then run `npm run build:quackback-theme`.

   Paste into QuackBack › Settings › Branding › Custom CSS. It replaces the old theme
   sections (variables, "Planned" status, tags & badges, filter tabs, upvote button);
   keep your functional overrides below it (hide powered-by, sign-in labels, admin
   redirect, password and name cards).
   ========================================================================== */
@import url("https://fonts.googleapis.com/css2?family=Atkinson+Hyperlegible+Next:wght@400;600;700&display=swap");

/* Light — Visualli "light" theme */
:root {
  --background: #f6f3ee; /* canvas */
  --foreground: #182140; /* ink */
  --card: #ffffff; /* surface */
  --card-foreground: #182140; /* ink */
  --popover: #ffffff; /* surface-overlay */
  --popover-foreground: #182140; /* ink */
  --primary: #182140; /* action */
  --primary-foreground: #ffffff; /* on-action */
  --secondary: #f0ece5; /* surface-sunken */
  --secondary-foreground: #182140; /* ink */
  --muted: #f0ece5; /* surface-sunken */
  --muted-foreground: #4f5671; /* ink-muted */
  --accent: #f0ece5; /* surface-sunken */
  --accent-foreground: #182140; /* ink */
  --destructive: #c22f3c; /* danger */
  --destructive-foreground: #ffffff; /* surface */
  --border: #e4dfd6; /* line */
  --input: #7a7f94; /* line-strong */
  --ring: #325e8c; /* focus */
  --success: #1d7a4e; /* success */
  --radius: 16px; /* radius-md: cards and the panel; controls derive smaller radii from it */
  --font-sans: "Atkinson Hyperlegible Next", ui-sans-serif, system-ui, sans-serif;
  font-family: var(--font-sans);
}

/* Dark — Visualli "dark" theme */
.dark {
  --background: #0f1220; /* canvas */
  --foreground: #eef0f7; /* ink */
  --card: #171b2b; /* surface */
  --card-foreground: #eef0f7; /* ink */
  --popover: #212639; /* surface-overlay */
  --popover-foreground: #eef0f7; /* ink */
  --primary: #f6f3ee; /* action */
  --primary-foreground: #182140; /* on-action */
  --secondary: #0f1220; /* surface-sunken */
  --secondary-foreground: #eef0f7; /* ink */
  --muted: #0f1220; /* surface-sunken */
  --muted-foreground: #b3b8cc; /* ink-muted */
  --accent: #0f1220; /* surface-sunken */
  --accent-foreground: #eef0f7; /* ink */
  --destructive: #ff8a93; /* danger */
  --destructive-foreground: #171b2b; /* surface */
  --border: #2b3148; /* line */
  --input: #6c7392; /* line-strong */
  --ring: #12c7d3; /* focus */
  --success: #5ed39e; /* success */
  --radius: 16px;
  --font-sans: "Atkinson Hyperlegible Next", ui-sans-serif, system-ui, sans-serif;
  font-family: var(--font-sans);
}

/* Votes like UpvoteCard (voted = topic-teal with node ink), tags like Badge,
   the selected filter tab like SegmentedControl. Topic colors stay pastel in dark mode. */
button[aria-label*="vote"],
button[class*="vote"] {
  background-color: #ffffff !important;
  border: 1.5px solid #7a7f94 !important;
  color: #182140 !important;
  border-radius: 10px !important;
}
button[aria-label*="vote"] svg,
button[class*="vote"] svg { stroke: currentColor !important; fill: none !important; color: inherit !important; }
button[aria-label*="vote"][aria-pressed="true"],
button[class*="vote"][data-state="on"],
[data-voted="true"] {
  background-color: #87f2f8 !important;
  border-color: #00cdd5 !important;
  color: #182140 !important;
}
[class*="badge"],
[class*="tag"] {
  background-color: #f0ece5 !important;
  color: #4f5671 !important;
  border: 1px solid #e4dfd6 !important;
  border-radius: 999px !important;
}
button[role="tab"][aria-selected="true"],
.active-filter {
  background-color: #ffffff !important;
  color: #182140 !important;
  box-shadow: 0 1px 2px rgba(24, 33, 64, 0.06), 0 8px 24px rgba(24, 33, 64, 0.08) !important;
  outline: 1px solid #e4dfd6 !important;
}

.dark button[aria-label*="vote"],
.dark button[class*="vote"] {
  background-color: #171b2b !important;
  border: 1.5px solid #6c7392 !important;
  color: #eef0f7 !important;
  border-radius: 10px !important;
}
.dark button[aria-label*="vote"] svg,
.dark button[class*="vote"] svg { stroke: currentColor !important; fill: none !important; color: inherit !important; }
.dark button[aria-label*="vote"][aria-pressed="true"],
.dark button[class*="vote"][data-state="on"],
.dark [data-voted="true"] {
  background-color: #87f2f8 !important;
  border-color: #0fc5cd !important;
  color: #182140 !important;
}
.dark [class*="badge"],
.dark [class*="tag"] {
  background-color: #0f1220 !important;
  color: #b3b8cc !important;
  border: 1px solid #2b3148 !important;
  border-radius: 999px !important;
}
.dark button[role="tab"][aria-selected="true"],
.dark .active-filter {
  background-color: #171b2b !important;
  color: #eef0f7 !important;
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.40), 0 8px 24px rgba(0, 0, 0, 0.35) !important;
  outline: 1px solid #2b3148 !important;
}

/* Focus like every Visualli control: a solid 2px focus ring */
:focus-visible { outline: 2px solid #325e8c !important; outline-offset: 2px !important; }
.dark :focus-visible { outline-color: #12c7d3 !important; }
```

### `themes/quackback-host.css` — the host page
```css
/* ==========================================================================
   QUACKBACK PANEL FRAME (host page) — generated from the Visualli design system v0.1.0.
   The panel QuackBack opens on Quackback("open"), anchored above the "Give feedback" button:
   radius-lg corners, shadow-pop, 16px (space-4) from the window edge; a bottom sheet on phones.
   ========================================================================== */
[data-quackback-widget],
[class*="quackback"],
[id*="quackback"] {
  width: 100% !important;
  max-width: 360px !important;
  height: 560px !important;
  max-height: calc(100dvh - 96px) !important;
  right: 16px !important;
  bottom: 64px !important; /* clears the 32px "Give feedback" button + 16px gap + 16px edge */
  border-radius: 24px !important;
  box-shadow: 0 2px 4px rgba(24, 33, 64, 0.08), 0 16px 40px rgba(24, 33, 64, 0.14) !important;
  overflow: hidden !important;
}
[data-quackback-widget] iframe,
[class*="quackback"] iframe,
[id*="quackback"] iframe { width: 100% !important; height: 100% !important; max-height: none !important; }

html.dark [data-quackback-widget],
html.dark [class*="quackback"],
html.dark [id*="quackback"] { box-shadow: 0 2px 4px rgba(0, 0, 0, 0.50), 0 16px 40px rgba(0, 0, 0, 0.50) !important; color-scheme: dark !important; }
html.light [data-quackback-widget],
html.light [class*="quackback"],
html.light [id*="quackback"] { color-scheme: light !important; }

/* Phones: a bottom sheet, like every Visualli sheet */
@media (max-width: 640px) {
  [data-quackback-widget],
  [class*="quackback"],
  [id*="quackback"] {
    position: fixed !important;
    left: 0 !important; right: 0 !important; bottom: 0 !important;
    max-width: 100% !important;
    height: 75dvh !important;
    max-height: 75dvh !important;
    border-radius: 24px 24px 0 0 !important;
  }
}
```
