├── LICENSE ├── README.md ├── index.html ├── script.js ├── slider.css ├── style.css └── toggle.css /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Ram Lmn 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Visualize CSS Matrix3D 2 | 3 | Helps you visualize CSS Matrix3D transforms, via inputs. 4 | 5 | [Try it out here](https://ramlmn.github.io/visualizing-matrix3d/) 6 | 7 | **Note:** This is buggy tool made to remind me of how Matrix transforms work (and it feels funny to play with 😁) 8 | 9 | ## License 10 | [MIT](LICENSE) 11 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | CSS Matrix3D Visualization 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | CSS3 Matrix3D 20 | GitHub 21 |
22 | 23 |
24 | 25 |
26 | 27 |
28 |
29 |
30 |
3
31 |
6
32 |
2
33 |
4
34 |
1
35 |
5
36 |
37 |

38 |

39 |

40 |
41 |
42 | 43 | 44 | 45 |
46 | 47 |
48 | 49 |
50 | 51 | 59 | 60 |

Transform Controls

61 |
62 |
63 | scalex(1) 64 | 65 |
66 |
67 | shearyx(0) 68 | 69 |
70 |
71 | shearzx(0) 72 | 73 |
74 |
75 | Px(0) 76 | 77 |
78 | 79 |
80 | shearxy(0) 81 | 82 |
83 |
84 | scaley(1) 85 | 86 |
87 |
88 | shearzy(0) 89 | 90 |
91 |
92 | Py(0) 93 | 94 |
95 | 96 |
97 | shearxz(0) 98 | 99 |
100 |
101 | shearyz(0) 102 | 103 |
104 |
105 | scalez(1) 106 | 107 |
108 |
109 | Pz(0) 110 | 111 |
112 | 113 |
114 | translatex(0) 115 | 116 |
117 |
118 | translatey(0) 119 | 120 |
121 |
122 | translatez(0) 123 | 124 |
125 |
126 | Pw(0) 127 | 128 |
129 |
130 | 131 | 132 |

Additional Controls (not part of CSS3 Matrix3D)

133 |
134 |
135 | rotateX(0deg) 136 | 137 |
138 |
139 | rotateY(0deg) 140 | 141 |
142 |
143 | rotateZ(0deg) 144 | 145 |
146 |
147 | 148 |
149 | 150 |
151 |

Notes:

152 | 160 |
161 | 162 |
163 | 164 |
165 | 166 | 167 | 168 | 169 | 170 | -------------------------------------------------------------------------------- /script.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 'use strict'; 3 | 4 | const canvas = document.querySelector('#demo__container'); 5 | 6 | const toggle = document.querySelector('#toggle-view'); 7 | toggle.addEventListener('change', _ => { 8 | if (toggle.checked) { 9 | canvas.style.transform = 'none'; 10 | } else { 11 | canvas.style.transform = ''; 12 | } 13 | }); 14 | 15 | const m11 = document.querySelector('#m11'); 16 | const m12 = document.querySelector('#m12'); 17 | const m13 = document.querySelector('#m13'); 18 | const m14 = document.querySelector('#m14'); 19 | const m21 = document.querySelector('#m21'); 20 | const m22 = document.querySelector('#m22'); 21 | const m23 = document.querySelector('#m23'); 22 | const m24 = document.querySelector('#m24'); 23 | const m31 = document.querySelector('#m31'); 24 | const m32 = document.querySelector('#m32'); 25 | const m33 = document.querySelector('#m33'); 26 | const m34 = document.querySelector('#m34'); 27 | const m41 = document.querySelector('#m41'); 28 | const m42 = document.querySelector('#m42'); 29 | const m43 = document.querySelector('#m43'); 30 | const m44 = document.querySelector('#m44'); 31 | 32 | const sliders = [m11, m12, m13, m14, m21, m22, m23, m24, m31, m32, m33, m34, m41, m42, m43, m44]; 33 | 34 | const rX = document.querySelector('#rotateX'); 35 | const rY = document.querySelector('#rotateY'); 36 | const rZ = document.querySelector('#rotateZ'); 37 | 38 | const target = document.querySelector('.demo-element'); 39 | const code = document.querySelector('.demo-code'); 40 | 41 | sliders.forEach(input => { 42 | input.addEventListener('input', evt => { 43 | const target = evt.currentTarget; 44 | const label = target.parentNode.querySelector('.item-input__label'); 45 | label.innerHTML = target.dataset.label.replace('$', target.value); 46 | calculateTransform(); 47 | }); 48 | }); 49 | 50 | [rX, rY, rZ].forEach(input => { 51 | input.addEventListener('input', evt => { 52 | const target = evt.currentTarget; 53 | const label = target.parentNode.querySelector('.item-input__label'); 54 | label.innerHTML = target.dataset.label.replace('$', target.value); 55 | calculateRotateTransform(); 56 | }); 57 | }); 58 | 59 | function getValues() { 60 | return sliders.map(slider => parseFloat(slider.value)); 61 | } 62 | 63 | function calculateTransform() { 64 | const arr = getValues(); 65 | 66 | const transform = [ 67 | 'matrix3d(', 68 | ` ${arr[0]}, ${arr[1]}, ${arr[2]}, ${arr[3]},`, 69 | ` ${arr[4]}, ${arr[5]}, ${arr[6]}, ${arr[7]},`, 70 | ` ${arr[8]}, ${arr[9]}, ${arr[10]}, ${arr[11]},`, 71 | ` ${arr[12]}, ${arr[13]}, ${arr[14]}, ${arr[15]}`, 72 | ')', 73 | ].join('\n'); 74 | 75 | code.textContent = `transform: ${transform}`; 76 | target.style.transform = transform; 77 | } 78 | 79 | function calculateRotateTransform() { 80 | const arr = getValues(); 81 | // let arr = [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]; 82 | 83 | const rx = parseFloat(rX.value); 84 | const ry = parseFloat(rY.value); 85 | const rz = parseFloat(rZ.value); 86 | 87 | const A = Math.cos(rx * (Math.PI / 180)); 88 | const B = Math.sin(rx * (Math.PI / 180)); 89 | const C = Math.cos(ry * (Math.PI / 180)); 90 | const D = Math.sin(ry * (Math.PI / 180)); 91 | const E = Math.cos(rz * (Math.PI / 180)); 92 | const F = Math.sin(rz * (Math.PI / 180)); 93 | 94 | // new transform array 95 | const newt = []; 96 | newt[0] = C * E * arr[0]; 97 | newt[1] = -1 * F; 98 | newt[2] = D; 99 | newt[3] = arr[3]; 100 | newt[4] = F; 101 | newt[5] = A * E * arr[5]; 102 | newt[6] = B; 103 | newt[7] = arr[7]; 104 | newt[8] = -1 * D; 105 | newt[9] = -1 * B; 106 | newt[10] = C * A * arr[10]; 107 | newt[11] = arr[11]; 108 | newt[12] = arr[12]; 109 | newt[13] = arr[13]; 110 | newt[14] = arr[14]; 111 | newt[15] = arr[15]; 112 | 113 | const fixedNewt = newt.map(item => parseFloat(parseFloat(item).toFixed(3))); 114 | 115 | // for (let i = 0; i < 16; i++) { 116 | // sliders[i].value = fixedNewt[i]; 117 | // } 118 | 119 | const transform = [ 120 | 'matrix3d(', 121 | ` ${fixedNewt[0]}, ${fixedNewt[1]}, ${fixedNewt[2]}, ${fixedNewt[3]},`, 122 | ` ${fixedNewt[4]}, ${fixedNewt[5]}, ${fixedNewt[6]}, ${fixedNewt[7]},`, 123 | ` ${fixedNewt[8]}, ${fixedNewt[9]}, ${fixedNewt[10]}, ${fixedNewt[11]},`, 124 | ` ${fixedNewt[12]}, ${fixedNewt[13]}, ${fixedNewt[14]}, ${fixedNewt[15]}`, 125 | ')', 126 | ].join('\n'); 127 | 128 | code.textContent = `transform: ${transform}`; 129 | target.style.transform = transform; 130 | } 131 | 132 | calculateTransform(); 133 | })(); 134 | -------------------------------------------------------------------------------- /slider.css: -------------------------------------------------------------------------------- 1 | .item-input--slider { 2 | width: 100%; 3 | display: flex; 4 | flex-direction: column; 5 | padding: .5em; 6 | } 7 | 8 | .item-input--slider [type="range"] { 9 | display: block; 10 | width: 100%; 11 | height: 2em; 12 | border-radius: 1px; 13 | margin: 0; 14 | padding: 0; 15 | background: transparent; 16 | -webkit-appearance: none; 17 | -moz-appearance: none; 18 | -ms-appearance: none; 19 | appearance: none; 20 | font: inherit; 21 | outline: 0; 22 | z-index: 1; 23 | cursor: pointer; 24 | } 25 | 26 | .item-input--slider:hover { 27 | background: #f5f5f5; 28 | } 29 | 30 | .item-input--slider [type="range"]::-moz-focus-outer { 31 | border: 0; 32 | outline: none !important; 33 | } 34 | 35 | .item-input--slider [type="range"]::-ms-tooltip { 36 | display: none; 37 | } 38 | 39 | 40 | /**************************** Tracks ****************************/ 41 | .item-input--slider [type="range"]::-webkit-slider-runnable-track { 42 | position: relative; 43 | background: rgba(0, 0, 0, .26); 44 | height: 2px; 45 | } 46 | 47 | .item-input--slider [type="range"]::-moz-range-track { 48 | position: relative; 49 | background: rgba(0, 0, 0, .26); 50 | height: 2px; 51 | border: none; 52 | } 53 | 54 | .item-input--slider [type="range"]::-ms-track { 55 | position: relative; 56 | background: rgba(0, 0, 0, .26); 57 | height: 2px; 58 | width: 100%; 59 | border: none; 60 | } 61 | 62 | 63 | .item-input--slider [type="range"]::-moz-range-progress { 64 | background: #3f51b5; 65 | } 66 | 67 | .item-input--slider [type="range"]::-ms-fill-lower { 68 | padding: 0; 69 | background: #3f51b5; 70 | } 71 | 72 | 73 | /**************************** Thumbs ****************************/ 74 | .item-input--slider [type="range"]::-webkit-slider-thumb { 75 | width: 1em; 76 | height: 1em; 77 | box-sizing: border-box; 78 | position: absolute; 79 | top: -.5em; 80 | border: none; 81 | border-radius: 50%; 82 | background: #3f51b5; 83 | transition: transform 0.18s cubic-bezier(.4,0,.2,1), 84 | border 0.18s cubic-bezier(.4,0,.2,1), 85 | box-shadow 0.18s cubic-bezier(.4,0,.2,1), 86 | background-color 0.28s cubic-bezier(.4,0,.2,1); 87 | -webkit-appearance: none; 88 | } 89 | 90 | .item-input--slider [type="range"]::-moz-range-thumb { 91 | width: 1em; 92 | height: 1em; 93 | box-sizing: border-box; 94 | position: absolute; 95 | top: -.5em; 96 | border: none; 97 | border-radius: 50%; 98 | background: #3f51b5; 99 | -moz-appearance: none; 100 | /*-moz-range-thumb doesn't currently support transitions.*/ 101 | } 102 | 103 | .item-input--slider [type="range"]::-ms-thumb { 104 | width: 1em; 105 | height: 1em; 106 | box-sizing: border-box; 107 | position: absolute; 108 | top: -.5em; 109 | border: none; 110 | border-radius: 50%; 111 | background: #3f51b5; 112 | } 113 | 114 | .item-input--slider [type="range"]:focus:not(:active)::-webkit-slider-thumb { 115 | box-shadow: 0 0 0 .5em rgba(63,81,181,.26); 116 | } 117 | 118 | .item-input--slider [type="range"]:focus:not(:active)::-moz-range-thumb { 119 | box-shadow: 0 0 0 .5em rgba(63,81,181,.26); 120 | } 121 | 122 | .item-input--slider [type="range"]:focus:not(:active)::-ms-thumb { 123 | box-shadow: 0 0 0 .5em rgba(63,81,181,.26); 124 | } 125 | 126 | .item-input--slider [type="range"]:active::-webkit-slider-thumb { 127 | background-image: none; 128 | transform: scale(1.25); 129 | } 130 | 131 | .item-input--slider [type="range"]:active::-moz-range-thumb { 132 | background: #3f51b5; 133 | transform: scale(1.25); 134 | } 135 | 136 | .item-input--slider [type="range"]:active::-ms-thumb { 137 | background: #3f51b5; 138 | transform: scale(1.25); 139 | } 140 | -------------------------------------------------------------------------------- /style.css: -------------------------------------------------------------------------------- 1 | html { 2 | box-sizing: border-box; 3 | } 4 | 5 | *, *::before, *::after { 6 | -webkit-tap-highlight-color: rgba(0, 0, 0, 0); 7 | box-sizing: inherit; 8 | } 9 | 10 | html, 11 | body { 12 | margin: 0; 13 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", 14 | Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", 15 | sans-serif; 16 | color: #212121; 17 | font-size: 16px; 18 | line-height: 1.5; 19 | 20 | width: 100%; 21 | height: 100%; 22 | display: flex; 23 | flex-direction: column; 24 | flex: 1; 25 | } 26 | 27 | :root { 28 | --theme-color-bg: rgb(63, 81, 181); 29 | --theme-color-bg-transparent: rgba(63, 81, 181, 0.6); 30 | --text-color-white: rgb(255, 255, 255); 31 | --text-color-light: rgb(238, 238, 238); 32 | --text-color-dark: rgb(70, 70, 70); 33 | } 34 | 35 | ::selection { 36 | background: #B3D4FC; 37 | text-shadow: none; 38 | } 39 | 40 | h1, h2, h3, h4, h5, h6 { 41 | margin: 0; 42 | padding: .5em 1em; 43 | color: var(--text-color-dark); 44 | } 45 | 46 | ul { 47 | margin: 0; 48 | } 49 | 50 | ul>li { 51 | padding: .25em 0; 52 | } 53 | 54 | p { 55 | margin: 0 0 .5em; 56 | padding: .5em 1em; 57 | } 58 | 59 | a { 60 | color: #3F51B5; 61 | text-decoration: none; 62 | } 63 | 64 | a:hover { 65 | text-decoration: underline; 66 | } 67 | 68 | header { 69 | display: flex; 70 | align-items: center; 71 | justify-content: space-between; 72 | z-index: 10; 73 | padding: .75em 1em; 74 | background: var(--theme-color-bg); 75 | color: var(--text-color-white); 76 | box-shadow: 0 2px 2px 0 rgba(0, 0, 0,.14), 77 | 0 3px 1px -2px rgba(0, 0, 0, .2), 78 | 0 1px 5px 0 rgba(0, 0, 0, .12); 79 | } 80 | 81 | header a { 82 | color: currentColor; 83 | } 84 | 85 | main { 86 | flex: 1; 87 | display: flex; 88 | flex-wrap: wrap; 89 | } 90 | 91 | section { 92 | overflow: auto; 93 | max-height: 100%; 94 | min-width: 25em; 95 | backface-visibility: hidden; 96 | } 97 | 98 | .demo-section { 99 | flex: 1; 100 | display: flex; 101 | flex-direction: column; 102 | background-image: url('data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%202%202%22%3E%3Cpath%20d%3D%22M1%202V0h1v1H0v1z%22%20fill-opacity%3D%22.05%22%2F%3E%3C%2Fsvg%3E'); 103 | background-size: 1em; 104 | } 105 | 106 | .demo-output { 107 | flex: 1; 108 | overflow: hidden; 109 | position: relative; 110 | min-height: 300px; 111 | } 112 | 113 | .demo-output__container { 114 | width: 100%; 115 | height: 100%; 116 | display: flex; 117 | align-items: center; 118 | justify-content: center; 119 | perspective: 1600px; 120 | transform-style: preserve-3d; 121 | transform: rotate3d(-1, 1, 0, 30deg); 122 | transition: transform 200ms ease; 123 | } 124 | 125 | .demo-output .axial-line { 126 | position: absolute; 127 | width: 200%; 128 | height: 0px; 129 | background: transparent; 130 | border: 1px dashed rgba(0, 0, 0, .5); 131 | margin: 0; 132 | padding: 0; 133 | } 134 | 135 | .demo-output .axial-line.axial-line--x { 136 | transform: rotateX(0deg); 137 | } 138 | 139 | .demo-output .axial-line.axial-line--y { 140 | transform: rotateZ(90deg); 141 | } 142 | 143 | .demo-output .axial-line.axial-line--z { 144 | transform: rotateY(90deg); 145 | } 146 | 147 | .demo-element { 148 | display: flex; 149 | align-items: center; 150 | justify-content: center; 151 | transform-style: preserve-3d; 152 | } 153 | 154 | .side { 155 | --side-size: 150px; 156 | --side-offset: 75px; 157 | 158 | position: absolute; 159 | 160 | display: flex; 161 | align-items: center; 162 | justify-content: center; 163 | 164 | width: var(--side-size); 165 | height: var(--side-size); 166 | 167 | color: var(--text-color-white); 168 | 169 | font-size: 32px; 170 | border: 1px inset rgba(0, 0, 0, 0.5); 171 | } 172 | 173 | .side-front { 174 | background: rgba(255, 0, 0, 0.8); 175 | transform: rotateY(0deg) translateZ(var(--side-offset)); 176 | } 177 | 178 | .side-back { 179 | background: rgba(0, 255, 0, 0.8); 180 | transform: rotateY(180deg) translateZ(var(--side-offset)); 181 | } 182 | 183 | .side-top { 184 | background: rgba(0, 0, 255, 0.8); 185 | transform: rotateX(90deg) translateZ(var(--side-offset)); 186 | } 187 | 188 | .side-bottom { 189 | background: rgba(255, 255, 0, 0.8); 190 | transform: rotateX(-90deg) translateZ(var(--side-offset)); 191 | } 192 | 193 | .side-left { 194 | background: rgba(255, 0, 255, 0.8); 195 | transform: rotateY(-90deg) translateZ(var(--side-offset)); 196 | } 197 | 198 | .side-right { 199 | background: rgba(0, 255, 255, 0.8); 200 | transform: rotateY(90deg) translateZ(var(--side-offset)); 201 | } 202 | 203 | code { 204 | align-self: flex-end; 205 | font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; 206 | font-size: 14px; 207 | word-wrap: break-word; 208 | white-space: pre-wrap; 209 | background: var(--text-color-light); 210 | padding: 2px 4px; 211 | border-radius: 2px; 212 | } 213 | 214 | .demo-code { 215 | display: block; 216 | width: 100%; 217 | padding: .5em; 218 | border-radius: 0; 219 | } 220 | 221 | .controls-section { 222 | border-left: 2px solid #eee; 223 | max-width: 600px; 224 | } 225 | 226 | .settings-list-header { 227 | display: block; 228 | margin: 0; 229 | padding: 2.5em 1em .5em; 230 | font-weight: normal; 231 | font-size: .9em; 232 | } 233 | 234 | .transform-settings { 235 | display: flex; 236 | flex-direction: column; 237 | } 238 | 239 | .basic-controls, 240 | .additional-controls { 241 | display: flex; 242 | flex-wrap: wrap; 243 | } 244 | 245 | .basic-controls .item-input--slider { 246 | width: 25%; 247 | } 248 | 249 | .additional-controls .item-input--slider { 250 | flex: 1; 251 | } 252 | 253 | .item-input--slider .item-input__label { 254 | text-align: center; 255 | } 256 | 257 | .notes { 258 | padding: 1em .5em; 259 | } 260 | 261 | .notes .notes-heading { 262 | font-size: 1em; 263 | padding: .5em; 264 | } 265 | 266 | @media only screen and (max-width: 1020px) { 267 | main { 268 | flex-direction: column; 269 | } 270 | 271 | .demo-section { 272 | width: 100%; 273 | } 274 | 275 | .controls-section { 276 | height: 40vh; 277 | width: 100%; 278 | max-width: 100%; 279 | overflow: auto; 280 | border-left: none; 281 | box-shadow: 0 -2px 2px 0 rgba(0,0,0,.14), 282 | 0 -3px 1px -2px rgba(0,0,0,.2), 283 | 0 -1px 5px 0 rgba(0,0,0,.12); 284 | } 285 | 286 | .item-input--slider { 287 | padding: .5em 1em 1em; 288 | } 289 | 290 | .basic-controls .item-input--slider { 291 | width: 50%; 292 | } 293 | } 294 | 295 | @media only screen and (max-width: 900px) { 296 | .additional-controls .item-input--slider { 297 | min-width: 100%; 298 | } 299 | } 300 | 301 | 302 | @media only screen and (max-width: 500px) { 303 | .item-input--slider { 304 | align-items: flex-start; 305 | } 306 | 307 | .basic-controls .item-input--slider { 308 | min-width: 100%; 309 | } 310 | 311 | header { 312 | position: fixed; 313 | left: 0; 314 | right: 0; 315 | padding: .5rem; 316 | background: transparent; 317 | box-shadow: none; 318 | color: rgba(0, 0, 0, .6); 319 | } 320 | 321 | section { 322 | min-width: initial; 323 | } 324 | } 325 | -------------------------------------------------------------------------------- /toggle.css: -------------------------------------------------------------------------------- 1 | .item-input--switch { 2 | height: 3em; 3 | padding: 0 1em; 4 | display: flex; 5 | cursor: pointer; 6 | align-items: center; 7 | } 8 | 9 | .item-input--switch:hover { 10 | background: #f5f5f5; 11 | } 12 | 13 | .item-input--switch input[type=checkbox] { 14 | position: absolute; 15 | height: 1px; 16 | width: 1px; 17 | overflow: hidden; 18 | clip: rect(1px, 1px, 1px, 1px); 19 | } 20 | 21 | .item-input--switch .switch { 22 | display: inline-block; 23 | width: 37px; 24 | height: 14px; 25 | position: relative; 26 | margin-right: .5em; 27 | } 28 | 29 | .item-input--switch .switch .track { 30 | border-radius: 19px; 31 | position: absolute; 32 | top: 0; 33 | bottom: 0; 34 | left: 1px; 35 | right: 1px; 36 | background: rgba(0,0,0,0.26); 37 | transform: translateZ(0); 38 | overflow: hidden; 39 | } 40 | 41 | .item-input--switch .switch .track::before { 42 | content: ''; 43 | position: absolute; 44 | top: 0; 45 | left: 0; 46 | right: 0; 47 | bottom: 0; 48 | background: #9FA8DA; 49 | opacity: 0; 50 | transition: opacity 0.15s ease-out; 51 | transform: translateZ(0); 52 | border-radius: 19px; 53 | } 54 | 55 | @supports (transform: none) { 56 | .item-input--switch .switch .track::before { 57 | border-radius: 0; 58 | } 59 | } 60 | 61 | .item-input--switch .switch .handle { 62 | position: absolute; 63 | top: -3px; 64 | left: 0; 65 | min-width: 20px; 66 | min-height: 20px; 67 | border-radius: 20px; 68 | background: #fafafa; 69 | box-shadow: 0 1px 3px rgba(0,0,0,0.5),inset 0 0 0 2px transparent; 70 | overflow: hidden; 71 | box-sizing: border-box; 72 | transition: all 0.15s ease-out; 73 | transform: translateZ(0); 74 | } 75 | 76 | .item-input--switch .switch .handle::before { 77 | content: ''; 78 | position: absolute; 79 | top: 0; 80 | left: 0; 81 | right: 0; 82 | bottom: 0; 83 | background: #3F51B5; 84 | opacity: 0; 85 | transition: opacity 0.15s ease-out; 86 | transform: translateZ(0); 87 | border-radius: 10px; 88 | } 89 | 90 | @supports (transform: none) { 91 | .item-input--switch .switch .handle::before { 92 | border-radius: 0; 93 | } 94 | } 95 | 96 | .item-input--switch input:focus+.switch .handle { 97 | box-shadow: 0 1px 3px rgba(0,0,0,0.5), 98 | inset 0 0 0 2px #3F51B5, 99 | 0 0 0 8px rgba(63,81,181,.26); 100 | } 101 | 102 | .item-input--switch :checked+.switch .track::before { 103 | opacity: 1; 104 | } 105 | 106 | .item-input--switch :checked+.switch .handle { 107 | transform: translate3d(17px, 0, 0); 108 | box-shadow: 0 1px 3px rgba(0,0,0,0.5), 109 | inset 0 0 0 2px transparent; 110 | } 111 | 112 | .item-input--switch :checked+.switch .handle::before { 113 | opacity: 1; 114 | } 115 | --------------------------------------------------------------------------------