Feedback Widget

Control for submitting user feedback.

html

<div class="feedback-widget" data-rating="emoji" style="--accent:#D9C8F0">
  <p class="feedback-widget__prompt">How was your experience?</p>
  <div class="feedback-widget__row feedback-widget__row--emoji" role="group" aria-label="Rating">
    <button type="button" aria-label="Bad">&#128542;</button>
    <button type="button" aria-label="Okay">&#128578;</button>
    <button type="button" aria-label="Great">&#128516;</button>
  </div>
  <div class="feedback-widget__row feedback-widget__row--stars" role="group" aria-label="Rating">
    <button type="button" aria-label="1 star">&#9733;</button>
    <button type="button" aria-label="2 stars">&#9733;</button>
    <button type="button" aria-label="3 stars">&#9733;</button>
    <button type="button" aria-label="4 stars">&#9733;</button>
    <button type="button" aria-label="5 stars">&#9733;</button>
  </div>
  <div class="feedback-widget__row feedback-widget__row--thumbs" role="group" aria-label="Rating">
    <button type="button" aria-label="Thumbs up">&#128077;</button>
    <button type="button" aria-label="Thumbs down">&#128078;</button>
  </div>
  <details class="feedback-widget__details" open>
    <summary>Add a comment</summary>
    <textarea class="feedback-widget__textarea" placeholder="Tell us more (optional)" rows="3"></textarea>
  </details>
</div>
<style>
.feedback-widget { font: 16px/1.5 system-ui, sans-serif; color: #241430; max-width: 300px; padding: 18px; background: #FAF7F2; border: 1.5px solid #24143022; border-radius: 12px; }
.feedback-widget__prompt { margin: 0 0 12px; font-weight: 600; font-size: 14.5px; }
.feedback-widget__row { display: none; gap: 8px; margin-bottom: 14px; }
.feedback-widget[data-rating="emoji"] .feedback-widget__row--emoji,
.feedback-widget[data-rating="stars"] .feedback-widget__row--stars,
.feedback-widget[data-rating="thumbs"] .feedback-widget__row--thumbs { display: flex; }
.feedback-widget__row button { background: #fff; border: 1.5px solid #24143022; border-radius: 8px; width: 36px; height: 36px; font-size: 16px; cursor: pointer; }
.feedback-widget__row--stars button { color: #24143044; }
.feedback-widget__row button:hover, .feedback-widget__row button:focus-visible { border-color: var(--accent); outline: none; }
.feedback-widget__details summary { cursor: pointer; font-size: 13px; font-weight: 600; color: var(--accent); list-style: none; }
.feedback-widget__details summary::-webkit-details-marker { display: none; }
.feedback-widget__textarea { width: 100%; box-sizing: border-box; margin-top: 8px; border: 1.5px solid #24143022; border-radius: 8px; padding: 8px 10px; font: 13.5px system-ui, sans-serif; resize: vertical; }
.feedback-widget__textarea:focus-visible { outline: 2px solid var(--accent); border-color: transparent; }
</style>

tailwind

<div class="feedback-widget-tw max-w-[300px] p-4.5 bg-[#FAF7F2] border-[1.5px] border-[#24143022] rounded-xl font-sans text-[#241430]" data-rating="emoji" style="--accent:#D9C8F0">
  <p class="m-0 mb-3 font-semibold text-sm">How was your experience?</p>
  <div class="rating-row-emoji hidden gap-2 mb-3.5" role="group" aria-label="Rating">
    <button type="button" class="bg-white border-[1.5px] border-[#24143022] rounded-lg w-9 h-9 text-base" aria-label="Bad">&#128542;</button>
    <button type="button" class="bg-white border-[1.5px] border-[#24143022] rounded-lg w-9 h-9 text-base" aria-label="Okay">&#128578;</button>
    <button type="button" class="bg-white border-[1.5px] border-[#24143022] rounded-lg w-9 h-9 text-base" aria-label="Great">&#128516;</button>
  </div>
  <div class="rating-row-stars hidden gap-2 mb-3.5" role="group" aria-label="Rating">
    <button type="button" class="bg-white border-[1.5px] border-[#24143022] rounded-lg w-9 h-9 text-[#24143044]" aria-label="1 star">&#9733;</button>
    <button type="button" class="bg-white border-[1.5px] border-[#24143022] rounded-lg w-9 h-9 text-[#24143044]" aria-label="2 stars">&#9733;</button>
    <button type="button" class="bg-white border-[1.5px] border-[#24143022] rounded-lg w-9 h-9 text-[#24143044]" aria-label="3 stars">&#9733;</button>
  </div>
  <div class="rating-row-thumbs hidden gap-2 mb-3.5" role="group" aria-label="Rating">
    <button type="button" class="bg-white border-[1.5px] border-[#24143022] rounded-lg w-9 h-9 text-base" aria-label="Thumbs up">&#128077;</button>
    <button type="button" class="bg-white border-[1.5px] border-[#24143022] rounded-lg w-9 h-9 text-base" aria-label="Thumbs down">&#128078;</button>
  </div>
  <details open>
    <summary class="cursor-pointer text-[13px] font-semibold list-none" style="color:#D9C8F0">Add a comment</summary>
    <textarea class="w-full box-border mt-2 border-[1.5px] border-[#24143022] rounded-lg px-2.5 py-2 text-[13.5px]" placeholder="Tell us more (optional)" rows="3"></textarea>
  </details>
</div>
<style>
.feedback-widget-tw[data-rating="emoji"] .rating-row-emoji,
.feedback-widget-tw[data-rating="stars"] .rating-row-stars,
.feedback-widget-tw[data-rating="thumbs"] .rating-row-thumbs { display: flex; }
.feedback-widget-tw summary::-webkit-details-marker { display: none; }
</style>

react

import { useState } from "react";

export function FeedbackWidget({
  prompt = "How was your experience?",
  ratingStyle = "emoji",
  placeholder = "Tell us more (optional)",
  accent = "#D9C8F0",
  open: initialOpen = true,
}) {
  const [open, setOpen] = useState(initialOpen);
  const rows = {
    emoji: [["Bad", "😞"], ["Okay", "🙂"], ["Great", "😄"]],
    stars: [["1 star", "★"], ["2 stars", "★"], ["3 stars", "★"], ["4 stars", "★"], ["5 stars", "★"]],
    thumbs: [["Thumbs up", "👍"], ["Thumbs down", "👎"]],
  }[ratingStyle] ?? [];
  return (
    <div style={{ fontFamily: "system-ui, sans-serif", color: "#241430", maxWidth: 300, padding: 18, background: "#FAF7F2", border: "1.5px solid #24143022", borderRadius: 12 }}>
      <p style={{ margin: "0 0 12px", fontWeight: 600, fontSize: 14.5 }}>{prompt}</p>
      <div role="group" aria-label="Rating" style={{ display: "flex", gap: 8, marginBottom: 14 }}>
        {rows.map(([label, glyph]) => (
          <button key={label} type="button" aria-label={label} style={{ background: "#fff", border: "1.5px solid #24143022", borderRadius: 8, width: 36, height: 36, fontSize: 16, cursor: "pointer" }}>{glyph}</button>
        ))}
      </div>
      <button type="button" onClick={() => setOpen((o) => !o)} style={{ background: "none", border: "none", padding: 0, cursor: "pointer", fontSize: 13, fontWeight: 600, color: accent }}>
        Add a comment
      </button>
      {open && (
        <textarea placeholder={placeholder} rows={3} style={{ width: "100%", boxSizing: "border-box", marginTop: 8, border: "1.5px solid #24143022", borderRadius: 8, padding: "8px 10px", fontSize: 13.5, resize: "vertical" }} />
      )}
    </div>
  );
}