CONSENT BLOCK: Tinted container for privacy/consent copy + required checkbox
COMPOSES: .collapsible-content (collapsible_controller.js) | .form-check .form-check-lg | .form-label-required | .invalid-feedback idiom
Pairs a short consent summary with an expandable full-policy detail and a required
consent checkbox. Use wherever a parent or applicant must actively agree to a privacy
notice, data-sharing statement, or terms before submitting a form. No new Stimulus
controller — the expand/collapse toggle reuses collapsible_controller.js.
House style: the consent row is
.form-check .form-check-lg .consent-block-check — the 24px check box, not
the 14px default. The DS default is deliberately unchanged; consent is the one place
that opts up. These users are parents and applicants, usually first-time, often on a
phone, making a one-off tick with real consequences, and .form-check-lg is
the only step whose control clears WCAG 2.5.8's 24px target on its own rather than
leaning on the row-spacing exception.
Default state: summary + collapsed detail behind a link-styled toggle. The
detail starts closed (aria-expanded="false") so the block stays short. The
checkbox's aria-describedby points at the summary paragraph.
<div class="consent-block" data-controller="collapsible"><p class="consent-block-summary" id="…-summary">…<button class="consent-block-toggle" data-action="click->collapsible#toggle" aria-expanded="false" aria-controls="…">…</button></p><div class="consent-block-detail collapsible-content" data-collapsible-target="content" id="…"><div class="tw:overflow-hidden">…</div></div><div class="form-check form-check-lg consent-block-check"><input class="form-check-input" type="checkbox" aria-required="true" aria-describedby="…-summary"><label class="form-check-label form-label-required">…</label></div></div>
Expanded state: after the toggle is clicked, .collapsible-content
gains .show, aria-expanded flips to true, and the
chevron rotates 180°. Shown here pre-opened for reference.
<div class="consent-block" data-controller="collapsible" data-collapsible-open-value="true"><p class="consent-block-summary" id="…-summary">…<button class="consent-block-toggle" data-action="click->collapsible#toggle" aria-expanded="true" aria-controls="…">Hide full privacy notice<i class="fa-regular fa-chevron-down tw:rotate-180" data-collapsible-target="icon" aria-hidden="true"></i></button></p><div class="consent-block-detail collapsible-content show" data-collapsible-target="content" id="…"><div class="tw:overflow-hidden">…</div></div><div class="form-check form-check-lg consent-block-check"><input class="form-check-input" type="checkbox" checked aria-describedby="…-summary"><label class="form-check-label">…</label></div></div>
Validation: unchecked required consent on submit. Checkbox gets
.is-invalid + aria-invalid="true", and
.consent-block-error follows the same visual idiom as
.invalid-feedback — added to aria-describedby alongside the summary.
<input class="form-check-input is-invalid" aria-required="true" aria-describedby="…-summary …-msg" aria-invalid="true">…<p class="consent-block-error" id="…-msg">Please agree to the privacy notice to continue with this application.</p>
Stacking: a required privacy consent above an optional marketing
consent — a common pairing at the end of an application form. Adjacent blocks get
tw:mt-4 spacing automatically.
<div class="consent-block" data-controller="collapsible">…</div><div class="consent-block" data-controller="collapsible">…</div> — adjacent .consent-block elements get tw:mt-4 spacing automatically via .consent-block + .consent-block, no wrapper needed.