Forms

ConsentCheckbox

A required checkbox beside legal text with links to the terms and the privacy policy, posted as a real checkbox.

#forms#input#security#no-js

Demo

Preview

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.qwe
main.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>}
Thread
56Useful? Like it, or add to its thread below.

Thread 0 posts

On ConsentCheckbox: questions, tips, corrections, and code that uses it.

No posts yet. The first one opens the thread.