VISUALLIdesign system

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

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

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

The panel

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

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

/* ==========================================================================
   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

/* ==========================================================================
   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;
  }
}