mtrl 0.0.2 → 0.1.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 (52) hide show
  1. package/package.json +2 -2
  2. package/src/components/button/styles.scss +198 -161
  3. package/src/components/checkbox/checkbox.js +4 -3
  4. package/src/components/checkbox/styles.scss +105 -55
  5. package/src/components/container/styles.scss +65 -58
  6. package/src/components/list/styles.scss +240 -11
  7. package/src/components/menu/features/items-manager.js +5 -1
  8. package/src/components/menu/styles.scss +37 -30
  9. package/src/components/navigation/constants.js +19 -54
  10. package/src/components/navigation/styles.scss +406 -6
  11. package/src/components/snackbar/styles.scss +46 -17
  12. package/src/components/switch/styles.scss +104 -40
  13. package/src/components/switch/switch.js +1 -1
  14. package/src/components/textfield/styles.scss +351 -5
  15. package/src/core/build/_ripple.scss +79 -0
  16. package/src/core/compose/features/disabled.js +27 -7
  17. package/src/core/compose/features/input.js +9 -1
  18. package/src/core/compose/features/textinput.js +16 -20
  19. package/src/core/dom/create.js +0 -1
  20. package/src/styles/abstract/_mixins.scss +9 -7
  21. package/src/styles/abstract/_theme.scss +157 -0
  22. package/src/styles/abstract/_variables.scss +72 -6
  23. package/src/styles/base/_reset.scss +86 -0
  24. package/src/styles/base/_typography.scss +155 -0
  25. package/src/styles/main.scss +104 -57
  26. package/src/styles/themes/_base-theme.scss +2 -27
  27. package/src/styles/themes/_baseline.scss +64 -39
  28. package/src/styles/utilities/_color.scss +154 -0
  29. package/src/styles/utilities/_flexbox.scss +194 -0
  30. package/src/styles/utilities/_spacing.scss +139 -0
  31. package/src/styles/utilities/_typography.scss +178 -0
  32. package/src/styles/utilities/_visibility.scss +142 -0
  33. package/test/components/button.test.js +46 -34
  34. package/test/components/checkbox.test.js +238 -0
  35. package/test/components/list.test.js +105 -0
  36. package/test/components/menu.test.js +385 -0
  37. package/test/components/navigation.test.js +227 -0
  38. package/test/components/snackbar.test.js +234 -0
  39. package/test/components/switch.test.js +186 -0
  40. package/test/components/textfield.test.js +314 -0
  41. package/test/core/ripple.test.js +21 -120
  42. package/test/setup.js +152 -239
  43. package/src/components/list/styles/_list-item.scss +0 -142
  44. package/src/components/list/styles/_list.scss +0 -89
  45. package/src/components/list/styles/_variables.scss +0 -13
  46. package/src/components/navigation/styles/_bar.scss +0 -51
  47. package/src/components/navigation/styles/_base.scss +0 -129
  48. package/src/components/navigation/styles/_drawer.scss +0 -169
  49. package/src/components/navigation/styles/_rail.scss +0 -65
  50. package/src/components/textfield/styles/base.scss +0 -107
  51. package/src/components/textfield/styles/filled.scss +0 -58
  52. package/src/components/textfield/styles/outlined.scss +0 -66
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mtrl",
3
- "version": "0.0.2",
3
+ "version": "0.1.0",
4
4
  "description": "A functional JavaScript component library with composable architecture based on Material Design 3",
5
5
  "keywords": ["component", "library", "ui", "user interface", "functional", "composable"],
6
6
  "main": "index.js",
@@ -16,5 +16,5 @@
16
16
  },
17
17
 
18
18
  "author": "floor",
19
- "license": "GPL-3.0"
19
+ "license": "MIT License"
20
20
  }
@@ -1,231 +1,268 @@
1
- // src/components/button/styles.scss
2
- @use 'sass:map';
3
- @use '../../styles/abstract/config' as c;
1
+ // src/components/button/_button.scss
2
+ @use '../../styles/abstract/base' as base;
3
+ @use '../../styles/abstract/variables' as v;
4
+ @use '../../styles/abstract/functions' as f;
5
+ @use '../../styles/abstract/mixins' as m;
6
+ @use '../../styles/abstract/theme' as t;
4
7
 
5
- .#{c.$prefix}-button {
8
+ $component: '#{base.$prefix}-button';
9
+
10
+ .#{$component} {
11
+ // @include m.touch-target;
6
12
  // Base styles
7
- @include c.typography('label-large');
8
- @include c.flex-center;
9
- @include c.shape('full');
10
- @include c.motion-transition(
11
- background-color,
12
- box-shadow,
13
- color,
14
- border-color
15
- );
16
-
17
- min-width: 40px;
18
- height: 40px;
19
- padding: 0 24px;
13
+ position: relative;
14
+ display: inline-flex;
15
+ align-items: center;
16
+ justify-content: center;
17
+ min-width: v.button('min-width');
18
+ height: v.button('height');
19
+ padding: 0 v.button('padding-horizontal');
20
20
  border: none;
21
+ border-radius: v.button('border-radius');
22
+ background-color: transparent;
23
+ color: inherit;
24
+ font: inherit;
25
+ text-decoration: none;
21
26
  cursor: pointer;
22
27
  user-select: none;
28
+ vertical-align: middle;
29
+ appearance: none;
30
+ overflow: hidden;
31
+
32
+ // Typography
33
+ @include m.typography('label-large');
34
+
35
+ // Transition
36
+ @include m.motion-transition(
37
+ background-color,
38
+ color,
39
+ box-shadow
40
+ );
41
+
42
+ // Focus styles
43
+ &:focus {
44
+ outline: none;
45
+ }
23
46
 
24
- // Label styles
25
- &-label {
26
- @include c.truncate;
47
+ &:focus-visible {
48
+ outline: 2px solid t.color('outline');
49
+ outline-offset: 2px;
27
50
  }
28
51
 
29
- // Icon styles
52
+ // Interactive states
53
+ &:disabled {
54
+ pointer-events: none;
55
+ opacity: 0.85;
56
+ }
57
+
58
+ // Ensure proper layout with icons
30
59
  &-icon {
31
- @include c.flex-center;
32
- margin-right: 8px;
33
- font-size: 18px;
34
- background-color: transparent;
35
- @include c.rtl {
36
- margin-right: 0;
60
+ display: inline-flex;
61
+ align-items: center;
62
+ justify-content: center;
63
+ width: 18px;
64
+ height: 18px;
65
+
66
+ svg {
67
+ width: 18px;
68
+ height: 18px;
69
+ }
70
+
71
+ + .#{$component}-text {
37
72
  margin-left: 8px;
38
73
  }
39
74
  }
75
+
76
+ &-text {
77
+ // Text truncation for long button labels
78
+ @include m.truncate;
79
+
80
+ .#{$component}--icon-only & {
81
+ @include m.visually-hidden;
82
+ }
83
+ }
84
+
85
+ &--circular {
86
+ border-radius: 50%;
87
+ padding: 8px;
88
+ min-width: unset;
89
+ width: 40px;
90
+ height: 40px;
91
+ display: inline-flex;
92
+ align-items: center;
93
+ justify-content: center;
94
+
95
+ .#{$component}-icon {
96
+ margin: 0;
97
+ }
98
+
99
+ .#{$component}--small {
100
+ width: 32px;
101
+ height: 32px;
102
+ }
103
+
104
+ .#{$component}--large {
105
+ width: 48px;
106
+ height: 48px;
107
+ }
108
+ }
109
+
110
+ // Ripple container
111
+ .ripple {
112
+ position: absolute;
113
+ border-radius: 50%;
114
+ transform: scale(0);
115
+ pointer-events: none;
116
+ background-color: currentColor;
117
+ opacity: 0.12;
118
+ }
119
+
120
+ &--disabled {
121
+ opacity: 0.38
122
+
123
+ }
40
124
 
41
125
  // Variants
42
126
  &--filled {
43
- background-color: var(--mtrl-sys-color-primary);
44
- color: var(--mtrl-sys-color-on-primary);
45
- @include c.elevation(0);
127
+ background-color: t.color('primary');
128
+ color: t.color('on-primary');
46
129
 
47
130
  &:hover {
48
- @include c.elevation(1);
49
- @include c.state-layer(var(--mtrl-sys-color-on-primary), 'hover');
50
- }
51
-
52
- &:focus {
53
- @include c.state-layer(var(--mtrl-sys-color-on-primary), 'focus');
131
+ @include m.state-layer(t.color('on-primary'), 'hover');
132
+ @include m.elevation(1);
54
133
  }
55
134
 
56
135
  &:active {
57
- @include c.state-layer(var(--mtrl-sys-color-on-primary), 'pressed');
136
+ @include m.state-layer(t.color('on-primary'), 'pressed');
137
+ @include m.elevation(0);
58
138
  }
139
+
140
+ // &:disabled {
141
+ // background-color: t.alpha('on-surface', 0.12);
142
+ // color: t.alpha('on-surface', 0.38);
143
+ // }
59
144
  }
60
145
 
61
- &--tonal {
62
- background-color: var(--mtrl-sys-color-secondary-container);
63
- color: var(--mtrl-sys-color-on-secondary-container);
146
+ // Elevated button variant
147
+ &--elevated {
148
+ background-color: t.color('surface-container-low');
149
+ color: t.color('primary');
150
+ @include m.elevation(1);
64
151
 
65
152
  &:hover {
66
- @include c.state-layer(var(--mtrl-sys-color-on-secondary-container), 'hover');
153
+ @include m.state-layer(t.color('primary'), 'hover');
154
+ @include m.elevation(2);
67
155
  }
68
156
 
69
- &:focus {
70
- @include c.state-layer(var(--mtrl-sys-color-on-secondary-container), 'focus');
157
+ &:active {
158
+ @include m.state-layer(t.color('primary'), 'pressed');
159
+ @include m.elevation(1);
71
160
  }
72
161
 
73
- &:active {
74
- @include c.state-layer(var(--mtrl-sys-color-on-secondary-container), 'pressed');
162
+ &:disabled {
163
+ @include m.elevation(0);
164
+ background-color: t.alpha('on-surface', 0.12);
165
+ color: t.alpha('on-surface', 0.38);
166
+ box-shadow: none;
75
167
  }
168
+
169
+ // Handle icon color in elevated button
170
+ .#{$component}-icon {
171
+ color: t.color('primary');
172
+ }
173
+
174
+ // &:disabled .#{$component}-icon {
175
+ // color: t.alpha('on-surface', 0.38);
176
+ // }
76
177
  }
77
178
 
78
- &--outlined {
79
- border: 1px solid var(--mtrl-sys-color-outline);
80
- color: var(--mtrl-sys-color-primary);
179
+ &--tonal {
180
+ background-color: t.color('secondary-container');
181
+ color: t.color('on-secondary-container');
81
182
 
82
183
  &:hover {
83
- @include c.state-layer(var(--mtrl-sys-color-primary), 'hover');
84
- }
85
-
86
- &:focus {
87
- @include c.state-layer(var(--mtrl-sys-color-primary), 'focus');
88
- border-color: var(--mtrl-sys-color-primary);
184
+ @include m.state-layer(t.color('on-secondary-container'), 'hover');
185
+ @include m.elevation(1);
89
186
  }
90
187
 
91
188
  &:active {
92
- @include c.state-layer(var(--mtrl-sys-color-primary), 'pressed');
189
+ @include m.state-layer(t.color('on-secondary-container'), 'pressed');
190
+ @include m.elevation(0);
93
191
  }
192
+
193
+ // &:disabled {
194
+ // background-color: t.alpha('on-surface', 0.12);
195
+ // color: t.alpha('on-surface', 0.38);
196
+ // }
94
197
  }
95
198
 
96
- // Variants (adding elevated and text)
97
- &--elevated {
98
- background-color: var(--mtrl-sys-color-surface-container-low);
99
- color: var(--mtrl-sys-color-primary);
100
- // Reduced from elevation(1) to a more subtle custom shadow
101
- box-shadow: 0px 1px 2px 0px rgba(0, 0, 0, 0.2),
102
- 0px 1px 3px 1px rgba(0, 0, 0, 0.10);
199
+ &--outlined {
200
+ border: 1px solid t.color('outline');
201
+ color: t.color('primary');
103
202
 
104
203
  &:hover {
105
- // Slightly increased elevation on hover, but still subtle
106
- box-shadow: 0px 1px 2px 1px rgba(0, 0, 0, 0.2),
107
- 0px 2px 6px 2px rgba(0, 0, 0, 0.10);
108
- @include c.state-layer(var(--mtrl-sys-color-primary), 'hover');
109
- }
110
-
111
- &:focus {
112
- @include c.state-layer(var(--mtrl-sys-color-primary), 'focus');
204
+ @include m.state-layer(t.color('primary'), 'hover');
205
+ background-color: t.alpha('primary', 0.08);
113
206
  }
114
207
 
115
208
  &:active {
116
- // Return to default elevation on press
117
- box-shadow: 0px 1px 2px 0px rgba(0, 0, 0, 0.2),
118
- 0px 1px 3px 1px rgba(0, 0, 0, 0.10);
119
- @include c.state-layer(var(--mtrl-sys-color-primary), 'pressed');
209
+ @include m.state-layer(t.color('primary'), 'pressed');
120
210
  }
121
211
 
122
- &.#{c.$prefix}-button--disabled {
123
- box-shadow: none;
124
- background-color: rgba(var(--mtrl-sys-color-on-surface-rgb), 0.12);
125
- color: rgba(var(--mtrl-sys-color-on-surface-rgb), 0.38);
126
- }
212
+ // &:disabled {
213
+ // border-color: t.alpha('on-surface', 0.12);
214
+ // color: t.alpha('on-surface', 0.38);
215
+ // }
127
216
  }
128
-
217
+
129
218
  &--text {
130
- background-color: transparent;
131
- color: var(--mtrl-sys-color-primary);
132
- padding: 0 12px;
133
- min-width: 48px;
219
+ min-width: 0;
220
+ padding: 0 v.button('padding-horizontal-small');
221
+ color: t.color('primary');
134
222
 
135
223
  &:hover {
136
- @include c.state-layer(var(--mtrl-sys-color-primary), 'hover');
137
- }
138
-
139
- &:focus {
140
- @include c.state-layer(var(--mtrl-sys-color-primary), 'focus');
224
+ background-color: t.alpha('primary', 0.08);
141
225
  }
142
226
 
143
227
  &:active {
144
- @include c.state-layer(var(--mtrl-sys-color-primary), 'pressed');
228
+ background-color: t.alpha('primary', 0.12);
145
229
  }
146
230
 
147
- // Adjust spacing when icon is present
148
- .#{c.$prefix}-button-icon {
149
- margin-right: 8px;
150
-
151
- @include c.rtl {
152
- margin-right: 0;
153
- margin-left: 8px;
154
- }
155
- }
156
-
157
- &.#{c.$prefix}-button--disabled {
158
- color: rgba(var(--mtrl-sys-color-on-surface-rgb), 0.38);
159
- }
160
- }
161
-
162
- &--icon {
163
- background-color: transparent;
164
- }
165
-
166
- &--circular {
167
- border-radius: 50%;
168
- padding: 8px;
169
- min-width: unset;
170
- width: 40px;
171
- height: 40px;
172
- display: inline-flex;
173
- align-items: center;
174
- justify-content: center;
175
-
176
- .#{c.$prefix}-button-icon {
177
- margin: 0;
178
- }
179
-
180
- &.#{c.$prefix}-button--small {
181
- width: 32px;
182
- height: 32px;
183
- }
184
-
185
- &.#{c.$prefix}-button--large {
186
- width: 48px;
187
- height: 48px;
188
- }
189
- }
190
-
191
- // States
192
- &--disabled {
193
- opacity: 0.38;
194
- pointer-events: none;
195
- cursor: default;
196
-
197
- &.#{c.$prefix}-button--outlined {
198
- border-color: rgba(var(--mtrl-sys-color-on-surface-rgb), 0.12);
231
+ &:disabled {
232
+ color: t.alpha('on-surface', 0.38);
199
233
  }
200
234
  }
201
235
 
202
- // Sizes
236
+ // Size variants
203
237
  &--small {
204
238
  height: 32px;
205
- padding: 0 16px;
206
-
207
- .#{c.$prefix}-button-icon {
208
- font-size: 16px;
209
- }
239
+ min-width: 48px;
240
+ padding: 0 v.button('padding-horizontal-small');
241
+ font-size: 13px;
210
242
  }
211
243
 
212
244
  &--large {
213
245
  height: 48px;
246
+ min-width: 78px;
214
247
  padding: 0 32px;
215
-
216
- .#{c.$prefix}-button-icon {
217
- font-size: 20px;
218
- }
248
+ font-size: 16px;
219
249
  }
220
250
 
221
- // Accessibility
222
- @include c.focus-ring();
223
-
224
- @include c.reduced-motion {
225
- transition: none;
226
- }
227
-
228
- @include c.high-contrast {
229
- border: 1px solid currentColor;
251
+ // Special case for icon-only buttons
252
+ &--icon-only {
253
+ min-width: v.button('height');
254
+ width: v.button('height');
255
+ padding: 0;
256
+ border-radius: 50%;
257
+
258
+ &.#{$component}--small {
259
+ min-width: 32px;
260
+ width: 32px;
261
+ }
262
+
263
+ &.#{$component}--large {
264
+ min-width: 48px;
265
+ width: 48px;
266
+ }
230
267
  }
231
268
  }
@@ -22,10 +22,11 @@ const withCheckIcon = (config) => (component) => {
22
22
  const icon = document.createElement('span')
23
23
  icon.className = `${config.prefix}-checkbox-icon`
24
24
  icon.innerHTML = `
25
- <svg viewBox="0 0 24 24" aria-hidden="true">
26
- <path d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41L9 16.17z"></path>
25
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="20" height="20" fill="currentColor">
26
+ <path d="M9.55 14.6L6.35 11.4l-1.9 1.9L9.55 18.4l10.9-10.9-1.9-1.9z"/>
27
27
  </svg>
28
28
  `
29
+
29
30
  component.element.appendChild(icon)
30
31
  return component
31
32
  }
@@ -82,7 +83,7 @@ const createCheckbox = (config = {}) => {
82
83
  withCheckIcon(baseConfig),
83
84
  withTextLabel(baseConfig),
84
85
  enhancedWithCheckable,
85
- withDisabled(),
86
+ withDisabled(baseConfig), // Pass the baseConfig to withDisabled
86
87
  withLifecycle(),
87
88
  comp => withAPI({
88
89
  disabled: comp.disabled,