Sentiment
Captures a qualitative user response using icons or scale indicators such as star ratings or emoji reactions. Use in feedback or survey flows where a structured emotional or satisfaction response is needed.
FigmaDescription
Presents a set of selectable icons or scale indicators — such as star ratings, numeric scales, or emoji-style reactions — that let a user express a qualitative opinion or satisfaction level in a single tap or click.
End-of-survey summary screens, inline feedback prompts within consultation responses, post-submission confirmation pages, and stakeholder check-in flows.
Government engagement surveys need quick, low-friction ways to capture how participants feel about a consultation or service interaction. A structured sentiment control reduces open-text fatigue while still producing comparable, reportable data.
Anatomy
| Part | Required? | Notes |
|---|---|---|
| Container | Required | Wraps the entire control. Receives the fieldset role and associates the visible label via legend. |
| Legend / Label | Required | Describes the question being answered. Rendered as a |
| Scale item | Required | Each selectable unit — a star, emoji, or numeric indicator. Rendered as a visually styled radio input. Minimum one item; typical ranges are 3, 5, or 10 items. |
| Icon or glyph | Required | The visual representation of the scale item (star, face, number). Decorative when the radio label already conveys meaning; otherwise carries an aria-label. |
| Selected indicator | Required | Visual treatment applied to the chosen item and all lower-ranked items in cumulative scales (e.g. stars 1-3 fill when 3 is selected). Uses --op-color-interactive-default for fill. |
| Helper text | Optional | Supplementary guidance below the control, e.g. '1 = Very dissatisfied, 5 = Very satisfied'. Uses --op-text-sm and --op-color-text-secondary. |
| Error message | Recommended | Inline validation message displayed when the field is required but unanswered. Rendered with --op-color-status-error and the status-error icon. |
Variants
Star rating
Cumulative satisfaction scale. Selected stars plus all preceding stars are filled. Commonly 5-point.
Satisfaction surveys, quality ratings, and any scenario where a ranked numeric scale is familiar to the audience.
Emoji reaction
Discrete emotional response. Each emoji represents a distinct sentiment band (e.g. very dissatisfied, dissatisfied, neutral, satisfied, very satisfied). Items are mutually exclusive — no cumulative fill.
Quick pulse checks in public-facing consultation flows where icon literacy outperforms numeric labels for diverse or lower-literacy audiences.
Numeric scale
Labelled integer range, typically 1-10 (NPS-style) or 1-5. Each number is a discrete radio button laid out horizontally.
Net Promoter Score surveys, standardised government satisfaction benchmarks, or when downstream analytics require a continuous numeric variable.
Thumbs (binary)
Two-option positive/negative response. No gradient — outcome is strictly helpful or not helpful.
Content usefulness prompts ("Was this page helpful?"), quick-win feedback on knowledge base articles or FAQ responses.
States
| State | Behaviour |
|---|---|
| Default (unselected) | All scale items render in their unfilled/outline state. No item carries aria-checked=true. The first item is programmatically focusable via tabindex=0; subsequent items are reachable with arrow keys. |
| Hovered | The hovered item and, for cumulative variants (star), all preceding items preview the selected fill using --op-color-interactive-hover at reduced opacity. Cursor changes to pointer. |
| Focused | The focused item receives a 3px focus ring using --op-color-interactive-focus. Visible regardless of input method. Hover preview does not activate on keyboard focus alone. |
| Selected | The chosen item is filled with --op-color-interactive-default. For cumulative variants, all items at or below the selected value are also filled. aria-checked=true is set on the active radio. |
| Disabled | All items render at 40% opacity. Pointer events are suppressed. aria-disabled=true is set on each input and the container fieldset. Previously selected value is preserved visually. |
| Read-only | Renders the current value in its selected state without interactive affordances. Used in review/confirmation screens. No focus ring or hover states. aria-readonly=true on the group. |
| Error | The container receives --op-color-status-error border. The error message is shown below the helper text. aria-describedby on the fieldset points to the error message id. |
Usage guidelines
When to use
- Collecting satisfaction or sentiment data at the end of a consultation or survey flow.
- Gathering a quick reaction to a specific piece of content, such as a policy document or FAQ answer.
- Benchmarking stakeholder or community sentiment across multiple consultation rounds for comparison.
- Supplementing open-text feedback fields with a structured numeric or emotional signal.
When not to use
- When the question requires a nuanced or multi-dimensional response — use a Likert matrix or open textarea instead.
- When sentiment data is informational only and the user cannot change it — use a read-only progress or badge indicator.
- As a primary navigation or filtering control — use a select or toggle group instead.
- When fewer than two response options exist — a single checkbox or yes/no radio pair is clearer.
Do / Don't
Do
Always pair the sentiment control with a visible legend that clearly states what is being rated.
Don't
Do not rely on surrounding page context alone to explain the scale; screen readers read the fieldset legend in isolation.
Do
Label the ends of numeric and star scales (e.g. 'Not at all likely' / 'Extremely likely') so meaning is unambiguous.
Don't
Do not leave a 1-10 scale unlabelled — numbers carry different cultural assumptions in diverse government audiences.
Do
Mark the field as required in the form validation schema and display an error message when it is skipped.
Don't
Do not silently treat an unanswered sentiment field as a neutral midpoint in your data pipeline.
Do
Use the emoji variant for public-facing Social Point surveys targeting mixed-literacy audiences.
Don't
Do not use emoji in formal stakeholder reporting interfaces (Open Point SRM) where numeric precision is expected.
Layout & Spacing
Container: display flex, gap --op-space-8 (8px) between scale items. Legend: margin-bottom --op-space-8 (8px), font --op-text-sm, font-weight 600, color --op-color-text-primary. Scale item touch target: minimum 44x44px, achieved via padding --op-space-8 on each side. Icon/glyph size: 24x24px default; 20x20px in compact/inline contexts. Helper text: margin-top --op-space-4 (4px), font --op-text-xs, color --op-color-text-secondary. Error message: margin-top --op-space-4 (4px), font --op-text-xs, color --op-color-status-error. Gap between helper text and error message: --op-space-4 (4px).
Tokens
| Part | Token | Value |
|---|---|---|
| Selected icon fill | --op-color-interactive-default | Green-400 — used for filled stars, active emoji outline, and selected numeric button background. |
| Unselected icon | --op-color-border-default | Used for unfilled star outlines and inactive numeric button borders. |
| Hover preview fill | --op-color-interactive-hover | Applied at 60% opacity on hovered item and preceding items in cumulative variants. |
| Focus ring | --op-color-interactive-focus | 3px solid outline, 2px offset, applied to the focused scale item. |
| Legend text | --op-color-text-primary | Primary text colour for the question label. |
| Helper text | --op-color-text-secondary | Secondary text colour for scale endpoint labels and supplementary guidance. |
| Error message | --op-color-status-error | Applied to both the error icon and error message text. |
| Error container border | --op-color-status-error | 1px solid border on the fieldset container when in error state. |
| Disabled state | --op-color-text-primary | Rendered at 40% opacity via opacity property; do not substitute a lighter token. |
| Container background | --op-color-bg-primary | Transparent by default; inherits page background. |
Engineering notes
- Render the entire control as a
- Each scale item is a visually hidden with a styled
- For star and numeric variants, implement rover tabindex: only the selected item (or first item if none selected) holds tabindex=0. Arrow keys move focus and update selection; Tab exits the group.
- Emit a custom change event with { name, value } payload so parent form libraries (e.g. React Hook Form, native FormData) can capture the rating without polling the DOM.
- For the cumulative star variant, set the filled state on all items with index <= selectedIndex in the change handler. Do not rely on CSS sibling selectors for this in environments with Shadow DOM.
- Persist the selected value to the form's data model on change, not on blur, so partially completed surveys can be saved as drafts.
- Avoid dynamically inserting or removing scale items after initial render — screen readers may lose track of the group structure. If scale size must change, remount the entire fieldset.
Keyboard interaction
| Key | Action |
|---|---|
| Tab | Moves focus into the sentiment group (onto the selected item, or the first item if none selected). A second Tab exits the group to the next focusable element. |
| Arrow Right / Arrow Down | Moves focus to the next scale item and selects it. Wraps from last item to first. |
| Arrow Left / Arrow Up | Moves focus to the previous scale item and selects it. Wraps from first item to last. |
| Enter / Space | Confirms selection of the currently focused item (equivalent to arrow-key selection for radio groups; included for user expectation). |
Why it matters
Government surveys are frequently completed by people using assistive technologies, including older residents, people with disabilities, and public servants using managed IT environments with keyboard-only navigation. A properly constructed radio group ensures every user can submit a sentiment response without a mouse.
Focus
Only one item in the group is in the tab sequence at a time (rover tabindex pattern). The focused item displays a 3px focus ring using --op-color-interactive-focus with a 2px transparent offset to ensure visibility on both light and dark backgrounds. Focus does not move automatically on page load; it enters the group only when the user tabs to it. After form submission or error correction, focus is programmatically set to the first error field, which may be this component if it was left unanswered.
ARIA
| Role or attribute | When to use | Example |
|---|---|---|
role="radiogroup" (on fieldset, via implicit ARIA) | Applied implicitly by the | <fieldset role="radiogroup" aria-labelledby="sentiment-legend" aria-required="true"> |
aria-required | Set to true on the fieldset when the field is mandatory. Communicated to screen readers before the user enters the group. | <fieldset aria-required="true"> |
aria-describedby | Points to the id of the helper text element and/or the error message element so both are read after the legend. | <fieldset aria-describedby="sentiment-helper sentiment-error"> |
aria-checked | Set to true on the currently selected radio input. Managed automatically by the browser for native ; set explicitly for custom elements. | <input type="radio" aria-checked="true" /> |
aria-disabled | Set to true on each radio input and the fieldset when the control is disabled. | <fieldset aria-disabled="true"> |
aria-invalid | Set to true on the fieldset when validation fails and the error message is shown. | <fieldset aria-invalid="true" aria-describedby="sentiment-error"> |
Contrast
Filled star / selected emoji icon: --op-color-interactive-default (green-400) against --op-color-bg-primary must meet 3:1 non-text contrast ratio (WCAG 1.4.11). Legend text: --op-color-text-primary against --op-color-bg-primary meets 4.5:1 for normal text at --op-text-sm. Helper text: --op-color-text-secondary against --op-color-bg-primary must be verified at --op-text-xs; if it falls below 4.5:1, increase font size to --op-text-sm or use --op-color-text-primary. Error text: --op-color-status-error against --op-color-bg-primary must meet 4.5:1. Do not use --op-color-status-error-bg as the sole error indicator — always include the text message. Focus ring: --op-color-interactive-focus at 3px provides sufficient contrast against both light and dark backgrounds by design.
Touch targets
Each scale item must have a minimum 44x44px tap target. For a 5-star control this means the total control width is at least 220px on mobile viewports. Use padding on the label rather than increasing icon size to maintain visual proportion. On narrow viewports (<375px), consider switching from a horizontal row to a 2-column grid layout.
Things to avoid
- Do not convey sentiment meaning through colour alone (e.g. red-to-green gradient without numeric or text labels). Users who are colour blind cannot distinguish the scale.
- Do not auto-advance to the next survey question when a sentiment item is selected — unexpected navigation confuses screen reader users and breaks the expected radio group interaction model.
- Do not use animated transitions on the fill state without honouring prefers-reduced-motion: reduce. Rapid fill animations can trigger discomfort for users with vestibular disorders.
- Do not remove the visible focus ring for aesthetic reasons. Government accessibility obligations require always-visible focus indicators.
- Do not stack multiple sentiment controls inside a single fieldset — each question must have its own fieldset/legend pair.