How to make a Gmail like checkbox
Tags: webdev, tutorial, beginners, codenewbie
Published: 2022-06-04 17:06:19

In this article, we will learn how to use HTML and CSS to make a Gmail like checkbox. But before we jump right into the topic, there are a few key concepts I want to cover first to make sure we are on the same page. 

You can easily follow along by coding on [Codepen](https://codepen.io/). You can do that by clicking `Start Coding` button on the top left of Codepen home page. 
![Start Coding button snapshot](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/siv41ma06r1v8icxk3tc.png)

---

## Basic
Here are a few things we need to know first to better understand how to make a Gmail like checkbox. You don't need to fully understand each thing below. Having a basic idea of them is good enough. And don't dive too deep yet when you are linked to the documents on MDN. It may be a lot easier than you think when we start building the checkbox.


### Input elements

There is an element called [input](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input) in HTML. We use it to collect information from users. There are many [types](https://developer.mozilla.org/en-US/docs/Learn/Forms/HTML5_input_types) of them. 
For example:
```html
<input type="text">
```

The appearance of `input` might look different than yours based on the browser you are using, because browsers have their own default styles for input elements.
![Make a <input type="text"> on Codepen](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4jkseh1jfcqmqhu90aru.png)

We can add some text to describe what information we want to collect from users. There is an attribute called [placeholder](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/text#placeholder) that we can use on `<input type="text">` element to provide a brief hint.
```html
Name: <input type="text" placeholder="John Doe">
```
The value of `placeholder` attribute will appear inside the input box.
![add description and placeholder to input on Codepen](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ns2e3vo2swubcmibr9kb.png)

### Pseudo-elements
A CSS [pseudo-element](https://developer.mozilla.org/en-US/docs/Web/CSS/Pseudo-elements) is a keyword added to a selector that lets you style a specific part of the selected element(s).

we can style the input's placeholder like this
```css
input::placeholder {
  color: blue;
}
```
The text color of placeholder is set to blue.
![add style to input::placeholder](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vmj2msxl0k5sdzbsl1ej.png)


### Pseudo-classes 
A CSS [pseudo-class](https://developer.mozilla.org/en-US/docs/Web/CSS/Pseudo-classes) is a keyword added to a selector that specifies a special state of the selected element(s).

`input:hover` style input when input is hovered. `input:hover::placeholder` style input's placeholder when input is hovered.
```css
input:hover::placeholder {
  color: red;
}
```
We are styling the `hover` state, which requires a pointer to move on top of the element. Input's placeholder remains `color: blue` unless we move the pointer on top of it to trigger its hover state, then `color: red` will be applied. 

![hover on input](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vlwg9vhx552o86up3uxu.gif)


 


### Positioned element
When we set `position` to a valid value other than `static`, the element we select becomes a [positioned element](https://developer.mozilla.org/en-US/docs/Web/CSS/position#types_of_positioning).

Here is what will happen when we set `position` to [absolute](https://developer.mozilla.org/en-US/docs/Web/CSS/position#values) 

> The element is removed from the normal document flow, and no space is created for the element in the page layout. It is positioned relative to its closest positioned ancestor, if any; otherwise, it is placed relative to the initial containing block. Its final position is determined by the values of `top`, `right`, `bottom`, and `left`.

Normal flow is the default behavior how browsers place our elements. For example, the block elements are placed top to bottom and content(texts) are placed left to right. 

```html
<div>
  <div class="child">Hello</div>
  <div>World</div>
</div>
```
```css
.child {
  color: red;
}
```
`div` by default is block element, so they are placed top to bottom. The texts inside of divs are placed left to right.
![Show how two sibling div elements and the content are placed in document normal flow on Codepen](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lg2ltyuotxjeh25ruba6.png)

When we set `div.child` to `position: absolute;`, it no longer takes up space in the normal flow, as it doesn't exist. 
```css
.child {
  position: absolute;
}
```
The `div` that has content "World" is now placed at where `div.child` would be. Because `div.child` is now taken out of flow, the "World" div will act like it should be placed first.
![set .child to position absolute on Codepen](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/c32u4yefhbyj594bp057.png)


### Containing block
We can control the position of a positioned element by defining `top`, `right`, `left`, and `bottom`. The offset will be relative to the element's closest [containing block](https://developer.mozilla.org/en-US/docs/Web/CSS/Containing_block#identifying_the_containing_block).

```css
.child {
  position: absolute;
  right: 0;
}
```

When we set `right: 0;`, as you can see, the `.child` div is now at the right edge of the document. The reason that `.child` is not positioned relative to `.parent` div is because if a positioned element does not have a positioned ancestor, its containing block will be the initial containing block, which has the dimensions of the [viewport](https://developer.mozilla.org/en-US/docs/Glossary/Viewport).

 ![set right:0 on .child result to position at the right edge](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1323w8gop6rek7yp7lxk.png)

we can make `.parent` div to be a positioned element so `.child` can be positioned relative to `.parent` div. Again, the value of position that is other than `static` can set a element to positioned element.

```css
.parent {
  position: relative;
}
```

`.parent` div is now a positioned ancestor of `.child` div. `.parent` div becomes the containing block of `.child` div, so setting `top` `right` `left` `bottom` on `.child` div will now be relative to `.parent div`
![visual result on Codepen](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6h0m06ck7bxjyokx3lxs.png)

---

## How checkbox is made on Gmail?

Let's take a look at how checkbox looks like on Gmail and explore how it is made.

As you can see, when the pointer is close enough to the checkbox's bottom, left, top, except right, a circular clickable gray area will appear. That clickable gray area can toggle checkbox status.

![interact Gmail checkbox](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qam9rdffz1iipcb0bfu1.gif)

### Inspect element with DevTools

DevTools allow us to do a bunch of stuff that help us develop. We can use it to inspect element on the screen. Press F12 to open DevTools panel(if F12 doesn't work for you, try google how to open DevTools panel)


Click the icon at the top left corner of DevTools panel then move our pointer to the element we want to inspect. We don't need to precisely click on the one we want. We can hover over elements to find which one we want.
![the inspect icon on devtools panel](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9k5yfe8b1y1q4wc3vpcc.png)
 

As shown in the image, it seems that the checkbox we are looking for is a div, and it has two pseudo elements `::before` and `::after`. When selecting `::before`, we don't see it but it has a size 0*0, which means it is rendered. The div after `::before` is set to `display: none`, so when selecting it, we don't see its size because browser doesn't render it. When selecting `::after`, we can see its size covers the checkbox div. 
![inspect checkbox with devtools](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kmqf511e21tkuq72f8ld.gif)


Something you may have noticed is that the area of `::after` seems to match the area that we could trigger the circular clickable area of checkbox.  
We can change `::after` size to confirm this.
![change ::after in DevTools](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gp9z123ec3pv0uqbgks4.gif)

Because pseudo-element is part of an element, it still belongs to that element. In this case, hovering on `::after` is actually hovering on the checkbox div.

Next, we can explore where that circle comes from. We have learned that `:hover` style only applied when the `:hover` state is triggered. So we can select the elements one by one and see whose style changes on hover.
As we can see, the styles of checkbox div and `::before` have `:hover` styles applied when hovering.
![see which element styles change when hover](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0oxz8bnmdmcssplncaog.gif)


We can use DevTools to force element state rather than manually trigger a state. Force the checkbox div to be hover state so we can easily examine the styles applied by `:hover`. The circle we are looking for seems to be the `::before`. Again, we can change its style to confirm that.

![change checkbox's ::before style with DevTools](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6fu7xdfsgvdb4vkhaj49.gif)


The circle comes from `::before` has a initial size of 0*0, which is the result of setting `transform: scale(0)`. When hover style applied, its size becomes 40*40, which is the result of setting `transform: scale(1)`. 

![check ::before size with DevTools](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wygymssy9ibtmoubqdgt.gif)



Actually, We have gathered enough information to make a similar checkbox. We can use the native checkbox input element in HTML to build one. In order you are curious, the checkbox on Gmail is a fully custom checkbox. The checkbox div has a background image as the box. When the checkbox div is checked, the background image becomes a checked box. 

![demonstrate with DevTools](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/h85p37eyecsonxfucr3r.gif)




---


## Let's start building

TODO:
1. A checkbox
2. A visible circular clickable area
3. An invisible area that can make the circle appear
4. Make it look nicer

### A checkbox
First, we create a [checkbox](https://developer.mozilla.org/zh-CN/docs/Web/HTML/Element/Input/checkbox). 

```html
<input type="checkbox" class="gmail-checkbox" />
```
That's all we need to do to have a functional checkbox.
![create a checkbox on Codepen](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/aynb3gmv5pg6rusc318s.png)
 
### A visible circular clickable area

Let's make a `::before` equal to the checkbox size first. `content: ''` is necessary to create a `::before` or `::after` pseudo-element. `position: absolute` make it a positioned element , so we can set `top` `right` `bottom` `left` on it. `inset: 0` is a shorthand as setting `top: 0` `right: 0` `bottom: 0` `left: 0` at once. 

```css
.gmail-checkbox::before {
  content: '';
  position: absolute;
  inset: 0;
  background-color: red;
  opacity: 0.5;
}
```

I add `background-color` and `opacity` to illustrate a problem we have. The problem is that the `::before` expand across the whole screen, because its containing block is the initial containing block which has the dimensions of the [viewport](https://developer.mozilla.org/en-US/docs/Glossary/Viewport).
![::before expand across the whole screen](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wjdgphzf6v1jwc0ni9ey.png)
 

We want `top` `right` `bottom` `left` on `::before` to be relative to the checkbox not the viewport. We need to make the checkbox a positioned element, so the containing block of `::before` can be the checkbox instead of the initial containing block.

```css
.gmail-checkbox {
  position: relative;
}
```

Checkbox becomes the containing block of `::before`.
![::before has same size as checkbox visually](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qzzhb04hgyg9yx4k6d93.png)
 

`transform: scale(3)` makes `::before` larger and `border-radius: 50%` makes it rounded.
```css
.gmail-checkbox::before {
  border-radius: 50%;
  transform: scale(3);
}
```

Clicking on the red circle can toggle checkbox. We have learned that pseudo-element `::before` is part of an element, so we are actually clicking on the checkbox.

![click on ::before](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yqzclgo9gqqr0uu1cicl.gif)


We want to hide `::before` the circle and only show it when hovering on the checkbox.

```css
.gmail-checkbox::before {
  opacity: 0;
  transform: scale(0);
}

.gmail-checkbox:hover::before {
  transform: scale(3);
  opacity: 0.5;
}
```

Currently, we have to hover exactly on the visible checkbox area to show `::before` the clickable circle.

![hover on checkbox to show ::before](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gq8zgr3sguw0e42rfsce.gif)


### An invisible area that can make the circle appear

We can make an invisible area as an extend of the checkbox to make it easier to be triggered hover state.

```css
.gmail-checkbox::after {
  content: '';
  position: absolute;
  inset: 0;
  transform: scale(3);
}
```

I add `background-color` and `opacity` to illustrate the area of `::after`. Hovering on `::after` is actually hovering on the element it belongs to, so 

![hover on ::after](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/flg7mds7g3iydknsvu2a.gif)

### Make it look nicer

Add [transition](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Transitions/Using_CSS_transitions) to the circle.
```css
.gmail-checkbox:hover::before {
  transform: scale(3);
  opacity: 0.5;
  transition: transform opacity;
  transition-duration: 0.3s;
}
```

![transition effect on ::before](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tqha62vufg8ayfqmk7hu.gif)

You may notice the color on `::before` is coving the checkbox. That's because it is stacked on top of the checkbox. We can use [z-index](https://developer.mozilla.org/en-US/docs/Web/CSS/z-index) on positioned elements to control how elements are overlapped, but it is out of scope of this tutorial so we won't discuss it.


In our case, the color from `::before` should be light , otherwise the checkbox won't be visible. We can use [accent-color](https://developer.mozilla.org/en-US/docs/Web/CSS/accent-color) to change checkbox color.

## Final result

![final result](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/amag9gixgfwsrlglnlji.gif)

HTML
```html
<input type="checkbox" class="gmail-checkbox" />
```
CSS
```css
.gmail-checkbox {
  position: relative;
  accent-color: red;
}

.gmail-checkbox::before {
  content: '';
  position: absolute;
  inset: 0;
  background-color: red;
  opacity: 0;
  transform: scale(0);
  border-radius: 50%;
} 

.gmail-checkbox:hover::before {
  transform: scale(3);
  opacity: 0.1;
  transition: transform opacity;
  transition-duration: 0.3s;
}

.gmail-checkbox::after {
  content: '';
  position: absolute;
  inset: 0;
  transform: scale(3);
}
```

## Wrap up
First, we look at a few CSS fundamentals. Then, we inspect the checkbox on Gmail to see how it is made. Finally, we make a similar checkbox with all the knowledge we learn so far. 

This article is meant for the beginners so I tried my best to break things down and I repeated a few things purposely because these fundamentals are not easy to understand. After having an overall idea, you can look for the materials you want to learn more.

I hope you find this article useful and I am welcome for any feedback. Thank you :D