1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
|
<!doctype html>
<html lang="en" id="top">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" href="data:,">
<title>Easy Toggle Switches</title>
<link href="https://bt.ht/atom.xml" type="application/atom+xml" rel="alternate" title="Atom feed for blog posts" />
<style>*{box-sizing:border-box;}body{font-family:sans-serif;margin:0 auto;max-width:650px;padding:1rem;}img{max-width:100%;}pre{overflow:auto;}table{text-align:left;width:100%;}</style>
</head>
<nav>
<a href="#menu">Menu ↓</a>
</nav>
<main>
<h1>Easy Toggle Switches</h1>
<p>2019-02-18</p>
<p>Sometimes there is a need to use toggle elements in-place of the default <code>checkbox</code> inputs. The problem is, I tend to see a lot of developers reaching for plugins or JavaScript components in order to implement these toggles.</p>
<p><em>This is overkill</em>. You can create your own custom <code>input</code> elements to mimic toggles perfectly with just a small amount of CSS.</p>
<h2>What we will be building</h2>
<p><img src="/public/images/easy-toggle-switches.png" alt="Easy toggle switches" /></p>
<p><a href="https://codepen.io/bradleytaunt/pen/bGgbajY">Live CodePen Example</a></p>
<h2>The HTML</h2>
<p>The build structure for these toggles is really simple, we only need:</p>
<ul>
<li>a parent container to hold all our corresponding elements</li>
<li>a checkbox <code>input</code> element (which we will hide)</li>
<li>an empty label designed to represent the slider</li>
<li>a text label that references the <code>input</code> </li>
</ul>
<pre><code>/* Main toggle parent container */
<div class="toggle-switch">
/* Checkbox input, hidden with CSS */
<input class="toggle-input" type="checkbox" id="toggle-1">
/* The toggle slider element */
<label class="toggle-slider" for="toggle-1"></label>
/* The text label to the right of the slider */
<label class="toggle-label" for="toggle-1">Toggle Switch</label>
</div>
</code></pre>
<p>And that's everything we need for the HTML.</p>
<p><strong>ProTip:</strong> Don't forget to increment both the <code>id</code> and <code>for</code> attributes when adding additional toggles. This seems like a no-brainer but it's overlooked more than you think.</p>
<h2>The CSS</h2>
<p>To get things started we will add the styling to the <code>.toggle-switch</code> item directly (using <code>flexbox</code> in this demo for easier layout).</p>
<p><strong>Sidenote</strong>: You will notice the inclusion of CSS variables in this demo - if you are unfamiliar with how to use root variables in CSS, take a look at one of my previous posts: <a href="https://accssible.com/2018/03/24/css-variables/">CSS variables</a>.</p>
<pre><code>:root {
--primary-color: #4A90E2;
}
.toggle-switch {
align-items: center;
display: flex;
font-size: 14px;
justify-content: center;
margin: 20px 0;
}
</code></pre>
<p>Next we will hide the default browser checkbox element since we won't be needing it:</p>
<pre><code>.toggle-input {
position: absolute;
visibility: hidden;
z-index: -1;
}
</code></pre>
<p>Let's also add some base styling for the <code>label</code> containing the text corresponding to it's <code>input</code> sibling:</p>
<pre><code>.toggle-label {
color: #ccc;
cursor: pointer;
font-weight: bold;
padding-left: 10px;
text-shadow: 1px 1px 0 rgba(255,255,255,0.3);
transition: ease all .3s;
}
</code></pre>
<p>Now we target the <code>.toggle-slider</code> label and add the styling for the main slider base:</p>
<pre><code>/* This is just the main slider base */
.toggle-slider {
background: #eee;
border-radius: 9999px;
box-shadow: inset 0 2px 4px rgba(0,0,0,0.1),
inset 0 4px 8px rgba(0,0,0,0.1),
0 1px 1px rgba(255,255,255,1);
cursor: pointer;
display: inline-block;
height: 20px;
position: relative;
transition: all ease .3s;
width: 40px;
}
</code></pre>
<p>We <em>could</em> include a separate element for the circle toggle switcher itself, but instead we will use the <code>:before</code> pseudo element:</p>
<pre><code>.toggle-slider:before {
background: white;
border-radius: 9999px;
box-shadow: 0 4px 8px rgba(0,0,0,0.1),
0 2px 4px rgba(0,0,0,0.2);
content:'';
height: 16px;
left: 2px;
position: absolute;
top: 2px;
transition: all ease .3s;
width: 16px;
}
</code></pre>
<h3>Interaction</h3>
<p>Right now we just have a static toggle that does nothing when the user interacts with it. Let's change that by moving the pseudo element's position based on the checkbox <code>input</code> state and updating the label text color:</p>
<pre><code>.toggle-input:checked + .toggle-slider {
background: var(--primary-color);
}
.toggle-input:checked + .toggle-slider:before {
/* Move 100% of the width minus it's own width plus initial 'left' */
left: calc(100% - 18px);
}
.toggle-input:checked ~ .toggle-label {
color: var(--primary-color);
}
</code></pre>
<p>And because we already included the <code>transition</code> property on both the base slider and label text, everything animates nicely between state changes.</p>
<h2>Final code</h2>
<p>To make things easier, you can find the HTML & CSS is their entirety below:</p>
<h3>HTML</h3>
<pre><code><div class="toggle-switch">
<input class="toggle-input" type="checkbox" id="toggle-1">
<label class="toggle-slider" for="toggle-1"></label>
<label class="toggle-label" for="toggle-1">Toggle Switch</label>
</div>
<div class="toggle-switch">
<input class="toggle-input" type="checkbox" id="toggle-2">
<label class="toggle-slider" for="toggle-2"></label>
<label class="toggle-label" for="toggle-2">Toggle Switch</label>
</div>
<div class="toggle-switch">
<input class="toggle-input" type="checkbox" id="toggle-3" checked>
<label class="toggle-slider" for="toggle-3"></label>
<label class="toggle-label" for="toggle-3">Toggle Switch</label>
</div>
<div class="toggle-switch">
<input class="toggle-input" type="checkbox" id="toggle-4">
<label class="toggle-slider" for="toggle-4"></label>
<label class="toggle-label" for="toggle-4">Toggle Switch</label>
</div>
</code></pre>
<h3>The CSS</h3>
<pre><code>:root {
--primary-color: #4A90E2;
}
.toggle-switch {
align-items: center;
display: flex;
font-size: 14px;
justify-content: center;
margin: 20px 0;
}
.toggle-input {
position: absolute;
visibility: hidden;
z-index: -1;
}
.toggle-slider {
background: #eee;
border-radius: 9999px;
box-shadow: inset 0 2px 4px rgba(0,0,0,0.1),
inset 0 4px 8px rgba(0,0,0,0.1),
0 1px 1px rgba(255,255,255,1);
cursor: pointer;
display: inline-block;
height: 20px;
position: relative;
transition: all ease .3s;
width: 40px;
}
.toggle-slider:before {
background: white;
border-radius: 9999px;
box-shadow: 0 4px 8px rgba(0,0,0,0.1),
0 2px 4px rgba(0,0,0,0.2);
content:'';
height: 16px;
left: 2px;
position: absolute;
top: 2px;
transition: all ease .3s;
width: 16px;
}
.toggle-input:checked + .toggle-slider {
background: var(--primary-color);
}
.toggle-input:checked + .toggle-slider:before {
left: calc(100% - 18px);
}
.toggle-label {
color: #ccc;
cursor: pointer;
font-weight: bold;
padding-left: 10px;
text-shadow: 1px 1px 0 rgba(255,255,255,0.3);
transition: ease all .3s;
}
.toggle-input:checked ~ .toggle-label {
color: var(--primary-color);
}
</code></pre>
<p>Enjoy your custom toggles!</p>
<footer role="contentinfo">
<h2>Menu Navigation</h2>
<ul id="menu">
<li><a href="/">Home</a></li>
<li><a href="/projects">Projects</a></li>
<li><a href="/uses">Uses</a></li>
<li><a href="/wiki">Wiki</a></li>
<li><a href="/resume">Resume</a></li>
<li><a href="/colophon">Colophon</a></li>
<li><a href="/now">Now</a></li>
<li><a href="/donate">Donate</a></li>
<li><a href="/atom.xml">RSS</a></li>
<li><a href="#top">↑ Top of the page</a></li>
</ul>
<small>
Built with <a href="https://git.sr.ht/~bt/barf">barf</a>. <br>
Maintained with ♥ for the web. <br>
Proud supporter of <a href="https://usefathom.com/ref/DKHJVX">Fathom</a> & <a href="https://nextdns.io/?from=74d3p3h8">NextDNS</a>. <br>
The content for this site is <a href="https://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA</a>.<br> The <a href="https://git.sr.ht/~bt/bt.ht">code for this site</a> is <a href="https://git.sr.ht/~bt/bt.ht/tree/master/item/LICENSE">MIT</a>.
</small>
</footer>
|