Single-Page Checkout
Contact, shipping and payment in one scrolling form with a single place-order button.
3 frameworksIntermediate
A "this is a gift" toggle that reveals gift wrap and a message field with a CSS-only peer.
<!--
"This is a gift" reveals the gift details with a CSS-only peer toggle - no JS.
The checkbox is the peer; the details panel is peer-checked:block hidden, so it
is genuinely absent from the layout (and from tab order) until the box is
ticked. The message textarea has a maxlength so the note can't overflow the card
it prints on.
-->
<form class="w-full max-w-md space-y-4 rounded-2xl border border-gray-200 bg-white p-5 sm:p-6 dark:border-gray-800 dark:bg-gray-950" action="#" method="post" novalidate>
<h2 class="text-sm font-semibold text-gray-900 dark:text-gray-100">Gift options</h2>
<label class="flex items-start gap-2.5 text-sm text-gray-700 dark:text-gray-300">
<input class="peer mt-0.5 h-4 w-4 rounded border-gray-300 text-blue-600 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-blue-600 focus-visible:ring-offset-2 dark:border-gray-600 dark:focus-visible:ring-blue-400 dark:focus-visible:ring-offset-gray-950" type="checkbox" name="is-gift" />
<span>This is a gift</span>
</label>
<div class="hidden space-y-4 border-t border-gray-200 pt-4 peer-has-[:checked]:block dark:border-gray-800">
<label class="flex items-center justify-between gap-3 rounded-lg border border-gray-200 p-3 text-sm dark:border-gray-800">
<span class="flex items-center gap-2.5 text-gray-700 dark:text-gray-300">
<input class="h-4 w-4 rounded border-gray-300 text-blue-600 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-blue-600 focus-visible:ring-offset-2 dark:border-gray-600 dark:focus-visible:ring-blue-400 dark:focus-visible:ring-offset-gray-950" type="checkbox" name="gift-wrap" />
Add gift wrap
</span>
<span class="shrink-0 tabular-nums text-gray-500 dark:text-gray-400">$4.99</span>
</label>
<div>
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300" for="gift-msg">Gift message</label>
<textarea class="mt-1 block w-full resize-y rounded-lg border border-gray-300 bg-white px-3 py-2.5 text-sm text-gray-900 placeholder:text-gray-400 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-blue-600 focus-visible:ring-offset-2 focus-visible:ring-offset-white dark:border-gray-700 dark:bg-gray-900 dark:text-gray-100 dark:placeholder:text-gray-500 dark:focus-visible:ring-blue-400 dark:focus-visible:ring-offset-gray-950" id="gift-msg" name="gift-message" rows="3" maxlength="200" placeholder="Happy birthday! Hope you love it."></textarea>
<p class="mt-1 text-xs text-gray-400 dark:text-gray-500">Up to 200 characters, printed on the packing slip.</p>
</div>
</div>
<button class="inline-flex w-full items-center justify-center rounded-lg bg-blue-600 px-5 py-3 text-sm font-semibold text-white transition-colors hover:bg-blue-700 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-blue-600 focus-visible:ring-offset-2 focus-visible:ring-offset-white motion-reduce:transition-none dark:focus-visible:ring-blue-400 dark:focus-visible:ring-offset-gray-950" type="submit">Save gift options</button>
</form>| Prop | Type | Default | Description |
|---|---|---|---|
wrapPrice | string | '$4.99' | Wrap price |
onSubmit | (e: FormEvent) => void | - | Called with the form's values when it is submitted. |
className | string | - | Additional classes merged onto the root element. |
Ticking "This is a gift" reveals the details with a `peer-has-[:checked]:block` toggle - no JavaScript, and the panel is truly absent from layout and tab order until checked. The message `<textarea>` has a `maxLength` so the note cannot overflow the packing slip. Set the wrap price with `wrapPrice`.