CSS Component Generator

Generate ready-to-paste buttons, toggle switches, tooltips, loading spinners and custom scrollbars, with the accessibility details these usually omit.

All 5 loaders run forever, so all 5 now ship a reduced-motion guard

Every loader preset emits animation: … infinite, which is correct — a spinner that stops looks like a page that has hung. It also meant the generated CSS moved continuously on every machine that pasted it, including machines whose owner had asked their operating system to stop things moving. prefers-reduced-motion is that request, and it is a system setting on every current desktop and mobile platform. People who turn it on are mostly not expressing a preference about taste: vestibular disorders make sustained rotation and translation genuinely unpleasant. Between them these five use all three of the movement types that setting targets.

LoaderMovementFull cyclePer minuteDegrees per minute
Spinner ring rotation 0.8s 75 27,000
Dual ring rotation 1s 60 21,600
Bouncing dots translation 1.2s 50
Sound bars scaling 0.9s 66.7
Pulse scaling 1.1s 54.5

A minute on a loading screen is 27,000 degrees of rotation from the spinner ring and 21,600 from the dual ring. Note the bouncing dots: the CSS says 0.6s, but the animation uses alternate, so a full there-and-back is 1.2s and the real count is 50 round trips rather than 100.

And the obvious guard is the wrong one

The usual fix is animation: none, and for a decorative flourish that is right. For a loader it is not, because the animation is the message. A frozen spinner does not say the page respects your settings; it says the page has hung, which is the one thing the loader exists to deny. What the setting actually targets is large-scale movement: rotation, translation, scaling. Opacity is not motion in that sense and is widely treated as safe. So the guard swaps the animation rather than removing it — still infinite, still visibly busy, nothing changing position or size.

NormalReduced motion
Animatestransformopacity
Cycle length1.2s to 1s3.2s
Repeats per minute50 to 7518.75
Dims to35% opacity

The replacement is slower than every animation it stands in for and fades to 35% rather than to nothing, so the element never vanishes. The guard also sets transform: none explicitly — without it, a loader caught mid-cycle keeps whatever rotation or scale it happened to hold when the media query took over.

3 of the 5 apply the guard to the loader element and 2 to its child spans, because that is where each one declares its animation — the bouncing dots and the sound bars are built from several moving pieces. Aiming at the parent instead would leave the movement running underneath a fading container, which is worse than doing nothing. Copy the CSS out and you get the @media (prefers-reduced-motion: reduce) block with it.

How to use

  1. Pick a component type.
  2. Adjust the colours, size and style.
  3. Copy the HTML and CSS together.
  4. Check keyboard focus and screen reader behaviour.

Frequently asked questions

Should a toggle switch be a checkbox underneath?

Yes. Building it from a real checkbox input keeps keyboard operation, screen reader announcement and form submission working for free. A div styled to look like a switch has none of that and has to reimplement all of it, usually incompletely.

Why should I not remove the focus outline?

Because keyboard users navigate by it, and without a visible focus indicator the interface becomes unusable without a mouse. If the default outline clashes with your design, replace it with something equally visible — a ring, a colour change, a shadow — rather than setting it to none.

How large should a touch target be?

At least 44 by 44 pixels, which is the widely used guideline and roughly the size of a fingertip. The visual element can be smaller if the interactive area is padded out to meet it, which is the usual approach for icon buttons.

Are CSS-only tooltips a good idea?

For decoration, yes. For anything a user needs, no — a tooltip shown purely on hover is unreachable by keyboard and invisible to touch, and content that only appears on hover is inaccessible to a substantial number of people. Important information belongs in the interface, not behind a hover.

Should a loading spinner announce itself?

Yes, if the wait is meaningful. A spinner is a purely visual signal, so screen reader users get nothing unless the region is marked as live and given a text status. It should also respect reduced-motion preferences, ideally by replacing the spin with a static or fading indicator.

Can I style scrollbars reliably?

Partly. Standard scrollbar colour and width properties are now widely supported, and the older webkit pseudo-elements offer finer control on those browsers. The important caution is not to make a scrollbar so thin or low-contrast that it becomes hard to see and grab.

🔒 This tool runs entirely in your browser. Nothing you enter is uploaded, logged, or stored.