webdevpuneet.com

Mood Picker — Emoji Face Satisfaction Selector

Mood Picker · Forms · Plain HTML, CSS & JS · Live preview

CategoryForms

Copy & paste

Source Code

<form class="mp-card" id="mpForm">
  <h3>How was your experience?</h3>
  <div class="mp-faces" id="mpFaces" role="radiogroup" aria-label="Mood">
    <button type="button" class="mp-face" data-value="1" data-label="Awful" role="radio" aria-checked="false" aria-label="Awful">
      <svg viewBox="0 0 48 48"><circle class="mp-bg" cx="24" cy="24" r="21"/><circle cx="17" cy="20" r="2.4"/><circle cx="31" cy="20" r="2.4"/><path class="mp-mouth" d="M16 33 Q24 26 32 33"/></svg>
    </button>
    <button type="button" class="mp-face" data-value="2" data-label="Bad" role="radio" aria-checked="false" aria-label="Bad">
      <svg viewBox="0 0 48 48"><circle class="mp-bg" cx="24" cy="24" r="21"/><circle cx="17" cy="20" r="2.4"/><circle cx="31" cy="20" r="2.4"/><path class="mp-mouth" d="M16 31 Q24 28 32 31"/></svg>
    </button>
    <button type="button" class="mp-face" data-value="3" data-label="Okay" role="radio" aria-checked="false" aria-label="Okay">
      <svg viewBox="0 0 48 48"><circle class="mp-bg" cx="24" cy="24" r="21"/><circle cx="17" cy="20" r="2.4"/><circle cx="31" cy="20" r="2.4"/><path class="mp-mouth" d="M16 30 L32 30"/></svg>
    </button>
    <button type="button" class="mp-face" data-value="4" data-label="Good" role="radio" aria-checked="false" aria-label="Good">
      <svg viewBox="0 0 48 48"><circle class="mp-bg" cx="24" cy="24" r="21"/><circle cx="17" cy="20" r="2.4"/><circle cx="31" cy="20" r="2.4"/><path class="mp-mouth" d="M16 29 Q24 35 32 29"/></svg>
    </button>
    <button type="button" class="mp-face" data-value="5" data-label="Great" role="radio" aria-checked="false" aria-label="Great">
      <svg viewBox="0 0 48 48"><circle class="mp-bg" cx="24" cy="24" r="21"/><circle cx="17" cy="20" r="2.4"/><circle cx="31" cy="20" r="2.4"/><path class="mp-mouth" d="M15 27 Q24 38 33 27"/></svg>
    </button>
  </div>
  <p class="mp-label" id="mpLabel">Pick the face that fits</p>
  <button class="mp-submit" type="submit" id="mpSubmit" disabled>Send feedback</button>
</form>

Extend with AI

Take This Snippet Further With AI

The AI prompt for this one is for Patreon supporters ($3 a month), along with a site with no ads. Supporters enter the passcode once and it is remembered in this browser.

· Become a supporter

What's included

Features

SVG faces
Crisp, consistent smileys drawn in SVG — no emoji-font differences.
Mood-encoded mouths
The mouth path curves from frown to grin per face.
Hover preview
Previewing a mood does not change the saved value.
Click to commit
Clicking stores the score and enables submit.
Keyboard accessible
Arrow keys move and set the mood in a radio-group model.
ARIA roles
radiogroup + radio with aria-checked and per-face labels.
Submit gating
The button stays disabled until a face is chosen.
No library
Pure HTML/CSS/JS/SVG — no emoji assets or rating widget.

About this UI Snippet

Mood Picker — SVG Smiley-Face Satisfaction Rating with Hover Preview

Screenshot of the Mood Picker snippet rendered live

A mood picker asks "how was it?" with a row of faces from frowning to grinning — a friendlier, more immediate rating than stars for satisfaction surveys, support closeouts, and check-ins. This snippet builds one with crisp inline-SVG faces, a hover preview, click-to-commit, and keyboard support, in plain HTML, CSS, and vanilla JavaScript with no emoji fonts or images.

Faces drawn in SVG, not emoji

Each face is an inline <svg> with two eyes and a single <path> mouth. The mouth's curve encodes the mood: a downward Q quadratic curve for sad, a flat L line for neutral, and an upward curve for happy. Drawing them in SVG instead of using emoji characters means they render identically on every OS (no "emoji looks different on Windows vs Mac" problem), scale crisply at any size, and recolour with CSS — the selected face turns warm amber via a class, with no extra assets.

Preview vs commit

Like a good rating control, hovering or focusing a face previews that mood — lifting it, colouring it, and showing its label ("Okay", "Great") — without changing the saved value. Clicking commits: it stores the score, enables the submit button, and becomes the state the row returns to when the pointer leaves. This separation lets users scan the options without accidentally locking in a choice.

Accessible by construction

The row is a role="radiogroup" and each face is a <button role="radio"> with an aria-label ("Good") and aria-checked tracking the committed value. Arrow keys move between faces and set the mood, matching the radio-group keyboard model, and a focus ring keeps keyboard users oriented. Screen-reader users hear a clear label for each option rather than an ambiguous picture.

Form-ready feedback loop

The submit button stays disabled until a face is chosen, preventing empty responses, and the label doubles as live confirmation ("Thanks for the feedback!"). The committed value is a number 1–5 you can post to your backend or map to an NPS-style bucket. Because the logic is tiny and the faces are pure markup, you can add a comment box, change the count to three faces, or swap the palette in minutes.

Tactile motion

Faces lift and scale on hover with a short transition, giving the control a playful, responsive feel that suits the friendly tone of a mood survey. Everything is a few dozen lines of CSS — a clean, dependency-free reference for emoji-style satisfaction pickers.

Step by step

How to Use

  1. 1
    Paste HTML, CSS, and JSA row of five SVG faces renders with a disabled submit button.
  2. 2
    Hover a faceIt lifts and the label shows its mood, without committing.
  3. 3
    Click to chooseClicking sets the mood, enables submit, and highlights the face.
  4. 4
    Use the keyboardTab to the row and use Left/Right arrows to move and select.
  5. 5
    Read the valueThe committed value is 1–5 (data-value); post or map it as you like.
  6. 6
    CustomiseChange to three faces, recolour, or add a comment field.

Real-world uses

Common Use Cases

Satisfaction surveys
Capture a quick mood before a written comment, using inline SVG faces whose mouths curve from a frown to a grin.
Support ticket closeout
Rate a resolved issue in a comment thread, where hovering previews a mood without changing the saved value.
Post-session check-ins
Ask how a call or lesson went with a review form for details, and a click committing the score.
Feedback widgets
Drop into a feedback tab widget for one-click sentiment, avoiding emoji font inconsistencies across platforms and devices.
Daily mood journaling
Log a daily mood next to a streak tracker, and learn SVG with radio-group ARIA roles for accessible faces.
Related: Shortcut Recorder
See the Shortcut Recorder for a related forms pattern worth pairing with this one.

Got questions?

Frequently Asked Questions

Emoji render differently on every platform — the same character looks distinct on Windows, macOS, Android, and iOS, so your survey loses visual consistency. Inline SVG faces look identical everywhere, scale crisply to any size, and recolour with CSS (the selected face turns amber), all without bundling image files. You also get full control over the exact expression via the mouth path.

Each face has a data-value from 1 (awful) to 5 (great), and the committed choice is held in a current variable. Read it on submit to post a number to your backend, or map it to buckets — for example treat 4 and 5 as promoters. The control also exposes a readable label per face for confirmations.

Yes. The row is a radiogroup, each face is a button with role="radio", an aria-label, and aria-checked, and arrow keys move and set the mood like a native radio group. A focus-visible ring shows the active face, and screen readers announce a clear word ("Good") instead of an image, so it is usable without sight or a mouse.

Yes — remove two of the button blocks and keep data-value sequential (1, 2, 3). The script reads whatever faces are present and the arrow-key navigation adapts automatically, so no JavaScript changes are needed. Three faces (sad/neutral/happy) is common for lightweight one-tap sentiment.

Keep the SVG markup and move the state into your component: track committed and hovered values, render the highlight class from them, and bind mouseenter/focus/click/keydown handlers. Replace the submit gating with a disabled prop driven by state, and post the numeric value in your submit handler. Tailwind users swap the classes for utilities and toggle the amber fill with a conditional class.