v1.6.x-dev NDS IQ v6

User Feedback - National Design System

A stepped satisfaction survey that captures Yes or No responses, follows up with contextual detail questions based on the answer, and remembers submissions per page so returning visitors see confirmation instead of being asked again.

Feedback Survey

The widget starts with a Yes/No question. Selecting an answer reveals a tailored follow-up form specific to that choice, then submission replaces the form with a confirmation message.

Page Satisfaction Survey
Was this page useful?
60% of users said Yes from 2843 Feedbacks
<section id="user-feedback" class="nds-user-feedback-section nds-content-section"> <div class="nds-section-body"> <form class="nds-form" action="/submit" method="POST"> <div class="nds-user-feedback"> <!-- Step 1: Question --> <div class="nds-user-feedback-overview"> <span class="nds-user-feedback-question">Was this page useful?</span> <span class="nds-user-feedback-status" hidden></span> <div class="nds-user-feedback-answer-btn"> <button type="button" class="nds-btn nds-primary nds-md" data-answer="Yes" aria-label="Yes"> <span class="nds-label">Yes</span> </button> <button type="button" class="nds-btn nds-primary nds-md" data-answer="No" aria-label="No"> <span class="nds-label">No</span> </button> </div> <span class="nds-user-feedback-statistic">60% of users said Yes from 2843 Feedbacks</span> <button type="button" class="nds-user-feedback-close nds-btn nds-secondary-outline nds-md" aria-label="Close feedback" hidden> <span class="nds-label">Close</span> <i class="nds-icon nds-hgi-cancel-01" aria-hidden="true"></i> </button> </div> <!-- Step 2: Follow-up details (JS shows/hides) --> <div class="nds-user-feedback-details" hidden> <div class="nds-user-feedback-options"> <!-- Shown when data-answer="yes" --> <fieldset class="nds-form-group nds-check-group nds-why-yes" data-min-checked="2"> <legend> <span class="nds-label">Please tell us why</span> <span class="nds-note">(you can select multiple options)</span> </legend> <div class="nds-form-container nds-check-container"> <div class="nds-form-header"> <label for="feedback-yes-relevant"><span class="nds-label">Content is relevant</span></label> </div> <div class="nds-form-control"> <input type="checkbox" id="feedback-yes-relevant" name="why-yes" value="relevant" class="nds-check"> </div> </div> <div class="nds-form-container nds-check-container"> <div class="nds-form-header"> <label for="feedback-yes-written"><span class="nds-label">It was well written</span></label> </div> <div class="nds-form-control"> <input type="checkbox" id="feedback-yes-written" name="why-yes" value="well-written" class="nds-check"> </div> </div> <div class="nds-form-container nds-check-container"> <div class="nds-form-header"> <label for="feedback-yes-layout"><span class="nds-label">The layout made it easy to read</span></label> </div> <div class="nds-form-control"> <input type="checkbox" id="feedback-yes-layout" name="why-yes" value="easy-layout" class="nds-check"> </div> </div> <div class="nds-form-container nds-check-container"> <div class="nds-form-header"> <label for="feedback-yes-other"><span class="nds-label">Something else</span></label> </div> <div class="nds-form-control"> <input type="checkbox" id="feedback-yes-other" name="why-yes" value="other" class="nds-check"> </div> </div> <div class="nds-form-footer" data-feedback-target hidden></div> </fieldset> <!-- Shown when data-answer="no" --> <fieldset class="nds-form-group nds-check-group nds-why-no" data-required> <legend> <span class="nds-label">Please tell us why</span> <span class="nds-note">(you can select multiple options)</span> </legend> <div class="nds-form-container nds-check-container"> <div class="nds-form-header"> <label for="feedback-no-irrelevant"><span class="nds-label">Content is not relevant</span></label> </div> <div class="nds-form-control"> <input type="checkbox" id="feedback-no-irrelevant" name="why-no" value="not-relevant" class="nds-check"> </div> </div> <div class="nds-form-container nds-check-container"> <div class="nds-form-header"> <label for="feedback-no-inaccurate"><span class="nds-label">Content is not accurate</span></label> </div> <div class="nds-form-control"> <input type="checkbox" id="feedback-no-inaccurate" name="why-no" value="not-accurate" class="nds-check"> </div> </div> <div class="nds-form-container nds-check-container"> <div class="nds-form-header"> <label for="feedback-no-long"><span class="nds-label">Content is too long</span></label> </div> <div class="nds-form-control"> <input type="checkbox" id="feedback-no-long" name="why-no" value="too-long" class="nds-check"> </div> </div> <div class="nds-form-container nds-check-container"> <div class="nds-form-header"> <label for="feedback-no-other"><span class="nds-label">Something else</span></label> </div> <div class="nds-form-control"> <input type="checkbox" id="feedback-no-other" name="why-no" value="other" class="nds-check"> </div> </div> <div class="nds-form-footer" data-feedback-target hidden></div> </fieldset> <!-- Gender (always shown in details step) --> <fieldset class="nds-form-group nds-radio-group nds-rowView nds-gender" data-required> <legend class="nds-label">I'm</legend> <div class="nds-form-container nds-radio-container"> <div class="nds-form-header"> <label for="feedback-gender-male"><span class="nds-label">Male</span></label> </div> <div class="nds-form-control"> <input type="radio" id="feedback-gender-male" name="gender" value="male" class="nds-radio"> </div> </div> <div class="nds-form-container nds-radio-container"> <div class="nds-form-header"> <label for="feedback-gender-female"><span class="nds-label">Female</span></label> </div> <div class="nds-form-control"> <input type="radio" id="feedback-gender-female" name="gender" value="female" class="nds-radio"> </div> </div> <div class="nds-form-footer" data-feedback-target hidden></div> </fieldset> </div> <!-- Free-text comment --> <div class="nds-user-feedback-comment"> <div class="nds-form-container nds-textarea"> <div class="nds-form-header"> <label for="feedback-comment"><span class="nds-label">Feedback</span></label> </div> <div class="nds-form-control"> <textarea id="feedback-comment" name="comment" class="nds-textarea" placeholder="Enter your message..." rows="4"></textarea> </div> <div class="nds-form-footer" data-feedback-target hidden></div> </div> </div> </div> <!-- Step 3: Submit row (JS shows/hides) --> <div class="nds-user-feedback-submit" hidden> <span class="nds-user-feedback-agreement"> For more information you may review <a href="#">e-participation statement</a> and <a href="#">rules of engagement.</a> </span> <button class="nds-user-feedback-submit-btn nds-btn nds-primary" data-answer="submit" aria-label="Submit"> <span class="nds-label">Submit</span> </button> </div> </div> </form> </div> </section>

Built-in Features

Auto-initialization

Activates on every .nds-user-feedback element on the page. All click handlers and state transitions wire up without any JavaScript calls.

Guided Answer Flow

Clicking Yes or No reveals a tailored follow-up form for that answer, hiding the other branch. Closing resets the entire widget back to the initial question state.

Submission Memory

Saves a cookie keyed to the current page path for 365 days. Returning visitors see the confirmation message instead of being presented the survey again.

Bilingual Feedback Messages

Reads the page language from the lang attribute and delivers success and error messages in Arabic or English automatically.

Form Validation

Uses data-required and data-min-checked on fieldsets to validate before submission. Inline error messages surface directly below the failing field.

Page Section Wrapper

The nds-user-feedback-section class on the enclosing section applies a primary-color top border and neutral background for standard bottom-of-page placement.

Usage Guidelines

Best Practices

  • Copy the markup from the HTML tab above and place it inside a <form> near the bottom of the page, after the main content, so it reads as a closing prompt
  • Leave the widget off transactional pages such as checkout flows, multi-step forms, or confirmation screens, where user attention should stay on the task
  • Tailor the .nds-why-yes and .nds-why-no checkbox options in the include to match the type of content on the page
  • Keep both checkbox lists to four to six options. Long lists reduce completion rates and produce noisier data
  • Do not use this as the only feedback channel for critical issues. The widget collects qualitative signals, not bug reports. Pair it with a link to a dedicated support form
  • Connect your data collection endpoint via the form's action attribute or by intercepting the submit event before the component's handler runs, since the component itself does not make a network request
  • Use data-success-message and data-error-message on the .nds-user-feedback element only when the built-in bilingual defaults do not match your page's tone
  • The .nds-user-feedback-statistic span is optional. Remove it if you do not have real satisfaction data to display

Data Attributes

AttributeElementDescription
data-answer="Yes|No" .nds-btn (answer buttons) Tells the JS which follow-up branch to reveal. Must be exactly Yes or No (capitalized).
data-state="details|status" .nds-user-feedback (JS-stamped) Set at runtime by the component. details shows the follow-up form and hides the statistic; status shows the confirmation message and hides the question and answer buttons. Absent in the initial question state.
data-answer="yes|no" .nds-user-feedback (JS-stamped) Set at runtime on the root element (lowercase) when the user selects an answer. Controls which follow-up branch is visible: yes shows .nds-why-yes, no shows .nds-why-no. Removed on reset.
data-required fieldset Marks a checkbox or radio group as required. At least one option must be selected before submission proceeds.
data-min-checked="N" fieldset Requires at least N checkboxes to be selected. Used on the yes branch to encourage multiple selections.
data-feedback-target .nds-form-footer Marks the element where NDS.Forms injects inline validation error messages for that field group.
data-success-message .nds-user-feedback Overrides the default success message. Defaults to "Your feedback is submitted!" (English) or "تم استلام ملاحظتك!" (Arabic).
data-error-message .nds-user-feedback Overrides the default error message. Defaults to "An error occurred, please try again" (English) or "حدث خطأ، يرجى المحاولة مرة أخرى" (Arabic).
data-no-persist .nds-user-feedback Disables cookie persistence: the widget never restores a saved submission and never writes one. Useful for demos, previews, or pages that should ask on every visit.

JavaScript API

The component auto-initializes on page load. The methods below cover re-initializing or wiring up a widget added to the DOM after load.

// Initialize every .nds-user-feedback on the page. Idempotent: already // initialized widgets are skipped, so it is safe to call repeatedly. NDS.UserFeedback.init(); // Alias of init(), provided for parity with other NDS components. NDS.UserFeedback.reinit(); // Initialize a single widget element (also skips if already initialized). NDS.UserFeedback.create(document.querySelector('.nds-user-feedback')); // Dependencies, all in the standard NDS bundle and auto-initialized: // - NDS.Feedback: renders the inline success/error confirmation message // - NDS.Cookies: persists submission state per page path for 365 days // - NDS.Forms: validates required fieldsets before submission
Last Modified Date: 28/06/2026 - 01:27 PM