Qaid
ARTICLE

Styling the Modal: Themes & Custom CSS

Restyle the feedback modal with custom CSS. Every selector you can target, plus four worked themes and the mobile sheet.

Qaid Team

Most of the styling attention goes on the buttons, and then a user clicks one and spends the next thirty seconds looking at the modal instead. Here is every class inside it, and four themes built out of them.

Getting CSS into the modal

The modal lives in a shadow root, so your page stylesheet cannot reach it. The css option is the way in. Whatever string you pass goes into a <style> inside that root, alongside the modal, the backdrop and every other overlay element.

new QaidFeedback({
  endpoint: '/api/feedback',
  css: `
    .qaid-modal-box {
      background: #1a1a2e;
      color: #e2e8f0;
    }
  `
});

Nothing on your page can win a specificity fight with it, because nothing on your page is in scope.

Every class in the modal

Fifteen selectors, and you will use about four of them:

Selector Element
.qaid-backdrop Dark overlay behind the modal
.qaid-modal-container Positioned wrapper (fixed, sets font)
.qaid-modal-arrow Triangle pointing to the selected element
.qaid-modal-box The modal card itself (background, shadow, padding)
.qaid-modal-header Flex row containing toggle + text
.qaid-modal-header-text Container for title and subtitle
.qaid-modal-title h3 heading
.qaid-modal-subtitle p description text
button.qaid-type-toggle Thumbs up/down toggle circle
.qaid-textarea Message input field
.qaid-btn-row Flex row containing action buttons
button.qaid-btn-submit Submit/Skip button
.qaid-bottom-sheet Mobile modal wrapper (< 640px)
.qaid-bottom-sheet-content Mobile modal card
.qaid-bottom-sheet-handle Drag handle bar at top of sheet

What config handles without CSS

A few things are settings rather than stylesheet:

new QaidFeedback({
  endpoint: '/api/feedback',
  modalWidth: 420,           // Modal width in pixels (default: 400)
  backdropOpacity: 0.4,      // Backdrop darkness 0-1 (default: 0.3)
  fontFamily: 'Inter, system-ui, sans-serif',
  fontSize: 15,              // Base font size in pixels (default: 16)
  colors: {
    marker: '#8b5cf6'        // Submit button color + textarea focus ring
  }
});

marker earns particular attention. It sets the submit button background and the focus ring on the textarea, so getting it wrong costs you two things at once. Everything else goes through css.

Dark mode, and overriding it

The modal follows the system setting through the CSS light-dark() function: white backgrounds in light mode, dark greys in dark. To pin it one way whatever the reader’s machine says, override the properties yourself:

/* Force dark modal */
.qaid-modal-box {
  background: #1f2937;
  color: #f9fafb;
}
.qaid-modal-title { color: #f9fafb; }
.qaid-modal-subtitle { color: #9ca3af; }
.qaid-textarea {
  background: #111827;
  color: #f9fafb;
  border-color: #374151;
}

Frosted glass

Worth doing when there is something colourful behind it, and pointless when there isn’t:

Loading the live demo…
new QaidFeedback({
  endpoint: '/api/feedback',
  colors: {
    positive: '#4ade80',
    negative: '#f87171',
    marker: '#a78bfa'
  },
  css: `
    .qaid-modal-box {
      background: rgba(255, 255, 255, 0.12);
      backdrop-filter: blur(16px);
      -webkit-backdrop-filter: blur(16px);
      border: 1px solid rgba(255, 255, 255, 0.2);
      box-shadow: 0 25px 50px rgba(0, 0, 0, 0.3);
    }
    .qaid-modal-title { color: #fff; }
    .qaid-modal-subtitle { color: rgba(255, 255, 255, 0.7); }
    .qaid-textarea {
      background: rgba(255, 255, 255, 0.1);
      border-color: rgba(255, 255, 255, 0.2);
      color: #fff;
    }
    .qaid-textarea::placeholder { color: rgba(255, 255, 255, 0.4); }
    .qaid-textarea:focus {
      border-color: rgba(255, 255, 255, 0.4);
    }
    .qaid-modal-container.qaid-below .qaid-modal-arrow {
      border-bottom-color: rgba(255, 255, 255, 0.12);
    }
    .qaid-modal-container.qaid-above .qaid-modal-arrow {
      border-top-color: rgba(255, 255, 255, 0.12);
    }
    .qaid-bottom-sheet-content {
      background: rgba(255, 255, 255, 0.12);
      backdrop-filter: blur(16px);
      -webkit-backdrop-filter: blur(16px);
    }
  `
});

The .qaid-modal-arrow rule is the one people miss. Leave it out and the arrow stays white while everything else goes translucent. The .qaid-bottom-sheet-content rule carries the same treatment to phones.

Terminal

Monospace type, green on near-black, for developer tools:

Loading the live demo…
new QaidFeedback({
  endpoint: '/api/feedback',
  fontFamily: "'JetBrains Mono', 'Fira Code', monospace",
  fontSize: 14,
  colors: { marker: '#22d3ee' },
  text: {
    modalTitle: '> feedback received',
    modalSubtitle: '// add context to your report',
    placeholder: '$ describe what happened...',
    submitButton: 'exec',
    skipButton: '^C'
  },
  css: `
    .qaid-modal-box {
      background: #0d1117;
      border: 1px solid #30363d;
      border-radius: 0.5rem;
      box-shadow: 0 16px 48px rgba(0, 0, 0, 0.6);
    }
    .qaid-modal-title {
      color: #58a6ff;
      font-weight: 400;
    }
    .qaid-modal-subtitle {
      color: #484f58;
      font-style: italic;
    }
    .qaid-textarea {
      background: #161b22;
      border-color: #30363d;
      color: #c9d1d9;
      border-radius: 0.25rem;
      font-family: inherit;
    }
    .qaid-textarea::placeholder { color: #484f58; }
    .qaid-textarea:focus { border-color: #58a6ff; }
    button.qaid-btn-submit {
      border-radius: 0.25rem;
      text-transform: uppercase;
      letter-spacing: 0.05em;
      font-family: inherit;
    }
    .qaid-modal-container.qaid-below .qaid-modal-arrow {
      border-bottom-color: #0d1117;
    }
    .qaid-modal-container.qaid-above .qaid-modal-arrow {
      border-top-color: #0d1117;
    }
    .qaid-bottom-sheet-content {
      background: #0d1117;
      border: 1px solid #30363d;
    }
    .qaid-bottom-sheet-handle { background: #30363d; }
  `
});

The typeface comes from fontFamily in config rather than CSS, and the rewritten text strings do as much work here as the colours do.

Soft pastel

Rounded and purple, for consumer products:

Loading the live demo…
new QaidFeedback({
  endpoint: '/api/feedback',
  colors: { marker: '#e879f9' },
  text: {
    modalTitle: 'Thanks for sharing!',
    modalSubtitle: "We'd love to hear more from you.",
    submitButton: 'Send',
    skipButton: 'Maybe later'
  },
  css: `
    .qaid-modal-box {
      background: #fefefe;
      border: 2px solid #f0e4f7;
      border-radius: 1.25rem;
      box-shadow: 0 12px 40px rgba(168, 85, 247, 0.1);
    }
    .qaid-modal-title { color: #7c3aed; }
    .qaid-modal-subtitle { color: #a78bfa; }
    .qaid-textarea {
      background: #faf5ff;
      border: 2px solid #ede9fe;
      border-radius: 0.75rem;
      color: #4c1d95;
    }
    .qaid-textarea::placeholder { color: #c4b5fd; }
    .qaid-textarea:focus { border-color: #c084fc; }
    button.qaid-btn-submit {
      border-radius: 2rem;
      padding: 0.5rem 1.5rem;
    }
    button.qaid-type-toggle:not(.qaid-type-toggle-custom) {
      border-radius: 0.75rem;
    }
    .qaid-modal-container.qaid-below .qaid-modal-arrow {
      border-bottom-color: #fefefe;
    }
    .qaid-modal-container.qaid-above .qaid-modal-arrow {
      border-top-color: #fefefe;
    }
    .qaid-bottom-sheet-content {
      background: #fefefe;
      border-top: 2px solid #f0e4f7;
    }
  `
});

Rounding the submit button and leaving the type toggle square is the mistake to avoid, so that corner radius gets set twice here.

Bordered minimal

Borders instead of shadows:

Loading the live demo…
new QaidFeedback({
  endpoint: '/api/feedback',
  colors: { marker: '#0284c7' },
  backdropOpacity: 0.15,
  css: `
    .qaid-modal-box {
      background: #ffffff;
      border: 2px solid #e2e8f0;
      border-radius: 0.5rem;
      box-shadow: none;
      padding: 1.25rem;
    }
    .qaid-modal-title { color: #0f172a; font-size: 1rem; }
    .qaid-modal-subtitle { color: #64748b; font-size: 0.8125rem; }
    .qaid-textarea {
      border: 2px solid #e2e8f0;
      border-radius: 0.375rem;
      background: #f8fafc;
    }
    .qaid-textarea:focus {
      border-color: #0284c7;
      background: #fff;
    }
    button.qaid-btn-submit { border-radius: 0.375rem; }
    .qaid-modal-container.qaid-below .qaid-modal-arrow {
      border-bottom-color: #ffffff;
    }
    .qaid-modal-container.qaid-above .qaid-modal-arrow {
      border-top-color: #ffffff;
    }
  `
});

Dropping box-shadow flattens the modal onto the page, and the lowered backdropOpacity stops the dimmed page behind it from putting the weight back.

The mobile bottom sheet

Under 640px the modal becomes a sheet that slides up from the bottom. The outer classes change, so every theme above needs a second set of rules:

/* Desktop modal */
.qaid-modal-box {
  background: #1a1a2e;
}

/* Mobile bottom sheet */
.qaid-bottom-sheet-content {
  background: #1a1a2e;
}

/* The drag handle */
.qaid-bottom-sheet-handle {
  background: rgba(255, 255, 255, 0.3);
}

Only the wrapper differs. The title, subtitle, textarea and buttons inside keep the classes they had, so .qaid-modal-title and .qaid-textarea still hit.

Four rules people get wrong

The arrow needs two rules, not one. A modal below its button colours the arrow with border-bottom-color and one above it uses border-top-color, so target .qaid-modal-container.qaid-below .qaid-modal-arrow and .qaid-modal-container.qaid-above .qaid-modal-arrow separately.

Colours belong in config and layout belongs in CSS. Set colors.marker and let it drive the submit button and the focus ring, then use css for backgrounds, borders and spacing.

Narrow the window before you call a theme finished. The bottom sheet is the layout most of your traffic will see.

::placeholder works inside the shadow root, so placeholder colour is stylable like anything else.

With a script tag rather than JavaScript, the CSS travels in a JSON config block:

<script type="application/json" data-feedback-config>
{
  "endpoint": "/api/feedback",
  "css": ".qaid-modal-box { background: #1a1a2e; color: #e2e8f0; }"
}
</script>
<script src="https://unpkg.com/@qaiddev/thumbs-embed/dist/embed.js"></script>

Or reference an existing style element with data-css-selector:

<style id="qaid-theme">
  .qaid-modal-box { background: #1a1a2e; color: #e2e8f0; }
</style>
<script
  src="https://unpkg.com/@qaiddev/thumbs-embed/dist/embed.js"
  data-endpoint="/api/feedback"
  data-css-selector="#qaid-theme"
></script>
Back to all articles