Theme
On this page (4)
Component Skill — components/feedback-form/component-skill.md

Native basis

A <form> with a <fieldset> of radios (faces, 0–10 or yes / no), optional topic checkboxes as chips and a Textarea. The inline variant shows its thank-you through :has(:checked).

Built from

Textarea, Button.

Web Platform APIs

<fieldset>:has()Constraint validation

Classes

.mk-feedback-form.mk-feedback-form-faces.mk-feedback-form-chips.mk-feedback-form-scale.mk-feedback-form-ends.mk-feedback-form-yesno.mk-feedback-form-thanks

Data attributes

data-variant: nps, inline.

§Default

Five faces (radios named "Very bad" … "Great" - the emoji are never the only name; the choice gets color and a ring), topic chips (checkboxes) and a comment.

§NPS

data-variant='nps': a 0–10 radio scale with the meaning of both ends written out.

§Was this helpful?

data-variant='inline': Yes / No as radios - choosing one swaps the question for a thank-you (role="status") through :has(:checked), no script. Click one.

§CSS view file

/* -- Feedback Form (website · Account & States) ---------------------------
   Collects a rating or written feedback about an experience - faces and topics on a card, a 0–10 recommendation scale, or "was this helpful?" inline. */
@layer components {
  /* every box in the block measures border-box - the same on any page */
  :where(.mk-feedback-form, .mk-feedback-form *, .mk-feedback-form ::before, .mk-feedback-form ::after) { box-sizing: border-box; }
  .mk-feedback-form { display: grid; gap: 1.25rem; width: 100%; max-width: 30rem; padding: 1.5rem; border: 1px solid var(--border);
    border-radius: var(--radius-xl);
    background: var(--card);
    color: var(--card-foreground);
    & form { display: grid; gap: 1.25rem; }
    & h2 { margin: 0; font-size: 1.125rem; font-weight: 600; }
    & fieldset { display: grid; gap: 0.625rem; margin: 0; padding: 0; border: 0; min-width: 0; }
    & legend { margin-block-end: 0.625rem; padding: 0; font-size: 0.9375rem; font-weight: 500; }
  }
  .mk-feedback-form-faces { display: flex; justify-content: space-between; gap: 0.5rem;
    & label { position: relative; display: grid; place-items: center; width: 3rem; height: 3rem; border-radius: 50%; background: var(--muted); font-size: 1.5rem; cursor: pointer; filter: grayscale(1); opacity: 0.75; transition: scale 150ms ease, filter 150ms ease, opacity 150ms ease; }
    & input { position: absolute; inset: 0; margin: 0; opacity: 0; cursor: pointer; }
    & label:hover { filter: none; opacity: 1; }
    & label:has(:checked) { filter: none; opacity: 1; scale: 1.15; box-shadow: 0 0 0 2px var(--primary); }
    & label:has(:focus-visible) { outline: 2px solid var(--ring); outline-offset: 3px; }
  }
  .mk-feedback-form-chips { display: flex; flex-wrap: wrap; gap: 0.5rem;
    & label { position: relative; padding: 0.375rem 0.875rem; border: 1px solid var(--border); border-radius: 999px; font-size: 0.875rem; cursor: pointer; }
    & input { position: absolute; inset: 0; margin: 0; opacity: 0; cursor: pointer; }
    & label:has(:checked) { border-color: var(--primary); background: var(--primary); color: var(--primary-foreground); }
    & label:has(:focus-visible) { outline: 2px solid var(--ring); outline-offset: 2px; }
  }
  .mk-feedback-form[data-variant="nps"] { max-width: 40rem; }
  .mk-feedback-form-scale { display: grid; grid-template-columns: repeat(11, minmax(0, 1fr)); gap: 0.25rem;
    & label { position: relative; display: grid; place-items: center; height: 2.5rem; border: 1px solid var(--border); border-radius: var(--radius-md); font-size: 0.875rem; font-weight: 600; font-variant-numeric: tabular-nums; cursor: pointer; }
    & input { position: absolute; inset: 0; margin: 0; opacity: 0; cursor: pointer; }
    & label:hover { background: var(--accent); }
    & label:has(:checked) { border-color: var(--primary); background: var(--primary); color: var(--primary-foreground); }
    & label:has(:focus-visible) { outline: 2px solid var(--ring); outline-offset: 2px; }
  }
  .mk-feedback-form-ends { display: flex; justify-content: space-between; color: var(--muted-foreground); font-size: 0.75rem; }
  .mk-feedback-form[data-variant="inline"] {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.75rem 1rem;
    max-width: none;
    padding: 1rem 1.25rem;
    border-style: dashed;
    background: transparent;
    & fieldset { display: flex; align-items: center; gap: 0.75rem; }
    & legend { float: left; margin: 0; }
    & .mk-feedback-form-yesno { display: flex; gap: 0.5rem; }
    & .mk-feedback-form-yesno label { position: relative; display: inline-flex; align-items: center; gap: 0.375rem; height: 2rem; padding-inline: 0.75rem; border: 1px solid var(--border); border-radius: var(--radius-md); font-size: 0.875rem; font-weight: 500; cursor: pointer; }
    & .mk-feedback-form-yesno input { position: absolute; inset: 0; margin: 0; opacity: 0; cursor: pointer; }
    & .mk-feedback-form-yesno svg { width: 0.875rem; height: 0.875rem; }
    & .mk-feedback-form-yesno label:hover { background: var(--accent); }
    & .mk-feedback-form-yesno label:has(:focus-visible) { outline: 2px solid var(--ring); outline-offset: 2px; }
    & .mk-feedback-form-thanks { display: none; margin: 0; color: #15803d; font-size: 0.875rem; font-weight: 500; }
    &:has(.mk-feedback-form-yesno :checked) fieldset { display: none; }
    &:has(.mk-feedback-form-yesno :checked) .mk-feedback-form-thanks { display: block; }
  }
  /* -- Accessibility ------------------------------------------- */
  @media (prefers-reduced-motion: reduce) {
    .mk-feedback-form-faces label { transition: none; }
  }
  @media (forced-colors: active) {
    .mk-feedback-form-faces label:has(:checked), .mk-feedback-form-scale label:has(:checked), .mk-feedback-form-chips label:has(:checked) { outline: 2px solid Highlight; }
  }
}

Comments, ideas or improvements? Edit this page's source on GitHub