Accessibility first: radio buttons
Tags: a11y, materialdesign, css, webdev
Published: 2018-12-06 10:34:06

---
title: Accessibility first: radio buttons
published: true
description: Accessible material design radio buttons
tags: a11y, materialdesign, css, webdev
series: Accessibility first
cover_image: https://thepracticaldev.s3.amazonaws.com/i/hkgtgtx82r3wneabjfly.png
---

This will be the last element I make, for now, in my accessibility first series. We've made material design switches, material design input boxes and now we're about to make material design radio buttons.

The aim of this short series was to show that you can put accessibility first and still use nice design. Most of these have been quite simple, stylised version of their base element.

## What do we want to create?

We'll be making a material design inspired [radio button](https://material.io/design/components/selection-controls.html#radio-buttons) today. As always I'll be avoiding JavaScript, not because it's intrinsically bad but rather I want to lean on the platform as much as possible, this makes a better user experience.

{% jsfiddle https://jsfiddle.net/link2twenty/b81ev7wp/ result %}

If you don't know radio buttons are grouped by name and you tab to each named group. Tabbing to the group will only select the checked button but you can use the arrow keys to move focus and, subsequently, check the new focused button.

## The markup

I have no doubt if you've seen any of my stuff you're used to the style now, label wrapping an input, which I will hide later.

```html
<label class="md_radio">
  <input name="{{group name}}" type="radio" />
  <span class="md_radio__button"></span>
  Radio Button
</label>
```
{% jsfiddle https://jsfiddle.net/link2twenty/apj94sue/ html result %}

## Styles

There are so many similarities between the radio button and the switch that I won't go into so much detail with this post but I'll still give you the general flavour.

The states we have to account for are
 - Not checked
 - Checked
 - Disabled
 - Focused


### Not checked

We'll do all the setup here too as not checked is the usual default state.

#### Setup

Here we're just setting some default margins, display type and setting the material font.

```css
@import url(http://fonts.googleapis.com/css?family=Open+Sans);
.md_radio {
  display: inline-flex;
  font-family: "Open Sans";
  align-items: center;
  margin: 5px 0;
}

.md_radio .md_radio__button {
  position: relative;
  cursor: pointer;
  margin: 0 3px;
}
```

#### The button

I'm using the `::before` and `::after` pseudo-classes to make the button, you'll notice my `::after` styles are set but are then hidden using scale this is preventing us from having to [paint](https://csstriggers.com/transform) every time a button is checked.

```css
.md_radio .md_radio__button::before,
.md_radio .md_radio__button::after {
  content: '';
  box-sizing: border-box;
  display: block;
  transition: all 100ms cubic-bezier(0.4, 0.0, 0.2, 1);
}

.md_radio .md_radio__button::before {
  height: 1.2em;
  width: 1.2em;
  border-radius: 50%;
  border: 0.15em solid #BDBDBD;
}

.md_radio .md_radio__button::after {
  position: absolute;
  top: 50%;
  left: 50%;
  height: 0.65em;
  width: 0.65em;
  border-radius: 50%;
  transform-origin: 50%, 50%;
  transform: scale(0, 0) translate(-50%, -50%);
  background: #00897B;
}
```
![Material Design Radio Buttons Not checked](https://thepracticaldev.s3.amazonaws.com/i/ha4pw5x2bg0s3s3uaimj.png)

### Checked

Checked is super simple for this example, we want to scale up `::after` and change the border colour of `::before` and that's it.

```css
.md_radio [type=radio]:checked+.md_radio__button::after {
  transform: scale(1, 1) translate(-50%, -50%);
}

.md_radio [type=radio]:checked+.md_radio__button::before {
  border-color: #00897B;
}
```
![Material Design Radio Buttons Checked](https://thepracticaldev.s3.amazonaws.com/i/o26rx8t2vqifukea5u7s.png)

### Disabled and focuses

These styles are exactly the same as my switch example so here's the code.

```css
/* Disabled */
.md_radio [type=radio]:disabled+.md_radio__button {
  cursor: not-allowed;
  filter: grayscale(100%);
  opacity: 0.6;
}

/* Focused */
.md_radio [type=radio]:focus+.md_radio__button {
  outline: #5d9dd5 solid 1px;
  box-shadow: 0 0 8px #5e9ed6;
}
```

## Finishing up

Finally, we need to hide our original input I do this by making the position absolute, to take up no space, setting the opacity to 0 and turning off pointer events.

```css
.md_radio [type=radio] {
  position: absolute;
  opacity: 0;
  pointer-events: none;
}
```

{% jsfiddle https://jsfiddle.net/link2twenty/b81ev7wp/ result html css %}

Thank you for coming along on this journey with me. I hope you can take something away from this an apply it to your own projects.