mtrl 0.2.8 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/index.ts +4 -0
  2. package/package.json +1 -1
  3. package/src/components/button/button.ts +34 -5
  4. package/src/components/navigation/api.ts +131 -96
  5. package/src/components/navigation/features/controller.ts +273 -0
  6. package/src/components/navigation/features/items.ts +133 -64
  7. package/src/components/navigation/navigation.ts +17 -2
  8. package/src/components/navigation/system/core.ts +302 -0
  9. package/src/components/navigation/system/events.ts +240 -0
  10. package/src/components/navigation/system/index.ts +184 -0
  11. package/src/components/navigation/system/mobile.ts +278 -0
  12. package/src/components/navigation/system/state.ts +77 -0
  13. package/src/components/navigation/system/types.ts +364 -0
  14. package/src/components/slider/config.ts +20 -2
  15. package/src/components/slider/features/controller.ts +737 -0
  16. package/src/components/slider/features/handlers.ts +18 -16
  17. package/src/components/slider/features/index.ts +3 -2
  18. package/src/components/slider/features/range.ts +104 -0
  19. package/src/components/slider/schema.ts +141 -0
  20. package/src/components/slider/slider.ts +34 -13
  21. package/src/components/switch/api.ts +16 -0
  22. package/src/components/switch/config.ts +1 -18
  23. package/src/components/switch/features.ts +198 -0
  24. package/src/components/switch/index.ts +1 -0
  25. package/src/components/switch/switch.ts +3 -3
  26. package/src/components/switch/types.ts +14 -2
  27. package/src/components/textfield/api.ts +53 -0
  28. package/src/components/textfield/features.ts +322 -0
  29. package/src/components/textfield/textfield.ts +8 -0
  30. package/src/components/textfield/types.ts +12 -3
  31. package/src/components/timepicker/clockdial.ts +1 -4
  32. package/src/core/compose/features/textinput.ts +15 -2
  33. package/src/core/composition/features/dom.ts +45 -0
  34. package/src/core/composition/features/icon.ts +131 -0
  35. package/src/core/composition/features/index.ts +12 -0
  36. package/src/core/composition/features/label.ts +155 -0
  37. package/src/core/composition/features/layout.ts +47 -0
  38. package/src/core/composition/index.ts +26 -0
  39. package/src/core/index.ts +1 -1
  40. package/src/core/layout/README.md +350 -0
  41. package/src/core/layout/array.ts +181 -0
  42. package/src/core/layout/create.ts +55 -0
  43. package/src/core/layout/index.ts +26 -0
  44. package/src/core/layout/object.ts +124 -0
  45. package/src/core/layout/processor.ts +58 -0
  46. package/src/core/layout/result.ts +85 -0
  47. package/src/core/layout/types.ts +125 -0
  48. package/src/core/layout/utils.ts +136 -0
  49. package/src/index.ts +1 -0
  50. package/src/styles/abstract/_variables.scss +28 -0
  51. package/src/styles/components/_navigation-mobile.scss +244 -0
  52. package/src/styles/components/_navigation-system.scss +151 -0
  53. package/src/styles/components/_switch.scss +133 -69
  54. package/src/styles/components/_textfield.scss +259 -27
  55. package/demo/build.ts +0 -349
  56. package/demo/index.html +0 -110
  57. package/demo/main.js +0 -448
  58. package/demo/styles.css +0 -239
  59. package/server.ts +0 -86
  60. package/src/components/slider/features/slider.ts +0 -318
  61. package/src/components/slider/features/structure.ts +0 -181
  62. package/src/components/slider/features/ui.ts +0 -388
  63. package/src/components/textfield/constants.ts +0 -100
  64. package/src/core/layout/index.js +0 -95
@@ -0,0 +1,244 @@
1
+ // src/components/navigation/_mobile.scss
2
+ @use '../../styles/abstract/variables' as v;
3
+ @use '../../styles/abstract/mixins' as m;
4
+ @use '../../styles/abstract/base' as base;
5
+ @use '../../styles/abstract/theme' as t;
6
+
7
+ $prefix: base.$prefix;
8
+
9
+ // Mobile navigation overlay
10
+ .#{$prefix}-nav-overlay {
11
+ position: fixed;
12
+ top: 0;
13
+ left: 0;
14
+ right: 0;
15
+ bottom: 0;
16
+ background-color: t.alpha('shadow', 0.5);
17
+ z-index: v.z-index('modal') - 1; // Just below modal level but above most content
18
+ opacity: 0;
19
+ visibility: hidden;
20
+ transition: opacity 0.3s v.motion('easing-standard'), visibility 0.3s v.motion('easing-standard');
21
+
22
+ // Add backdrop blur for a modern effect where supported
23
+ @supports (backdrop-filter: blur(4px)) {
24
+ backdrop-filter: blur(4px);
25
+ background-color: t.alpha('shadow', 0.4);
26
+ }
27
+
28
+ &.active {
29
+ opacity: 1;
30
+ visibility: visible;
31
+ }
32
+ }
33
+
34
+ // Close button for mobile drawer
35
+ .#{$prefix}-nav-close-btn {
36
+ position: absolute;
37
+ top: 12px;
38
+ right: 12px;
39
+ width: 40px;
40
+ height: 40px;
41
+ border-radius: 50%;
42
+ background-color: transparent;
43
+ border: none;
44
+ cursor: pointer;
45
+ display: none; // Hidden by default, shown on mobile
46
+ align-items: center;
47
+ justify-content: center;
48
+ color: t.color('on-surface');
49
+ z-index: 1;
50
+ -webkit-tap-highlight-color: transparent; // Removes default mobile tap highlight
51
+
52
+ &:hover {
53
+ background-color: t.alpha('on-surface', 0.05);
54
+ }
55
+
56
+ // Touch feedback state
57
+ &.active {
58
+ background-color: t.alpha('on-surface', 0.12);
59
+ }
60
+
61
+ &:focus-visible {
62
+ outline: 2px solid t.color('primary');
63
+ outline-offset: 2px;
64
+ }
65
+
66
+ svg {
67
+ width: 24px;
68
+ height: 24px;
69
+ stroke: currentColor;
70
+ }
71
+
72
+ // Ensure WCAG-compliant touch target size
73
+ @media (pointer: coarse) {
74
+ min-width: 48px;
75
+ min-height: 48px;
76
+ }
77
+ }
78
+
79
+ // Body class to prevent scrolling when drawer is open
80
+ .#{$prefix}-body-drawer-open {
81
+ overflow: hidden;
82
+
83
+ // On iOS we need to handle the body position differently
84
+ @supports (-webkit-overflow-scrolling: touch) {
85
+ position: fixed;
86
+ width: 100%;
87
+ height: 100%;
88
+ }
89
+ }
90
+
91
+ // Responsive behavior for navigation system - apply to small screens or touch devices
92
+ @media (max-width: 960px), (pointer: coarse) {
93
+ // Rail navigation
94
+ .#{$prefix}-nav--rail {
95
+ width: 56px;
96
+ z-index: v.z-index('modal') - 1;
97
+ border-right: 1px solid t.color('outline-variant');
98
+
99
+ // Hide labels on mobile
100
+ .#{$prefix}-nav-label {
101
+ display: none;
102
+ }
103
+
104
+ // Center align items
105
+ .#{$prefix}-nav-item {
106
+ justify-content: center;
107
+ height: 56px;
108
+ -webkit-tap-highlight-color: transparent; // Remove default mobile highlight
109
+
110
+ // Increase touch targets for better accessibility
111
+ @media (pointer: coarse) {
112
+ min-height: 48px;
113
+ padding: 12px 8px;
114
+ }
115
+
116
+ // Touch-friendly active state
117
+ &:active {
118
+ background-color: t.alpha('on-surface', 0.12);
119
+ }
120
+ }
121
+
122
+ // Larger icons on mobile
123
+ .#{$prefix}-nav-icon {
124
+ font-size: 1.25rem;
125
+
126
+ // Ensure icon touch target meets WCAG requirements
127
+ @media (pointer: coarse) {
128
+ min-width: 44px;
129
+ min-height: 44px;
130
+ }
131
+ }
132
+ }
133
+
134
+ // Drawer navigation
135
+ .#{$prefix}-nav--drawer {
136
+ position: fixed;
137
+ top: 0;
138
+ bottom: 0;
139
+ left: 0;
140
+ width: 280px;
141
+ max-width: 85vw;
142
+ z-index: v.z-index('modal');
143
+ background-color: t.color('surface');
144
+ box-shadow: v.elevation('level-3');
145
+ transform: translateX(-100%);
146
+ transition: transform 0.3s v.motion('easing-standard');
147
+ overflow-y: auto;
148
+ -webkit-overflow-scrolling: touch; // Smooth scrolling on iOS
149
+
150
+ &:not(.#{$prefix}-nav--hidden) {
151
+ transform: translateX(0);
152
+ }
153
+
154
+ // Optimize drawer items for touch
155
+ .#{$prefix}-nav-item {
156
+ -webkit-tap-highlight-color: transparent;
157
+
158
+ @media (pointer: coarse) {
159
+ min-height: 48px;
160
+ padding: 12px 16px;
161
+ }
162
+
163
+ // Touch feedback
164
+ &:active {
165
+ background-color: t.alpha('on-surface', 0.12);
166
+ }
167
+ }
168
+
169
+ // Support RTL languages
170
+ @include m.rtl {
171
+ left: auto;
172
+ right: 0;
173
+ transform: translateX(100%);
174
+
175
+ &:not(.#{$prefix}-nav--hidden) {
176
+ transform: translateX(0);
177
+ }
178
+ }
179
+ }
180
+ }
181
+
182
+ // Desktop behavior
183
+ @media (min-width: 961px) {
184
+ .#{$prefix}-nav--drawer {
185
+ // For desktop, transition by width instead of transform
186
+ transition: width 0.3s v.motion('easing-standard'), opacity 0.3s v.motion('easing-standard');
187
+ }
188
+
189
+ .#{$prefix}-nav-close-btn {
190
+ display: none !important; // Always hidden on desktop
191
+ }
192
+
193
+ // Hide overlay on desktop
194
+ .#{$prefix}-nav-overlay.active {
195
+ opacity: 0;
196
+ visibility: hidden;
197
+ }
198
+ }
199
+
200
+ // Safe area insets for notched devices like iPhone X and newer
201
+ @supports (padding-bottom: env(safe-area-inset-bottom)) {
202
+ .#{$prefix}-nav--drawer {
203
+ padding-top: env(safe-area-inset-top);
204
+ padding-bottom: env(safe-area-inset-bottom);
205
+ }
206
+
207
+ .#{$prefix}-nav--rail {
208
+ padding-top: env(safe-area-inset-top);
209
+ padding-bottom: env(safe-area-inset-bottom);
210
+ }
211
+
212
+ .#{$prefix}-nav--bar {
213
+ // For bottom navigation bars
214
+ &.#{$prefix}-nav--bottom {
215
+ padding-bottom: env(safe-area-inset-bottom);
216
+ }
217
+ }
218
+ }
219
+
220
+ // Gesture hints for touch interfaces
221
+ @media (pointer: coarse) {
222
+ // Add visual affordance for swipe gesture
223
+ .#{$prefix}-nav--drawer:not(.#{$prefix}-nav--hidden) {
224
+ &::after {
225
+ content: '';
226
+ position: absolute;
227
+ top: 50%;
228
+ right: 0;
229
+ width: 4px;
230
+ height: 40px;
231
+ border-radius: 4px 0 0 4px;
232
+ background-color: t.alpha('on-surface', 0.1);
233
+ transform: translateY(-50%);
234
+ opacity: 0.5;
235
+ // Hide after interaction to avoid cognitive load
236
+ animation: fadeOutAfterDelay 4s forwards;
237
+ }
238
+ }
239
+
240
+ @keyframes fadeOutAfterDelay {
241
+ 0%, 50% { opacity: 0.5; }
242
+ 100% { opacity: 0; }
243
+ }
244
+ }
@@ -0,0 +1,151 @@
1
+ // src/components/navigation/system.scss
2
+
3
+ // Navigation system specific styles
4
+ // These complement the existing navigation component styles
5
+
6
+ $nav-animation-duration: 0.2s;
7
+ $nav-animation-function: cubic-bezier(0.4, 0, 0.2, 1);
8
+
9
+ // Drawer animation states
10
+ .mtrl-nav--drawer {
11
+ transition: transform $nav-animation-duration $nav-animation-function,
12
+ opacity $nav-animation-duration $nav-animation-function;
13
+
14
+ // Hidden state (transform off-screen)
15
+ &.mtrl-nav--hidden {
16
+ transform: translateX(-100%);
17
+ opacity: 0;
18
+ pointer-events: none;
19
+ }
20
+
21
+ // Right positioned drawer
22
+ &.mtrl-nav--right {
23
+ &.mtrl-nav--hidden {
24
+ transform: translateX(100%);
25
+ }
26
+ }
27
+
28
+ // Animation classes
29
+ &.mtrl-nav--animate-in {
30
+ animation: nav-drawer-in $nav-animation-duration $nav-animation-function;
31
+ }
32
+
33
+ &.mtrl-nav--animate-out {
34
+ animation: nav-drawer-out $nav-animation-duration $nav-animation-function;
35
+ }
36
+ }
37
+
38
+ // Navigation system container (optional - for wrapping rail + drawer)
39
+ .mtrl-nav-system {
40
+ display: flex;
41
+ position: relative;
42
+
43
+ // Rail is fixed width
44
+ .mtrl-nav--rail {
45
+ flex: 0 0 auto;
46
+ }
47
+
48
+ // Drawer expands
49
+ .mtrl-nav--drawer {
50
+ flex: 1 0 auto;
51
+ }
52
+ }
53
+
54
+ // Drawer entry animation
55
+ @keyframes nav-drawer-in {
56
+ 0% {
57
+ transform: translateX(-20px);
58
+ opacity: 0;
59
+ }
60
+ 100% {
61
+ transform: translateX(0);
62
+ opacity: 1;
63
+ }
64
+ }
65
+
66
+ // Drawer exit animation
67
+ @keyframes nav-drawer-out {
68
+ 0% {
69
+ transform: translateX(0);
70
+ opacity: 1;
71
+ }
72
+ 100% {
73
+ transform: translateX(-20px);
74
+ opacity: 0;
75
+ }
76
+ }
77
+
78
+ // Right-side drawer animations
79
+ .mtrl-nav--drawer.mtrl-nav--right {
80
+ &.mtrl-nav--animate-in {
81
+ animation: nav-drawer-right-in $nav-animation-duration $nav-animation-function;
82
+ }
83
+
84
+ &.mtrl-nav--animate-out {
85
+ animation: nav-drawer-right-out $nav-animation-duration $nav-animation-function;
86
+ }
87
+ }
88
+
89
+ @keyframes nav-drawer-right-in {
90
+ 0% {
91
+ transform: translateX(20px);
92
+ opacity: 0;
93
+ }
94
+ 100% {
95
+ transform: translateX(0);
96
+ opacity: 1;
97
+ }
98
+ }
99
+
100
+ @keyframes nav-drawer-right-out {
101
+ 0% {
102
+ transform: translateX(0);
103
+ opacity: 1;
104
+ }
105
+ 100% {
106
+ transform: translateX(20px);
107
+ opacity: 0;
108
+ }
109
+ }
110
+
111
+ // Mobile optimizations
112
+ @media (max-width: 960px) {
113
+ .mtrl-nav-system {
114
+ flex-direction: column;
115
+
116
+ .mtrl-nav--rail {
117
+ // Mobile rail can be top or bottom
118
+ &.mtrl-nav--top, &.mtrl-nav--bottom {
119
+ width: 100%;
120
+ }
121
+ }
122
+
123
+ .mtrl-nav--drawer {
124
+ // Mobile drawer takes full width
125
+ width: 100%;
126
+ max-width: 100%;
127
+
128
+ &.mtrl-nav--hidden {
129
+ transform: translateY(-100%);
130
+ }
131
+
132
+ &.mtrl-nav--bottom {
133
+ &.mtrl-nav--hidden {
134
+ transform: translateY(100%);
135
+ }
136
+ }
137
+ }
138
+ }
139
+ }
140
+
141
+ // Focus and accessibility enhancements
142
+ .mtrl-nav-system {
143
+ [role="navigation"] {
144
+ &:focus-within {
145
+ outline: 2px solid var(--mtrl-primary-color, #6200ee);
146
+ outline-offset: -2px;
147
+ }
148
+ }
149
+ }
150
+
151
+ @use 'navigation-mobile'
@@ -10,12 +10,36 @@ $component: '#{base.$prefix}-switch';
10
10
  .#{$component} {
11
11
  // Base styles
12
12
  display: inline-flex;
13
- align-items: center;
13
+ flex-direction: column;
14
14
  position: relative;
15
- min-height: 32px;
15
+ width: 100%;
16
16
  padding: 4px 0;
17
17
  user-select: none;
18
18
 
19
+ // Container for switch and label
20
+ &-container {
21
+ display: flex;
22
+ flex-direction: row;
23
+ justify-content: space-between;
24
+ align-items: center;
25
+ min-height: 48px;
26
+ width: 100%;
27
+ }
28
+
29
+ // Content wrapper
30
+ &-content {
31
+ display: flex;
32
+ flex-direction: column;
33
+ flex-grow: 1;
34
+ margin-right: 12px;
35
+ }
36
+
37
+ // Label
38
+ &-label {
39
+ @include m.typography('title-medium');
40
+ color: t.color('on-surface');
41
+ }
42
+
19
43
  // Input (visually hidden but accessible)
20
44
  &-input {
21
45
  position: absolute;
@@ -46,6 +70,24 @@ $component: '#{base.$prefix}-switch';
46
70
  background-color: t.color('surface-container-highest');
47
71
  border: 2px solid t.color('outline');
48
72
  @include m.motion-transition(background-color, border-color, outline);
73
+
74
+ // Ripple effect (consistent size for both hover and press)
75
+ &::after {
76
+ content: '';
77
+ position: absolute;
78
+ width: 40px;
79
+ height: 40px;
80
+ top: -6px;
81
+ background-color: transparent;
82
+ opacity: 0;
83
+ border-radius: 50%;
84
+ z-index: 0;
85
+ transform: translateX(-6px);
86
+ transition: opacity v.motion('duration-medium1') v.motion('easing-emphasized'),
87
+ background-color v.motion('duration-medium1') v.motion('easing-emphasized'),
88
+ transform v.motion('duration-medium1') cubic-bezier(0.2, 1.4, 0.35, 1);
89
+ pointer-events: none;
90
+ }
49
91
  }
50
92
 
51
93
  // Switch thumb (slider)
@@ -58,10 +100,17 @@ $component: '#{base.$prefix}-switch';
58
100
  border-radius: 50%;
59
101
  background-color: t.color('outline');
60
102
  transform: translateX(0);
61
- transition: all v.motion('duration-short4') v.motion('easing-emphasized');
103
+ transition: transform v.motion('duration-medium1') cubic-bezier(0.2, 1.4, 0.35, 1),
104
+ background-color v.motion('duration-medium1') v.motion('easing-emphasized'),
105
+ width v.motion('duration-medium1') v.motion('easing-emphasized'),
106
+ height v.motion('duration-medium1') v.motion('easing-emphasized'),
107
+ top v.motion('duration-medium1') v.motion('easing-emphasized'),
108
+ left v.motion('duration-medium1') v.motion('easing-emphasized');
62
109
  display: flex;
63
110
  align-items: center;
64
111
  justify-content: center;
112
+ z-index: 1; /* Ensure thumb is above ripple */
113
+ pointer-events: none;
65
114
 
66
115
  // Icon inside thumb
67
116
  &-icon {
@@ -80,44 +129,25 @@ $component: '#{base.$prefix}-switch';
80
129
  }
81
130
  }
82
131
 
83
- // Label
84
- &-label {
85
- @include m.typography('body-large');
86
- margin-left: 12px;
87
- color: t.color('on-surface');
88
- }
89
-
90
- // Label position variants
91
- &--label-start {
92
- .#{$component}-label {
93
- margin-left: 0;
94
- margin-right: 12px;
95
- }
96
- }
97
-
98
- &--label-end {
99
- flex-direction: row;
100
-
101
- .#{$component}-label {
102
- margin-left: 12px;
103
- margin-right: 0;
132
+ // Supporting text
133
+ &-helper {
134
+ @include m.typography('body-medium');
135
+ color: t.color('on-surface-variant');
136
+
137
+ &--error {
138
+ color: t.color('error');
104
139
  }
105
140
  }
106
141
 
107
142
  // RTL support
108
143
  @include m.rtl {
109
- &--label-start {
110
- .#{$component}-label {
111
- margin-left: 12px;
112
- margin-right: 0;
113
- }
144
+ &-container {
145
+ flex-direction: row;
114
146
  }
115
-
116
- &--label-end {
117
- .#{$component}-label {
118
- margin-left: 0;
119
- margin-right: 12px;
120
- }
147
+
148
+ &-content {
149
+ margin-left: 12px;
150
+ margin-right: 0;
121
151
  }
122
152
 
123
153
  // Adjust thumb movement for RTL layout
@@ -131,6 +161,10 @@ $component: '#{base.$prefix}-switch';
131
161
  .#{$component}-track {
132
162
  background-color: t.color('primary');
133
163
  border-color: t.color('primary');
164
+
165
+ &::after {
166
+ transform: translateX(14px);
167
+ }
134
168
  }
135
169
 
136
170
  .#{$component}-thumb {
@@ -148,9 +182,22 @@ $component: '#{base.$prefix}-switch';
148
182
  }
149
183
  }
150
184
 
185
+ // Error state
186
+ &--error {
187
+ .#{$component}-track {
188
+ border-color: t.color('error');
189
+ }
190
+ }
191
+
151
192
  // Disabled state
152
193
  &--disabled {
153
- opacity: 0.38;
194
+ .#{$component}-track {
195
+ opacity: 0.38;
196
+ }
197
+
198
+ .#{$component}-label {
199
+ opacity: 0.38;
200
+ }
154
201
 
155
202
  // Specific styles for disabled + checked
156
203
  &.#{$component}--checked {
@@ -161,53 +208,70 @@ $component: '#{base.$prefix}-switch';
161
208
  }
162
209
 
163
210
  .#{$component}-thumb {
164
- background-color: t.color('on-primary');
165
- opacity: 1;
211
+ background-color: t.color('surface-dim');
212
+ opacity: 0.38;
166
213
  &-icon {
167
- color: t.color('outline')
214
+ color: t.color('on-surface');
168
215
  }
169
216
  }
170
217
  }
171
218
  }
172
219
 
173
- // Hover effects (state layer)
174
- &:not(&--disabled) {
175
- .#{$component}-input:hover + .#{$component}-track {
176
- &::before {
177
- content: '';
178
- position: absolute;
179
- top: -8px;
180
- left: -8px;
181
- right: -8px;
182
- bottom: -8px;
183
- background-color: t.color('on-surface');
184
- opacity: v.state('hover-state-layer-opacity');
185
- border-radius: 20px;
186
- }
220
+ // Interactive states for enabled switches
221
+ &:not(.#{$component}--disabled) {
222
+ // Remove track hover effects
223
+ .#{$component}-input:hover + .#{$component}-track::before,
224
+ .#{$component}-input:active + .#{$component}-track::before {
225
+ display: none;
187
226
  }
188
-
189
- // Active/pressed state
190
- .#{$component}-input:active + .#{$component}-track {
191
- &::before {
192
- content: '';
193
- position: absolute;
194
- top: -8px;
195
- left: -8px;
196
- right: -8px;
197
- bottom: -8px;
198
- background-color: t.color('on-surface');
227
+
228
+ // Handle hover and active states for both unchecked
229
+ &:not(.#{$component}--checked) {
230
+ .#{$component}-input:hover ~ .#{$component}-track,
231
+ .#{$component}-input:active ~ .#{$component}-track {
232
+ .#{$component}-thumb {
233
+ background-color: t.color('on-surface-variant');
234
+ }
235
+
236
+ &::after {
237
+ background-color: t.color('on-surface');
238
+ opacity: v.state('hover-state-layer-opacity');
239
+ transform: translateX(-6px);
240
+ }
241
+ }
242
+
243
+ .#{$component}-input:active ~ .#{$component}-track::after {
199
244
  opacity: v.state('pressed-state-layer-opacity');
200
- border-radius: 20px;
201
245
  }
202
246
  }
203
247
 
204
- // Hover/active states when checked
248
+ // Hover/active states for checked
205
249
  &.#{$component}--checked {
206
- .#{$component}-input:hover + .#{$component}-track::before,
207
- .#{$component}-input:active + .#{$component}-track::before {
208
- background-color: t.color('primary');
250
+ .#{$component}-input:hover ~ .#{$component}-track,
251
+ .#{$component}-input:active ~ .#{$component}-track {
252
+ &::after {
253
+ background-color: t.color('primary');
254
+ opacity: v.state('hover-state-layer-opacity');
255
+ transform: translateX(14px);
256
+ }
257
+ }
258
+
259
+ .#{$component}-input:active ~ .#{$component}-track::after {
260
+ opacity: v.state('pressed-state-layer-opacity');
209
261
  }
210
262
  }
263
+
264
+ // Expand thumb on press for both checked and unchecked
265
+ .#{$component}-input:active ~ .#{$component}-track .#{$component}-thumb {
266
+ width: 28px;
267
+ height: 28px;
268
+ top: 0;
269
+ left: 0;
270
+ }
271
+
272
+ &.#{$component}--checked .#{$component}-input:active ~ .#{$component}-track .#{$component}-thumb {
273
+ left: 0;
274
+ }
211
275
  }
212
276
 
213
277
  // Accessibility