Tag: Forms

  • CSS Checkbox Generator: Custom Style, Accessible [2026]

    CSS Checkbox Generator: Custom Style, Accessible [2026]

    TL;DR: A CSS checkbox generator outputs custom-styled checkboxes that wrap a native <input type="checkbox"> for accessibility — keyboard navigation, screen reader announcements, form-submit semantics — while letting you control every visual aspect. The technique uses appearance: none to strip the default style, then renders a custom check via ::before pseudo-element. Our free CSS checkbox generator ships 20+ presets (rounded, bordered, animated, brand-colored), custom check icons, and the indeterminate state.

    HTML checkboxes look the same on every site by default — and every site replaces them. Bootstrap, Tailwind UI, Material Design, every CMS, every shadcn-ui clone has a custom checkbox component. The challenge isn’t visual — it’s keeping the accessibility intact while restyling. A custom div-based checkbox loses keyboard support, form submission, screen-reader announcements, and pointer-events behaviour. The trick: keep the native <input> for accessibility, hide it visually, and style a sibling element that responds to its state.

    Our CSS checkbox generator outputs the canonical pattern: a hidden native input + an associated label with a custom-styled box. Keyboard Space still toggles, Tab still navigates, screen readers still announce “checkbox, checked”, and the form still submits the value. This guide explains the accessibility-preserving CSS pattern, the gotchas with :focus-visible rings, and how to add the indeterminate state correctly.

    The accessibility-preserving CSS pattern

    The pattern uses appearance: none on the input to strip default OS styling, then customises every visual aspect with CSS — without losing the underlying <input>:

    input[type="checkbox"] {
      appearance: none;
      -webkit-appearance: none;
      width: 24px;
      height: 24px;
      border: 2px solid #d0d5dd;
      border-radius: 6px;
      background: white;
      cursor: pointer;
      position: relative;
      transition: all 150ms ease;
    }
    
    input[type="checkbox"]:checked {
      background: #635BFF;
      border-color: #635BFF;
    }
    
    input[type="checkbox"]:checked::after {
      content: "";
      position: absolute;
      inset: 4px 6px;
      border-right: 2px solid white;
      border-bottom: 2px solid white;
      transform: rotate(45deg);
    }
    
    input[type="checkbox"]:focus-visible {
      outline: 2px solid #635BFF;
      outline-offset: 2px;
    }

    That’s it — a fully custom-styled checkbox with keyboard support, form support, and accessibility intact. The native <input> still receives focus, still toggles on space, still submits. appearance: none works in every modern browser since 2020.

    Visual variations and presets

    Preset Look Best for
    Rounded square 8px corner radius (default in 2026 design) Modern web apps
    Sharp square No radius Brutalist or technical UIs
    Circle 50% radius — like a radio button Soft, friendly UIs
    Animated check Stroke-dash animation on toggle Premium feel
    Material ripple Tap-feedback ripple animation Material Design apps
    iOS-style Filled fill on check iOS-feel UIs

    How to generate a custom checkbox

    1. Open the CSS checkbox generator
    2. Pick a preset shape (rounded square, sharp, circle)
    3. Set border color, fill color, check icon style
    4. Toggle features: animation, ripple, indeterminate-state support, focus ring style
    5. Click Copy CSS for the production code (includes :focus-visible for accessibility)

    The indeterminate state — what most generators skip

    HTML checkboxes have three states: unchecked, checked, and indeterminate. Indeterminate is set programmatically (via JavaScript: el.indeterminate = true;) and represents “partially selected” — common in tree views where a parent is partially-but-not-fully checked among children.

    The CSS selector for indeterminate is :indeterminate. Our generator outputs a third visual state for it (typically a horizontal bar instead of a checkmark):

    input[type="checkbox"]:indeterminate {
      background: #635BFF;
      border-color: #635BFF;
    }
    
    input[type="checkbox"]:indeterminate::after {
      content: "";
      position: absolute;
      left: 4px;
      right: 4px;
      top: 50%;
      height: 2px;
      background: white;
      transform: translateY(-50%);
    }

    Common gotchas

    • Don’t replace the input with a div. Common mistake: hide the input entirely and use a custom <div> for the visual. You lose all accessibility — keyboard nav, screen-reader, form submit. Always keep the real input and style it.
    • :focus-visible vs :focus. Use :focus-visible for the focus ring — it shows only on keyboard focus, not on click. Without this, every click also shows the focus ring, which looks bad. :focus-visible is supported everywhere except the very oldest browsers.
    • Disabled state. A custom checkbox needs :disabled styling — usually 50% opacity and cursor: not-allowed. Don’t forget this state; users who don’t notice a disabled checkbox will tap it repeatedly in frustration.
    • Hover-only feedback isn’t enough. Touch users don’t have hover. Make sure your checked / unchecked / focus states all work without depending on hover.
    • Don’t forget the label. A bare checkbox without a label is unusable for screen readers. Wrap your checkbox + text in a <label> or use aria-labelledby. Most accessibility audits flag missing checkbox labels first.
    • iOS Safari and -webkit-appearance. Older Safari needs -webkit-appearance: none alongside appearance: none. Both prefixes work in Safari 14+; for iOS 12 and below, you need -webkit-appearance first.

    When NOT to use a CSS checkbox

    For toggle switches (on/off), use a switch component instead — visually different from checkboxes, semantically same. For radio buttons (mutually exclusive options), use type="radio" with the same custom-CSS pattern. For custom multi-select UIs (chip-style filters, tag pickers), button + aria-pressed is more appropriate than a checkbox. For very heavy custom interactions (slider toggles with drag gestures), a JS-driven component beats CSS-only — but for the 95% case, the native checkbox + custom CSS is the right call.

    Frequently asked questions

    Will custom CSS break accessibility?

    Not if you keep the native <input type="checkbox">. The element handles all accessibility — keyboard, screen reader, form submission. Hide its default visual with appearance: none and style it. Don’t replace the input with a div.

    Does this support the indeterminate state?

    Yes — set via JavaScript (checkbox.indeterminate = true) and styled via the :indeterminate CSS pseudo-class. Our generator outputs a third visual state for indeterminate (typically a bar instead of a checkmark).

    How do I make the focus ring keyboard-only?

    Use :focus-visible instead of :focus. :focus-visible only triggers on keyboard navigation, not on click. Supported in Chrome 86+, Firefox 85+, Safari 15.4+ — universal in 2026.

    Can I animate the check appearing?

    Yes — animate the ::after pseudo-element’s transform or stroke-dasharray. Generator presets include “animated check” with stroke-dashoffset that draws the checkmark on toggle. Performance is fine for hundreds of checkboxes.

    Is my data uploaded?

    No. The generator runs in your browser. Settings, the live preview, and the exported CSS stay on your device.

    What’s the difference between a checkbox and a switch?

    Semantically the same (binary on/off), visually different. Checkboxes are for forms with multiple selections (filter lists, terms-acceptance). Switches are for settings that take effect immediately (notifications on/off, dark mode). Native HTML doesn’t have a switch — use a checkbox + ARIA role="switch" for the same accessibility behaviour with switch styling.

    Related tools and guides

     

  • CSS Toggle Switch Generator: iOS-Style Switches [2026]

    CSS Toggle Switch Generator: iOS-Style Switches [2026]

    TL;DR: A CSS toggle switch is the iOS-style “slider” UI for binary on/off settings. Built around a hidden native checkbox (for accessibility), with a custom-styled track and thumb that animates between off and on positions. Different from a checkbox visually, but semantically the same. Use switches for “settings that take effect immediately” (notifications on/off, dark mode); use checkboxes for forms with multi-select. Our free CSS switch generator ships 12+ presets — iOS, Material, brutalist — with full keyboard / screen-reader support.

    The toggle switch became the standard UI element for boolean settings around 2010 when iOS popularised it on iPhone. Compared to a checkbox, the switch communicates “this setting takes effect right now” rather than “this is part of a form”. By 2026, every settings screen uses switches; checkboxes are reserved for forms with multiple options to select.

    The implementation challenge: keep keyboard support, screen-reader announcements, and form behaviour while replacing the visual entirely. The pattern: a native <input type="checkbox"> hidden visually but kept in the DOM, plus a CSS-styled track and thumb that respond to :checked. Add ARIA role="switch" for the correct screen-reader announcement (“switch, on” vs “checkbox, checked”), and you have a switch that’s accessible by default. Our CSS switch generator outputs this pattern with 12+ visual presets.

    Switch vs checkbox — when to use which

    Use case Switch Checkbox
    Settings that change immediately (notifications) Yes No
    Form with submit button (newsletter signup) No Yes
    Multi-select filter No Yes
    Dark mode toggle Yes No
    Accept terms of service No (it’s a form input) Yes
    Visibility setting (public / private) Yes (immediate effect) No

    Rule: switch = immediate-effect binary; checkbox = form input.

    The accessible CSS switch pattern

    /* HTML: native input, ARIA role */
    <label class="switch">
      <input type="checkbox" role="switch">
      <span class="track"></span>
    </label>
    
    /* CSS: hide native input, style the track */
    .switch input {
      position: absolute;
      opacity: 0;
      pointer-events: none;
    }
    
    .switch .track {
      display: inline-block;
      width: 44px;
      height: 24px;
      background: #d0d5dd;
      border-radius: 999px;
      position: relative;
      transition: background 200ms;
      cursor: pointer;
    }
    
    .switch .track::after {
      content: "";
      position: absolute;
      top: 2px;
      left: 2px;
      width: 20px;
      height: 20px;
      background: white;
      border-radius: 50%;
      transition: transform 200ms;
      box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
    }
    
    .switch input:checked + .track {
      background: #635BFF;
    }
    
    .switch input:checked + .track::after {
      transform: translateX(20px);
    }
    
    .switch input:focus-visible + .track {
      outline: 2px solid #635BFF;
      outline-offset: 2px;
    }

    The native input is hidden visually but kept in the DOM — keyboard Space still toggles it, screen readers announce “switch, on / off” because of role="switch", and form submission still works.

    Visual presets

    • iOS Default: rounded pill, white thumb, blue/green when on. Apple’s HIG style.
    • Material Design: wider thumb, slightly different proportions, M3 colour tokens.
    • Brutalist: sharp rectangular track, no border-radius, bold colours.
    • Outlined: visible border on the track, transparent track when off.
    • Squared: slight border-radius (8px) instead of pill — modern web app style.
    • Compact: 32×16 (smaller than default 44×24) for dense UI.
    • Animated icon: sun/moon icons fade in the thumb based on state.
    • 5 more presets in the gallery.

    How to generate a CSS switch

    1. Open the CSS switch generator
    2. Pick a preset (iOS, Material, brutalist, etc.)
    3. Adjust track color, thumb color, animation duration, and switch size
    4. Click Copy CSS + HTML for a complete drop-in component

    Common gotchas

    • Don’t replace the input with a div. Common bug: hide the input entirely, replace with custom div. Loses keyboard support, screen-reader announcements, form submission. Always keep the native input and style around it.
    • role=”switch” on input vs label. ARIA role="switch" goes on the input, not the wrapping label. Screen readers announce “switch, on / off” instead of “checkbox, checked / unchecked”.
    • :focus-visible is mandatory. Use :focus-visible for the focus ring, not :focus — otherwise every click shows the focus ring, looks bad. Universal browser support in 2026.
    • Don’t trap focus inside the switch. A common bug: pointer-events: none on the input but no clear focusable target. Result: keyboard users can’t tab to the switch. Verify keyboard navigation across all switches.
    • Animation can be disabled. Respect @media (prefers-reduced-motion: reduce) and skip the thumb-slide animation for users with vestibular disorders. Toggle still works; just snaps instantly.
    • Don’t make switches too small. Minimum touch target is 44×44 px (Apple HIG) or 48×48 px (Material). The visible track can be smaller, but the clickable area must hit minimum.

    When NOT to use a CSS switch

    For form inputs that submit with a button (newsletter signup, accept-terms, multi-select filters), use a checkbox — switches imply immediate effect. For binary settings that take significant action (deleting account, publishing content), use a button with confirmation, not a switch — accidental switch toggles are too easy. For three-way state (off / on / mixed), use a button group or radio. For very dense settings panels (10+ switches per screen), consider grouped settings with subheadings rather than a flat wall of switches.

    Frequently asked questions

    Switch or checkbox — what’s the difference?

    Semantically the same (binary state); visually and behaviourally different. Switch = immediate-effect setting (notifications on/off). Checkbox = form input that requires submit. Native HTML doesn’t have a switch element — use a checkbox + ARIA role="switch".

    Will custom CSS break accessibility?

    Not if you keep the native input. Hide it visually with positioning + opacity, but leave it in the DOM. Keyboard, screen reader, form submission all work. Don’t replace the input with a div.

    Is role=”switch” supported by screen readers?

    Yes — VoiceOver (iOS / macOS), TalkBack (Android), NVDA, JAWS all announce “switch, on” or “switch, off”. Older readers fall back to “checkbox, checked / unchecked”, which is acceptable.

    How do I prevent accidental toggles?

    For high-stakes actions, use a button with confirmation rather than a switch. For sensitive settings (e.g., “make profile public”), pair the switch with a confirmation modal on first use. Switches are designed for fast, reversible toggles — high-stakes actions deserve more friction.

    Is my data uploaded?

    No. The generator runs in your browser. Settings, the live preview, and the exported CSS stay on your device.

    Can I animate the switch differently?

    Yes — pick a “Bounce” or “Snap” preset, or write your own cubic-bezier() easing. Smooth slide is the default; bouncy animations feel playful but should respect prefers-reduced-motion.

    Related tools and guides