Zero Trickery Custom Radios and Checkboxes

I feel like half of all “custom-designed radio buttons and checkboxes” do two things:

  1. Make them bigger
  2. Colorize them

I always think of SurveyMonkey for having big chunky radios and checkboxes. And indeed, just poking at their interface quickly, even internally, the app uses has those all over the place:

SurveyMonkey’s implementation appears to be pseudo-elements on a <label> element with icon fonts and such.

I think it’s worth noting that if that’s all you are doing, you might be able to do that with zero CSS trickery. Just… make them bigger and colorize them.

Like…

input[type="radio"],
input[type="checkbox"] {
  width: 3em;
  height: 3rem;
  accent-color: green;
}

That’ll chunk those suckers up and colorize them pretty good!

Firefox
Chrome

Except (obligatory sad trombone) in Safari:

Safari

We’re so close to having some very simple CSS to accomplish the main use-case for custom checkboxes and radios. But no cigar, that is, unless you can bring yourself to just not care about the Safari UI (it is still perfectly functional, after all).

If you do need to give up and go for a completely custom design, Stephanie Eckles has got you covered:

In related news, I always think of a “toggle” UI control as a set of 2 radio buttons progressively enhanced, but it turns out <button> is the way, as Michelle Barker covers here. No native UI for slider toggle thingies, alas.


The post Zero Trickery Custom Radios and Checkboxes appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.

“Yes or No?”

Sara Soueidan digs into this HTML/UX situation. “Yes” or “no” is a boolean situation. A checkbox represents this: it’s either on or off (uh, mostly). But is a checkbox always the best UX? It depends, of course:

Use radio buttons if you expect the answer to be equally distributed. If I expect the answer to be heavily biased to one answer I prefer the checkbox. That way the user either makes an explicit statement or just acknowledges the expected answer.

If you want a concrete, deliberate, explicit answer and don’t want a default selection, use radio buttons. A checkbox has an implicit default state. And the user might be biased to the default option. So having the requirement for an explicit “No” is the determinig factor.

So you’ve got the checkbox approach:

<label>
  <input type="checkbox">
  Yes?
</label>

Which is nice and compact but you can’t make it “required” (easily) because it’s always in a valid state.

So if you need to force a choice, radio buttons (with no default) are easier:

<label>
  <input type="radio" name="choice-radio">
  Yes
</label>
<label>
  <input type="radio" name="choice-radio">
  No
</label>

I mean, we might as well consider another a range input too, which can function as a toggle if you max it out at 1:

<label class="screen-reader-only" for="choice">Yes or No?</label>
<span aria-hidden="true">No</span>
<input type="range" max="1" id="choice" name="choice">
<span aria-hidden="true">Yes</span>

Lolz.

And I suppose a <select> could force a user choice too, right?

<label>
  Yes or no?
  <select>
    <option value="">---</option>
    <option value="">Yes</option>
    <option value="">No</option>
  </select>
</label>

I weirdly don’t hate that only because selects are so compact and styleable.

If you really wanna stop and make someone think, though, make them type it, right?

<label>
  Type "yes" or "no"
  <input type="text" pattern="[Yy]es|[Nn]o">
</label>

Ha.


The post “Yes or No?” appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.

Custom Styling Form Inputs With Modern CSS Features

It’s entirely possible to build custom checkboxes, radio buttons, and toggle switches these days, while staying semantic and accessible. We don’t even need a single line of JavaScript or extra HTML elements! It’s actually gotten easier lately than it has been in the past. Let’s take a look.

Here’s where we’ll end up:

Things sure have gotten easier than they were!

The reason is that we can finally style the ::before and ::after pseudo-elements on the <input> tag itself. This means we can keep and style an <input> and won’t need any extra elements. Before, we had to rely on the likes of an extra <div> or <span>, to pull off a custom design.

Let’s look at the HTML

Nothing special here. We can style our inputs with just this HTML:

<!-- Checkbox -->
<input type="checkbox">

<!-- Radio -->
<input type="radio">

<!-- Switch -->
<input type="checkbox" class="switch">

That’s it for the HTML part, but of course it’s recommended to have name and id attributes, plus a matching <label> element:

<!-- Checkbox -->
<input type="checkbox" name="c1" id="c1">
<label for="c1">Checkbox</label>

<!-- Radio -->
<input type="radio" name="r1" id="r1">
<label for="r1">Radio</label>

<!-- Switch -->
<input type="checkbox" class="switch" name="s1" id="s1">
<label for="s1">Switch</label>

Getting into the styling 

First of all, we check for the support of appearance: none;, including it’s prefixed companions. The appearance property is key because it is designed to remove a browser’s default styling from an element. If the property isn’t supported, the styles won’t apply and default input styles will be shown. That’s perfectly fine and a good example of progressive enhancement at play.

@supports(-webkit-appearance: none) or (-moz-appearance: none) {
  input[type='checkbox'],
  input[type='radio'] {
    -webkit-appearance: none;
    -moz-appearance: none;
  }
}

As it stands today, appearance  is a working draft, but here’s what support looks like:

This browser support data is from Caniuse, which has more detail. A number indicates that browser supports the feature at that version and up.

Desktop

ChromeFirefoxIEEdgeSafari
82*74*No79*TP*

Mobile / Tablet

Android ChromeAndroid FirefoxAndroidiOS Safari
79*68*76*13.3*

Like links, we’ve gotta consider different interactive states with form elements. We’ll consider these when styling our elements:

  • :checked
  • :hover
  • :focus
  • :disabled

For example, here’s how we can style our toggle input, create the knob, and account for the :checked state:

/* The toggle container */
.switch {
  width: 38px;
  border-radius: 11px;
}

/* The toggle knob */
.switch::after {
  left: 2px;
  top: 2px;
  border-radius: 50%;
  width: 15px;
  height: 15px;
  background: var(--ab, var(--border));
  transform: translateX(var(--x, 0));
}

/* Change color and position when checked */
.switch:checked {
  --ab: var(--active-inner);
  --x: 17px;
}

/* Drop the opacity of the toggle knob when the input is disabled */
.switch:disabled:not(:checked)::after {
  opacity: .6;
}

We are using the <input> element like a container. The knob inside of the input is created with the ::after pseudo-element. Again, no more need for extra markup!

If you crack open the styles in the demo, you’ll see that we’re defining some CSS custom properties because that’s become such a nice way to manage reusable values in a stylesheet:

@supports(-webkit-appearance: none) or (-moz-appearance: none) {
  input[type='checkbox'],
  input[type='radio'] {
    --active: #275EFE;
    --active-inner: #fff;
    --focus: 2px rgba(39, 94, 254, .25);
    --border: #BBC1E1;
    --border-hover: #275EFE;
    --background: #fff;
    --disabled: #F6F8FF;
    --disabled-inner: #E1E6F9;
  }
}

But there’s another reason we’re using custom properties — they work well for updating values based on the state of the element! We won’t go into full detail here, but here’s an example how we can use custom properties for different states.

/* Default */
input[type='checkbox'],
input[type='radio'] {
  --active: #275EFE;
  --border: #BBC1E1;
  border: 1px solid var(--bc, var(--border));
}

/* Override defaults */
input[type='checkbox']:checked,
input[type='radio']:checked {
  --b: var(--active);
  --bc: var(--active);
}
  
/* Apply another border color on hover if not checked & not disabled */
input[type='checkbox']:not(:checked):not(:disabled):hover,
input[type='radio']:not(:checked):not(:disabled):hover {
  --bc: var(--border-hover);
}

For accessibility, we ought to add a custom focus style. We are removing the default outline because it can’t be rounded like the rest of the things we’re styling. But a border-radius along with a box-shadow can make for a rounded style that works just like an outline.

input[type='checkbox'],
input[type='radio'] {
  --focus: 2px rgba(39, 94, 254, .25);
  outline: none;
  transition: box-shadow .2s;
}

input[type='checkbox']:focus,
input[type='radio']:focus {
  box-shadow: 0 0 0 var(--focus);
}

It’s also possible to align and style the <label> element which directly follows the <input> element in the HTML:

<input type="checkbox" name="c1" id="c1">
<label for="c1">Checkbox</label>
input[type='checkbox'] + label,
input[type='radio'] + label {
  display: inline-block;
  vertical-align: top;
  /* Additional styling */
}

input[type='checkbox']:disabled + label,
input[type='radio']:disabled + label {
    cursor: not-allowed;
}

Here’s that demo again:

Hopefully, you’re seeing how nice it is to create custom form styles these days. It requires less markup, thanks to pseudo-elements that are directly on form inputs. It requires less fancy style switching, thanks to custom properties. And it has pretty darn good browser support, thanks to @supports.

All in all, this is a much more pleasant developer experience than we’ve had to deal with in the past!

The post Custom Styling Form Inputs With Modern CSS Features appeared first on CSS-Tricks.

A11Y with Lindsey

Lindsey Kopacz has a wonderful blog about accessibility. I've seen a number of her articles making the rounds lately and I was like, dang I better make sure I'm subscribed. For example:

Regarding that last one, I remember learning from Sara Soueidan that a good tip for this to position them over the new custom checkboxes and hide them via opacity instead of hiding the native checkboxes by clipping them away. That covers the scenario of people exploring a touch screen for native interactive elements.

The post A11Y with Lindsey appeared first on CSS-Tricks.

Under-Engineered Toggles

Toggles. Switches. Whatever you want to call them, they've been with us for some time and have been a dominant a staple for many form interfaces. They're even baked right into many CSS frameworks, including Bootstrap and Foundation.

It's easy to think of them in binary terms: on and off. Off and on. Click to change the state and call it a day, right? I mean, it's just a checkbox with a styled visual representation.

Well, Adrian Roselli shows us that there's a lot more to think about. And, not only that, but he shows how we can under-engineer them:

I am only going to provide styles to visually convert a standard checkbox into a visual toggle. No ARIA, no script, no special features. A progressively enhanced checkbox that will continue to work if the CSS file does not load

There's a lot to digest here. His approaches to accessibility run the gamut, from hover, active, focus and disabled states to contrast in both light and dark modes, and many things in between. What's particularly key is the progressive enhancement he mentions in that quote above.

I think the most interesting thing about Adrian’s post is just how flexible his approach is to handle any situation, including color schemes and writing modes. He also takes note of the indeterminate checkbox, that state that's nether on or off, but something perhaps in between. We have a CSS pseudo-selector for that and it could warrant a post its own, given that it's a purely visual state that cannot be set in the HTML and needs to be registered via JavaScript. It's interesting to think of an "in between" state for a switch and Adrian's use case for the default state Airplane Mode is pretty compelling.

It’s an awful lot of work that we have to do to ensure that the front-end is designed well and I think this post is the best example I’ve seen in a while as to why our work is not a problem to be solved and more of a challenge to better understand the tools of our craft.

Direct Link to ArticlePermalink

The post Under-Engineered Toggles appeared first on CSS-Tricks.