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.
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:
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:
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:
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:
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>