Animated feedback form with star rating and message fields
Blog

Create an Animated Feedback Form with HTML, CSS and JavaScript

Build a compact feedback form that opens with a smooth animation, collects a name, email address, star rating, and message, then shows a confirmation. This example uses semantic HTML, scoped CSS, and vanilla JavaScript, so it does not need jQuery or a framework.

Quick answer: Use a real button to open the form, native required fields for basic validation, and a fieldset of radio buttons for an accessible star rating. Animate the panel with CSS and respect the visitor’s reduced-motion preference.

Live DemoDownload Source Code

What you will build

The demo below starts with a feedback button. Activating it reveals a small form with labels, five keyboard-selectable stars, and a confirmation message. It runs entirely in the browser: submitting the sample does not email, save, or transmit the entered feedback. Connect a server endpoint or form service before using it to collect real responses.

How the animated feedback form works

The interface has two states: collapsed and open. JavaScript toggles a class and moves keyboard focus into the form. CSS transitions the panel’s opacity and position. On submit, the browser checks required fields and the selected rating, then presents an aria-live confirmation.

Implementation notes

  • HTML: Use labelled inputs, an email input, a textarea, a fieldset, and actual buttons.
  • CSS: Scope styles to the demo wrapper so they do not alter the WordPress theme.
  • JavaScript: Use event listeners, native form validation, and textContent for status updates.

Accessibility and form security

All controls work with a keyboard, the rating uses radio buttons, focus moves into the panel when it opens and returns to the launch button when it closes, and the status message is announced politely. The included demo is client-side only. Do not ask visitors for sensitive information, and never treat browser-only validation as a substitute for server-side validation and spam protection.

Common issues

  • The form does not open: Confirm the HTML widget includes the script and check that the button and panel IDs match.
  • Styles affect other page elements: Keep selectors under .swc-feedback-demo.
  • Submit does nothing: Select a rating and complete the required fields; use the ZIP to compare a working standalone example.

Frequently asked questions

Does this sample send feedback to an email address?

No. It validates the fields in the browser and displays a local confirmation only. Add a secure server-side endpoint or trusted form service to receive responses.

Can I change the animation?

Yes. Adjust the wrapper’s transition duration and transform in the CSS. The reduced-motion rule disables the transition for visitors who request less motion.

Can I use the form without jQuery?

Yes. The example uses modern vanilla JavaScript and has no library dependency.

HTML: Accessible feedback form controls
<form class="feedback-form">
  <label for="feedback-email">Email</label>
  <input id="feedback-email" name="email" type="email" required>
  <fieldset>
    <legend>Your rating</legend>
    <label><input type="radio" name="rating" value="5" required> 5 stars</label>
  </fieldset>
  <button type="submit">Send feedback</button>
  <p role="status" aria-live="polite"></p>
</form>
CSS: Animated panel and reduced motion
.swc-feedback-demo__panel {
  max-height: 0;
  opacity: 0;
  transform: translateY(-8px);
  transition: opacity 250ms ease, transform 250ms ease;
}

.swc-feedback-demo.is-open .swc-feedback-demo__panel {
  max-height: 900px;
  opacity: 1;
  transform: translateY(0);
}

@media (prefers-reduced-motion: reduce) {
  .swc-feedback-demo * { transition: none !important; }
}
JavaScript: Open and validate the form
const root = document.querySelector('.swc-feedback-demo');
const openButton = root.querySelector('.feedback-launcher');
const form = root.querySelector('.feedback-form');
const status = root.querySelector('[role="status"]');

openButton.addEventListener('click', () => {
  root.classList.add('is-open');
  form.querySelector('input').focus();
});

form.addEventListener('submit', (event) => {
  event.preventDefault();
  if (!form.reportValidity()) return;
  status.textContent = 'Thanks for your feedback.';
});