shadcn.io is not affiliated with official shadcn/ui
Terms Consent Login Block for React
A full-height agreements screen for a tenant app: a select-all checkbox that turns mixed over the two required agreements, an optional product-updates box that is never pre-ticked, and a Continue button that names what is missing instead of being disabled.
Installation
Overview
Ask people to accept your terms without a dead button, with this consent login block for React with Base UI. It's the step after sign-in of Leasebird, a tenant app: the page's side rails run its full height, and one band crosses the screen between two rules with a plus mark where each meets a rail. The product's mark sits over the heading and the signed-in address, then one bordered group of hairline cells holds the agreements. The first cell is a select-all Checkbox that is ticked when both required agreements are, mixed when one is, and clear when neither is, with its count beside it ("1 of 2 required accepted"). Under it are the Terms of use and the Privacy notice, each with what it covers, its version and its day, and a link to read it, then product updates, which are optional, never pre-ticked and not part of select-all. The Shine Button continues and is never disabled: pressed with a required box unticked it marks that box, names what is missing under the group and moves focus to the first one. A good press shows a spinner for a moment, then puts what was accepted in the form's own space and moves focus to it, with a way to start over. It's built from Checkbox, Button, Shine Button and Guides, with CSS entrances only. For any app that needs a recorded acceptance at the door.
FAQ
What is recorded when I press Continue, and how do I wire it for real?
Nothing. Continue waits under a spinner for less than a second, then shows which versions were accepted, the fixed day the demo uses and whether product updates were chosen, and a polite status message says nothing was recorded. Start over clears every box. For real, post the ticked agreements from the submit handler where the code says so: the user, each agreement's id and version, the time and where it was accepted, stored where you can show it later, and only then open the app. Keep the busy state until the server answers and show its error under the group if it refuses.
Why is Continue never disabled?
A disabled button can't say why, and keyboard users can't reach it to find out. Here it stays enabled: pressed with the Terms or the Privacy notice unticked, it marks those cells, writes one sentence under the group that names them and moves focus to the first, so a screen reader hears what to do. Ticking a box clears its mark as you go, and the line under the group always has its space, so nothing moves.
How does the select-all box work, and why doesn't it include product updates?
It reads the two required boxes: clear when neither is ticked, mixed when one is, ticked when both are, and pressing it ticks or clears both. Product updates stay outside it because one box should not buy consent to two different things, and an optional agreement should be a choice made on its own: it is never pre-ticked and never required. The mixed state is the indeterminate prop beside checked.
Why do the agreements show a version and a day?
Because what a person accepted is a particular text. The version and its effective day are typed in AGREEMENTS, so when you publish version 8 you change two values and everyone who accepted version 7 is asked again. Keep the number you show in the same record you store, so you can prove which words were accepted.
Is it usable with a keyboard and a screen reader?
Yes. Tab runs through select-all, each agreement's box, the Read links, Continue and the quiet link. A press on any part of a cell ticks its box, and Space does the same from the keyboard. Each box is named by its title and described by what it covers and its version, and a marked one is also described by the group's problem line. After Continue, focus moves to the result's heading and a polite status message says what was accepted, so the outcome is heard.
Related
- Pro
Privacy Notice Login
Split with a panelA split sign-in page for a patient portal: a Why we ask button under each field opens one sentence on why it is needed, a data list says what the portal keeps, and a required privacy box names itself when it is left unticked.
- Pro
Cookie Consent Login
Sign-up and consentA bakery subscription's cookie choices shown open over a drawn sign-in page: Essential locked on, Analytics and Marketing switches that start off, and three equal buttons, Reject all, Accept all and Save choices.
- @Pro
Create Account Handle Check Login
Sign-up and consentA sign-up page for a community forum in two columns of fields: a handle with an at sign that checks itself against a typed taken list and offers three tries as buttons, then the email, a password with an eight-character hint and Create account.
- Pro
Centered Sign In Login
Centered formA full-height sign-in page for a team chat app: the product's mark, Google and GitHub rows with lettermark tiles, an or rule, email and password checked on submit, and one Sign in button with a simulated wait and its result.
- Pro
OAuth Consent Login
Sign-up and consentA consent screen for a calendar when a scheduling app asks for access: two required permissions shown locked, two optional ones you can untick, a button that counts what it grants and a scope details disclosure.
- Pro
Account Locked Login
Recovery and resetA locked-account page for a point-of-sale system that draws the lockout ladder, marks the rung the account is on with a fixed unlock time, and offers an unlock link by email or a password reset.