ConsentCheckbox
A required checkbox beside legal text with links to the terms and the privacy policy, posted as a real checkbox.
Demo
Rules not accepted yet.
Get it
It needs theme.qwe, as every component does; or take the whole kit, quillo-ui.qwe. Each component brings its own styles to the pages that show it.
$ curl -O https://quillolang.org/components/ui/theme.qwe$ curl -O https://quillolang.org/components/ui/consent_checkbox.qwemain.qwe
import ui/theme.qwe use theme_cssuse ui/consent_checkbox.qweroute GET / = <html><head><style>{theme_css}</style></head><body class="ui-root">...</body></html>Example
The code of the preview above.
demos/consent_checkbox.qwe
component ConsentCheckboxDemoLive() { state agreed = false <div.demo-stack> <ConsentCheckbox name="rules" field="consent-checkbox-demo-live" required={false} checked={agreed} on_change={(c) => { agreed = c }}> I have read the <a href="https://quillolang.org/docs/" target="_blank" rel="noopener">community rules</a> </ConsentCheckbox> <p.demo-note>{agreed ? "Rules accepted." : "Rules not accepted yet."}</p> </div>}view ConsentCheckboxDemo() = <div.demo-stack> <ConsentCheckbox name="terms" terms_href="https://quillolang.org/terms" privacy_href="https://quillolang.org/privacy" field="consent-checkbox-demo-terms" hint="Both open in a new tab."/> <ConsentCheckbox name="marketing" required={false} field="consent-checkbox-demo-marketing"> Email me about new releases, at most one a month </ConsentCheckbox> <ConsentCheckbox name="age" terms_href="https://quillolang.org/terms" terms_label="age policy" field="consent-checkbox-demo-error" error="You must accept this to sign up."/> <ConsentCheckboxDemoLive/> </div>Source
ui/consent_checkbox.qwe
// ConsentCheckbox: a checkbox beside legal text with links in it ("I agree to the// terms of service and the privacy policy"), required by default. It is a real// checkbox, so a plain <form> sends `name` as `value` ("1") when it is ticked and// the browser refuses to send without it; the links open in a new tab so the// form is not lost.//// <ConsentCheckbox name="terms" terms_href="/terms" privacy_href="/privacy"/>// <ConsentCheckbox name="marketing" required={false} hint="At most one a month.">// Email me about new releases// </ConsentCheckbox>// <ConsentCheckbox name="terms" terms_href="/terms" error="Accept the terms to go on."/>//// children: the text, any HTML, in place of the built-in sentence. Without it the// sentence is "I agree to the <terms> and the <privacy policy>", with a link for// each href given (terms_href, privacy_href; their words: terms_label "terms of// service", privacy_label "privacy policy"), and "I agree to the terms" with none.// checked, value ("1"), required (true), disabled, hint (a quieter line under it),// error (a message under it, in red; it also marks the box aria-invalid), field// (the input's id, default "consent-checkbox-" + name).// on_change(checked): inside one of your components, given true or false.import ui/theme.qwe use ui_clslet consent_checkbox_css = css`.ui-consent-checkbox { display: grid; gap: 4px; font: 14px/1.45 var(--ui-font); color: var(--ui-fg); }.ui-consent-checkbox-label { display: flex; align-items: flex-start; gap: 10px; cursor: pointer; }.ui-consent-checkbox-label input { position: absolute; opacity: 0; width: 1px; height: 1px; margin: 0; }.ui-consent-checkbox-box { flex: none; position: relative; width: 18px; height: 18px; margin-top: 2px; background: var(--ui-surface); border: 1px solid var(--ui-strong); border-radius: 4px; transition: background .15s, border-color .15s, box-shadow .15s;}.ui-consent-checkbox-box::after { content: ""; position: absolute; left: 5px; top: 1.5px; width: 5px; height: 10px; border: solid var(--ui-accent-fg); border-width: 0 2px 2px 0; transform: rotate(45deg) scale(0); transition: transform .12s;}.ui-consent-checkbox-label:hover .ui-consent-checkbox-box { border-color: var(--ui-accent); }.ui-consent-checkbox-label input:checked + .ui-consent-checkbox-box { background: var(--ui-accent); border-color: var(--ui-accent); }.ui-consent-checkbox-label input:checked + .ui-consent-checkbox-box::after { transform: rotate(45deg) scale(1); }.ui-consent-checkbox-label input:focus-visible + .ui-consent-checkbox-box { box-shadow: var(--ui-focus); }.ui-consent-checkbox-text a { color: var(--ui-accent); font-weight: 500; text-decoration: underline; text-underline-offset: 2px; border-radius: 3px; }.ui-consent-checkbox-text a:hover { text-decoration-thickness: 2px; }.ui-consent-checkbox-text a:focus-visible { outline: none; box-shadow: var(--ui-focus); }.ui-consent-checkbox-req { margin-left: 2px; color: var(--ui-danger); }.ui-consent-checkbox-hint, .ui-consent-checkbox-error { margin: 0 0 0 28px; font-size: 13px; color: var(--ui-muted); }.ui-consent-checkbox-error { color: var(--ui-danger); }.ui-consent-checkbox-invalid .ui-consent-checkbox-box { border-color: var(--ui-danger); }.ui-consent-checkbox-disabled { opacity: .55; }.ui-consent-checkbox-disabled .ui-consent-checkbox-label { cursor: not-allowed; }@media (prefers-reduced-motion: reduce) { .ui-consent-checkbox-box, .ui-consent-checkbox-box::after { transition: none; } }`view ConsentCheckbox(name: str, terms_href: str? = null, privacy_href: str? = null, terms_label: str = "terms of service", privacy_label: str = "privacy policy", checked: bool = false, value: str = "1", required: bool = true, disabled: bool = false, hint: str? = null, error: str? = null, field: str? = null, on_change = null, class: str? = null, children = null) { style global consent_checkbox_css let id = field ?? `consent-checkbox-${name}` let described = ui_cls([hint != null && `${id}-hint`, error != null && `${id}-error`]) let link = (href: str?, text: str) => href != null ? <a href={href} target="_blank" rel="noopener">{text}</a> : text // No children is null on newer Quillo and empty HTML on older: both mean the built-in sentence. let text = children != null && str(children) != "" ? children : (privacy_href != null ? ["I agree to the ", link(terms_href, terms_label), " and the ", link(privacy_href, privacy_label)] : ["I agree to the ", link(terms_href, terms_label)]) <div class={ui_cls(["ui-consent-checkbox", error != null && "ui-consent-checkbox-invalid", disabled && "ui-consent-checkbox-disabled", class])}> <label.ui-consent-checkbox-label for={id}> <input type="checkbox" id={id} name={name} value={value} checked={checked} required={required} disabled={disabled} aria-invalid={error != null ? "true" : null} aria-describedby={described != "" ? described : null} @change={(e) => { on_change && on_change(e.checked) }}/> <span.ui-consent-checkbox-box aria-hidden="true"></span> <span.ui-consent-checkbox-text>{text}{required && <span.ui-consent-checkbox-req aria-hidden="true">*</span>}</span> </label> {hint != null && <p.ui-consent-checkbox-hint id={`${id}-hint`}>{hint}</p>} {error != null && <p.ui-consent-checkbox-error id={`${id}-error`} role="alert">{error}</p>} </div>}Related
OtpInputBoxes for a one-time code: typing moves along, Backspace goes back, pasting fills them all.5 views · 5 downloadsPasswordInputA password field with a show/hide toggle and, for new passwords, a strength meter.5 views · 4 downloadsPasswordStrengthA new-password field with a meter that fills as it gets stronger and a checklist of rules ticked as they are met.5 views · 4 downloadsAddressFormStreet, city, region, postcode and country in the usual layout, with the autocomplete names browsers fill from.5 views · 7 downloadsAuthFormSign in, sign up, forgot and reset password in one form that switches in place, each posting to its own route.7 views · 4 downloadsAutocompleteA free-text box that offers matches as one types, with the arrow keys and Enter; whatever is typed may stay.5 views · 7 downloads
Thread
Thread 0 posts
On ConsentCheckbox: questions, tips, corrections, and code that uses it.
No posts yet. The first one opens the thread.