@@ -11,7 +11,8 @@ The Switch component is a user interface element used for toggling between two s
1111See [ Bootstrap Switch] ( https://getbootstrap.com/docs/5.3/forms/checks-radios/#switches ) docs.
1212
1313``` gjs live preview
14- import { Switch, Shadowed } from 'ember-primitives';
14+ import { Switch } from 'ember-primitives/components/switch';
15+ import { Shadowed } from 'ember-primitives/components/shadowed';
1516
1617<template>
1718 <Shadowed>
@@ -29,8 +30,7 @@ import { Switch, Shadowed } from 'ember-primitives';
2930```
3031
3132</details >
32-
33- <details ><summary ><h3 >Dark/Light Theme Switch</h3 ></summary >
33+ <details open ><summary ><h3 >Dark/Light Theme Switch</h3 ></summary >
3434
3535CSS inspired/taken from [ this Codepen] ( https://codepen.io/Umer_Farooq/pen/eYJgKGN?editors=1100 )
3636
@@ -47,93 +47,156 @@ const toggleTheme = (e) =>
4747 <s.Control {{on 'change' toggleTheme}} />
4848 <s.Label>
4949 <span class="sr-only">Toggle between light and dark mode</span>
50- <Moon />
51- <Sun />
52- <span class="ball"></span>
50+ <span class="ball" data-state={{if s.isChecked "on" "off"}}>
51+ {{#if s.isChecked}}
52+ <Moon />
53+ {{else}}
54+ <Sun />
55+ {{/if}}
56+ </span>
5357 </s.Label>
5458 </Switch>
5559
5660 <style>
57- @import url("https://fonts.googleapis.com/css2?family=Montserrat&display=swap");
58-
5961 * {box-sizing: border-box;}
6062
61- div {
62- padding: 1rem;
63- font-family: "Montserrat", sans-serif;
64- background-color: #eee;
65- display: flex;
66- justify-content: center;
67- align-items: center;
68- flex-direction: column;
69- text-align: center;
70- margin: 0;
71- transition: background 0.2s linear;
72- }
73-
74- div.dark {background-color: #292c35;}
75- div.dark label { background-color: #9b59b6; }
76-
77-
78- input[type='checkbox'][role='switch'] {
79- opacity: 0;
80- position: absolute;
81- }
82-
83- .sr-only {
84- width: 0px;
85- max-width: 0px;
86- height: 0px;
87- max-height: 0px;
88- overflow: hidden;
89- margin-left: -0.5rem;
63+ @scope {
64+ div {
65+ padding: 1rem;
66+ background-color: #eee;
67+ display: flex;
68+ justify-content: center;
69+ align-items: center;
70+ flex-direction: column;
71+ text-align: center;
72+ margin: 0;
73+ transition: background 0.2s linear;
74+ width: 100%;
75+ }
76+
77+ div.dark {background-color: #292c35;}
78+ div.dark label { background-color: #9b59b6; }
79+
80+
81+ input[type='checkbox'][role='switch'] {
82+ touch-action: pan-y;
83+ opacity: 0;
84+ position: absolute;
85+ }
86+
87+ .sr-only {
88+ width: 0px;
89+ max-width: 0px;
90+ height: 0px;
91+ max-height: 0px;
92+ overflow: hidden;
93+ margin-left: -0.5rem;
94+ }
95+
96+ label {
97+ background-color: #aaaaff;
98+ border: 1px solid;
99+ width: 60px;
100+ height: 32px;
101+ border-radius: 50px;
102+ position: relative;
103+ padding: 5px;
104+ cursor: pointer;
105+ display: flex;
106+ justify-content: space-between;
107+ align-items: center;
108+ gap: 0.5rem;
109+ }
110+
111+ svg { fill: currentColor; position: absolute; top: 3px; left: 3px; }
112+ .moon { color: #f1c4ff; }
113+ .sun { color: #f39c12; }
114+
115+ label .ball {
116+ background-color: #111;
117+ width: 26px;
118+ height: 26px;
119+ position: absolute;
120+ left: 2px;
121+ top: 2px;
122+ border-radius: 50%;
123+ transition-property: transform filter;
124+ transition-duration: 0.2s;
125+ transition-timing-function: linear(0, 0.1, 0.25, 0.5, 0.68, 0.8, 0.88, 0.94, 0.98, 0.995, 1);;
126+ border: 2px solid #f1c40f;
127+
128+ &[data-state="on"] {
129+ border: 2px solid #f1c4ff;
130+ }
131+ }
132+
133+ label:hover .ball {
134+ filter: drop-shadow(0 0 3px #f1c40f);
135+ }
136+ label:active .ball {
137+ filter: drop-shadow(0 0 10px #f1c40f);
138+ }
139+ input[type='checkbox'][role='switch']:checked + label .ball {
140+ transform: translateX(28px);
141+ }
142+ input[type='checkbox'][role='switch']:checked:hover + label .ball {
143+ filter: drop-shadow(0 0 3px #f1c4ff);
144+ }
145+ input[type='checkbox'][role='switch']:checked:active + label .ball {
146+ filter: drop-shadow(0 0 10px #f1c4ff);
147+ }
90148 }
91-
92- label {
93- background-color: #111;
94- width: 50px;
95- height: 26px;
96- border-radius: 50px;
97- position: relative;
98- padding: 5px;
99- cursor: pointer;
100- display: flex;
101- justify-content: space-between;
102- align-items: center;
103- gap: 0.5rem;
104- }
105-
106- svg { fill: currentColor; }
107- .fa-moon { color: #f1c40f; }
108- .fa-sun { color: #f39c12; }
109-
110- label .ball {
111- background-color: #fff;
112- width: 22px;
113- height: 22px;
114- position: absolute;
115- left: 2px;
116- top: 2px;
117- border-radius: 50%;
118- transition: transform 0.2s linear;
119- }
120-
121- input[type='checkbox'][role='switch']:checked + label .ball {
122- transform: translateX(24px);
123- }
124-
125149 </style>
126150 </Shadowed>
127151</template>
128152
129153// 🎵 It's raining, it's pouring, ... 🎵
130154// https://www.youtube.com/watch?v=ll5ykbAumD4
131155const Sun = <template>
132- <svg class="fa-sun" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">{{!--! Font Awesome Pro 6.4.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. --}}<path d="M361.5 1.2c5 2.1 8.6 6.6 9.6 11.9L391 121l107.9 19.8c5.3 1 9.8 4.6 11.9 9.6s1.5 10.7-1.6 15.2L446.9 256l62.3 90.3c3.1 4.5 3.7 10.2 1.6 15.2s-6.6 8.6-11.9 9.6L391 391 371.1 498.9c-1 5.3-4.6 9.8-9.6 11.9s-10.7 1.5-15.2-1.6L256 446.9l-90.3 62.3c-4.5 3.1-10.2 3.7-15.2 1.6s-8.6-6.6-9.6-11.9L121 391 13.1 371.1c-5.3-1-9.8-4.6-11.9-9.6s-1.5-10.7 1.6-15.2L65.1 256 2.8 165.7c-3.1-4.5-3.7-10.2-1.6-15.2s6.6-8.6 11.9-9.6L121 121 140.9 13.1c1-5.3 4.6-9.8 9.6-11.9s10.7-1.5 15.2 1.6L256 65.1 346.3 2.8c4.5-3.1 10.2-3.7 15.2-1.6zM160 256a96 96 0 1 1 192 0 96 96 0 1 1 -192 0zm224 0a128 128 0 1 0 -256 0 128 128 0 1 0 256 0z"/></svg>
156+ <svg
157+ class="sun"
158+ xmlns="http://www.w3.org/2000/svg"
159+ width="16"
160+ height="16"
161+ viewBox="0 0 16 16"
162+ fill="none"
163+ stroke="currentColor"
164+ stroke-width="1.5"
165+ stroke-linecap="round"
166+ stroke-linejoin="round"
167+ aria-hidden="true"
168+ >
169+ <circle cx="8" cy="8" r="3.25" />
170+ <line x1="8" y1="1" x2="8" y2="3" />
171+ <line x1="8" y1="13" x2="8" y2="15" />
172+ <line x1="1" y1="8" x2="3" y2="8" />
173+ <line x1="13" y1="8" x2="15" y2="8" />
174+ <line x1="3.05" y1="3.05" x2="4.47" y2="4.47" />
175+ <line x1="11.53" y1="11.53" x2="12.95" y2="12.95" />
176+ <line x1="11.53" y1="4.47" x2="12.95" y2="3.05" />
177+ <line x1="3.05" y1="12.95" x2="4.47" y2="11.53" />
178+ </svg>
133179</template>;
134180
135181const Moon = <template>
136- <svg class="fa-moon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512">{{!--! Font Awesome Pro 6.4.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. --}}<path d="M223.5 32C100 32 0 132.3 0 256S100 480 223.5 480c60.6 0 115.5-24.2 155.8-63.4c5-4.9 6.3-12.5 3.1-18.7s-10.1-9.7-17-8.5c-9.8 1.7-19.8 2.6-30.1 2.6c-96.9 0-175.5-78.8-175.5-176c0-65.8 36-123.1 89.3-153.3c6.1-3.5 9.2-10.5 7.7-17.3s-7.3-11.9-14.3-12.5c-6.3-.5-12.6-.8-19-.8z"/></svg>
182+ <svg
183+ xmlns="http://www.w3.org/2000/svg"
184+ class="moon"
185+ width="16"
186+ height="16"
187+ viewBox="0 0 16 16"
188+ fill="none"
189+ stroke="currentColor"
190+ stroke-width="1.5"
191+ stroke-linecap="round"
192+ stroke-linejoin="round"
193+ aria-hidden="true"
194+ >
195+ <path
196+ transform="translate(-1 0)"
197+ d="M11.5 2a5.5 5.5 0 1 0 2 9.5 4.5 4.5 0 0 1 -2 -9.5z"
198+ />
199+ </svg>
137200</template>;
138201```
139202
@@ -234,3 +297,9 @@ Adheres to the `switch` [role requirements](https://www.w3.org/WAI/ARIA/apg/patt
234297| <kbd >Enter</kbd > | Toggles the component's state |
235298
236299In addition, a label is required so that users know what the switch is for.
300+
301+ ## References
302+
303+ - https://web.dev/articles/building/a-switch-component
304+ - https://getbootstrap.com/docs/5.3/forms/checks-radios/#switches
305+ - https://web.dev/articles/building/a-switch-component
0 commit comments