For a Chrome extension I made a while back, I wanted to add an options page. On that page I needed a checkbox, so I thought, why not make it into a nice toggle switch. Obviously I looked around the web to see what other people had made, to save myself some time and effort, but unfortunately I didn’t find anything I liked. The solutions either used images, a lot of JavaScript or they used all kinds of unnecessary HTML.
I wanted a something that was semantical and simple, so I ended up whipping up my own solution. I’ll explain how I went about it below, but if you don’t want to read that, you can also jump to the code on …
The HTML
I started out with the HTML. My idea was to just use the bare minimum HTML necessary, that looks like this:
I know some people don’t like it, but wrapping an input inside a label tag is perfectly legal HTML.
The CSS
Nothing more is needed in terms of HTML and since we’re not going to use any JavaScript, lets jump right into the CSS.
I’m using a CSS class on the label element to properly target it. In order to place all items inside the switch, we’re going to use absolute positioning, so the label will get position: relative. I also gave it a width and height and since
1.magik-switch {
2 display: inline-block;
3 height: 2rem;
4 position: relative;
5 width: 6rem;
6}
Next up are the On and Off labels. The temptation might be to add additional HTML for that, but that wouldn’t be
semantic and besides we can use the ::before
and ::after
pseudo elements
for this. Let’s put those on the
1.magik-switch::after,
2.magik-switch::before {
3 bottom: 0;
4 display: block;
5 position: absolute;
6 top: 0;
7}
8
9.magik-switch::before {
10 content: "On";
11 left: 0;
12 right: 50%;
13}
14
15.magik-switch::after {
16 content: "Off";
17 right: 0;
18 left: 50%;
19}
That’s pretty straightforward CSS I’d say. Both ::before
and ::after
are displayed as a block level element, placed
inside the <label>
. ::before
is placed on the left hand side and has a "On"
as content, ::after
is placed on the right
and has "Off"
as content.
In case you want an easy way to support different languages, it would be a good idea to add data attributes to
the <label>
element that hold the words you want to display, like so:
1<label class="magik-switch" data-label-on="On" data-label-off="Off">
2 <input type="checkbox">
3</label>
1.magik-switch::before {
2 content: attr(data-label-on);
3 left: 0;
4 right: 50%;
5}
6
7.magik-switch::after {
8 content: attr(data-label-off);
9 right: 0;
10 left: 50%;
11}
The last bit we need to implement is the actual toggle switch. There is still the HTML input element there, so we can use that to style the toggle switch.
So now it also becomes clear why the
TODO finish text
- accessible because of regular HTML used (TAB + Spacebar)
1.magik-switch input {
2 font-size: 1em;
3 line-height: 2.2rem;
4 text-align: center;
5}
Complete demo
1
2<!DOCTYPE html>
3<html>
4<head>
5<style>
6
7/*
8 * generic styles for the whole page
9 */
10body,
11html {
12 font-size: 62.5%;
13 margin: 0;
14 padding: 0;
15}
16
17.demo {
18 left: 50%;
19 margin: -1.1rem 0 0 -3.1rem;
20 position: absolute;
21 width: 100px;
22 top: 50%;
23}
24
25/*
26 * styles for the magik switch
27 */
28
29.magik-switch {
30 background: #ececec;
31 border: 1px solid rgba(0, 0, 0, 0.2);
32 border-radius: 4px;
33 box-shadow: 0 0 4px rgba(0, 0, 0, 0.1), inset 0 1px 3px 0px rgba(0, 0, 0, 0.1);
34 display: inline-block;
35 height: 2rem;
36 padding: 0.1rem;
37 position: relative;
38 width: 6rem;
39}
40
41.magik-switch::after,
42.magik-switch::before {
43 bottom: 0;
44 color: #999;
45 display: block;
46 font-size: 1em;
47 font-weight: bold;
48 line-height: 2.2rem;
49 position: absolute;
50 text-align: center;
51 top: 0;
52}
53
54.magik-switch::before {
55 color: #460;
56 content: attr(data-label-on);
57 left: 0;
58 right: 50%;
59 text-shadow: 0 0 2px rgba(10, 100, 5, 0.6);
60}
61
62.magik-switch::after {
63 color: #ccc;
64 content: attr(data-label-off);
65 right: 0;
66 left: 50%;
67 text-shadow: 0 0 2px rgba(255, 255, 255, 0.8);
68}
69
70.magik-switch input[type=checkbox] {
71 height: 2rem;
72 left: 0.1rem;
73 margin: 0;
74 position: absolute;
75 width: 3rem;
76 transition: left 0.15s ease-in-out;
77}
78
79.magik-switch input[type=checkbox]::after {
80 background: #fff;
81 border: 1px solid rgba(0, 0, 0, 0.3);
82 border-radius: 2px;
83 bottom: 0;
84 box-shadow: inset 0 1px 0 0 rgba(255, 255, 255, 0.5);
85 cursor: pointer;
86 content: "";
87 left: 0;
88 position: absolute;
89 right: 0;
90 top: 0;
91 z-index: 2;
92 transition: all 0.15s ease-in-out;
93}
94
95.magik-switch input[type=checkbox]:checked {
96 left: 50%;
97}
98
99.magik-switch input[type=checkbox]:checked::after {
100 background-color: #690;
101 /*
102 background: radial-gradient(ellipse at center,
103 rgba(180, 221, 180, 1) 0%,
104 rgba(131, 199, 131, 1) 17%,
105 rgba(82, 177, 82, 1) 33%,
106 rgba(0, 138, 0, 1) 67%,
107 rgba(0, 87, 0, 1) 83%,
108 rgba(0, 36, 0, 1) 100%
109 );*/
110 box-shadow: rgba(255, 255, 255, 0.4) 0 -1px 7px 1px, inset rgba(55, 112, 0, 0.9) 0 -1px 9px, rgba(122, 255, 0, 0.6) 0 2px 12px;
111}
112</style>
113</head>
114<body>
115
116<div class="demo">
117
118 <label class="magik-switch" data-label-on="On" data-label-off="Off">
119 <input type="checkbox">
120 </label>
121
122</div>
123
124</body>
125</html>