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.

Privacy consent — collapsed detail

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 detail

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>

Required — error state

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>

Two stacked blocks

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.