mtrl 0.0.3 → 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 (39) hide show
  1. package/package.json +1 -1
  2. package/src/components/button/styles.scss +198 -161
  3. package/src/components/checkbox/checkbox.js +3 -2
  4. package/src/components/checkbox/styles.scss +105 -55
  5. package/src/components/container/styles.scss +65 -58
  6. package/src/components/list/constants.js +0 -5
  7. package/src/components/list/list-item.js +12 -4
  8. package/src/components/list/list.js +11 -19
  9. package/src/components/list/styles.scss +240 -11
  10. package/src/components/menu/styles.scss +37 -30
  11. package/src/components/navigation/styles.scss +406 -6
  12. package/src/components/snackbar/styles.scss +46 -17
  13. package/src/components/switch/styles.scss +93 -46
  14. package/src/components/textfield/styles.scss +351 -5
  15. package/src/core/build/_ripple.scss +79 -0
  16. package/src/core/dom/create.js +0 -1
  17. package/src/styles/abstract/_mixins.scss +9 -7
  18. package/src/styles/abstract/_theme.scss +157 -0
  19. package/src/styles/abstract/_variables.scss +72 -6
  20. package/src/styles/base/_reset.scss +86 -0
  21. package/src/styles/base/_typography.scss +155 -0
  22. package/src/styles/main.scss +104 -57
  23. package/src/styles/themes/_base-theme.scss +2 -27
  24. package/src/styles/themes/_baseline.scss +64 -39
  25. package/src/styles/utilities/_color.scss +154 -0
  26. package/src/styles/utilities/_flexbox.scss +194 -0
  27. package/src/styles/utilities/_spacing.scss +139 -0
  28. package/src/styles/utilities/_typography.scss +178 -0
  29. package/src/styles/utilities/_visibility.scss +142 -0
  30. package/src/components/list/styles/_list-item.scss +0 -142
  31. package/src/components/list/styles/_list.scss +0 -89
  32. package/src/components/list/styles/_variables.scss +0 -13
  33. package/src/components/navigation/styles/_bar.scss +0 -51
  34. package/src/components/navigation/styles/_base.scss +0 -129
  35. package/src/components/navigation/styles/_drawer.scss +0 -169
  36. package/src/components/navigation/styles/_rail.scss +0 -65
  37. package/src/components/textfield/styles/base.scss +0 -107
  38. package/src/components/textfield/styles/filled.scss +0 -58
  39. package/src/components/textfield/styles/outlined.scss +0 -66
@@ -1,8 +1,14 @@
1
- // src/components/checkbox/styles.scss
2
- @use 'sass:map';
3
- @use '../../styles/abstract/config' as c;
1
+ // src/components/checkbox/_checkbox.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}-checkbox {
8
+ $component: '#{base.$prefix}-checkbox';
9
+
10
+ .#{$component} {
11
+ // Base styles
6
12
  display: inline-flex;
7
13
  align-items: center;
8
14
  position: relative;
@@ -10,6 +16,7 @@
10
16
  padding: 4px 0;
11
17
  user-select: none;
12
18
 
19
+ // Input (visually hidden but accessible)
13
20
  &-input {
14
21
  position: absolute;
15
22
  opacity: 0;
@@ -23,22 +30,24 @@
23
30
  cursor: not-allowed;
24
31
  }
25
32
 
26
- &:focus-visible ~ .#{c.$prefix}-checkbox-icon {
27
- outline: 2px solid var(--mtrl-sys-color-primary);
33
+ // Focus indicator
34
+ &:focus-visible ~ .#{$component}-icon {
35
+ outline: 2px solid t.color('primary');
28
36
  outline-offset: 2px;
29
37
  }
30
38
  }
31
39
 
40
+ // Checkbox visual element
32
41
  &-icon {
33
42
  position: relative;
34
43
  width: 18px;
35
44
  height: 18px;
36
- border-radius: 2px;
37
- background-color: var(--mtrl-sys-color-surface-container-highest);
38
- border: 2px solid var(--mtrl-sys-color-outline);
39
- transition: background-color map.get(c.$motion, 'duration-short4') map.get(c.$motion, 'easing-standard'),
40
- border-color map.get(c.$motion, 'duration-short4') map.get(c.$motion, 'easing-standard');
45
+ border-radius: f.get-shape('tiny');
46
+ background-color: t.color('surface-container-highest');
47
+ border: 2px solid t.color('outline');
48
+ @include m.motion-transition(background-color, border-color);
41
49
 
50
+ // Check mark icon
42
51
  svg {
43
52
  position: absolute;
44
53
  top: 50%;
@@ -47,22 +56,23 @@
47
56
  height: 18px;
48
57
  transform: translate(-50%, -50%) scale(0);
49
58
  fill: currentColor;
50
- color: var(--mtrl-sys-color-on-primary);
51
- transition: transform map.get(c.$motion, 'duration-short4') map.get(c.$motion, 'easing-emphasized');
59
+ color: t.color('on-primary');
60
+ transition: transform v.motion('duration-short4') v.motion('easing-emphasized');
52
61
  }
53
62
  }
54
63
 
64
+ // Label styling
55
65
  &-label {
56
- @include c.typography('body-large');
66
+ @include m.typography('body-large');
57
67
  margin-left: 12px;
58
- color: var(--mtrl-sys-color-on-surface);
68
+ color: t.color('on-surface');
59
69
  }
60
70
 
61
71
  // Label position variants
62
72
  &--label-start {
63
73
  flex-direction: row-reverse;
64
74
 
65
- .#{c.$prefix}-checkbox-label {
75
+ .#{$component}-label {
66
76
  margin-left: 0;
67
77
  margin-right: 12px;
68
78
  }
@@ -71,42 +81,44 @@
71
81
  &--label-end {
72
82
  flex-direction: row;
73
83
 
74
- .#{c.$prefix}-checkbox-label {
84
+ .#{$component}-label {
75
85
  margin-left: 12px;
76
86
  margin-right: 0;
77
87
  }
78
88
  }
79
89
 
80
- @include c.rtl {
90
+ // RTL support
91
+ @include m.rtl {
81
92
  &--label-start {
82
- .#{c.$prefix}-checkbox-label {
93
+ .#{$component}-label {
83
94
  margin-left: 12px;
84
95
  margin-right: 0;
85
96
  }
86
97
  }
87
98
 
88
99
  &--label-end {
89
- .#{c.$prefix}-checkbox-label {
100
+ .#{$component}-label {
90
101
  margin-left: 0;
91
102
  margin-right: 12px;
92
103
  }
93
104
  }
94
105
  }
95
106
 
96
- // States
97
- .#{c.$prefix}-checkbox-input:checked ~ .#{c.$prefix}-checkbox-icon {
98
- background-color: var(--mtrl-sys-color-primary);
99
- border-color: var(--mtrl-sys-color-primary);
107
+ // Checked state
108
+ .#{$component}-input:checked ~ .#{$component}-icon {
109
+ background-color: t.color('primary');
110
+ border-color: t.color('primary');
100
111
 
101
112
  svg {
102
113
  transform: translate(-50%, -50%) scale(1);
103
114
  }
104
115
  }
105
116
 
117
+ // Indeterminate state
106
118
  &--indeterminate {
107
- .#{c.$prefix}-checkbox-icon {
108
- background-color: var(--mtrl-sys-color-primary);
109
- border-color: var(--mtrl-sys-color-primary);
119
+ .#{$component}-icon {
120
+ background-color: t.color('primary');
121
+ border-color: t.color('primary');
110
122
 
111
123
  &::after {
112
124
  content: '';
@@ -116,7 +128,7 @@
116
128
  transform: translate(-50%, -50%);
117
129
  width: 10px;
118
130
  height: 2px;
119
- background-color: var(--mtrl-sys-color-on-primary);
131
+ background-color: t.color('on-primary');
120
132
  }
121
133
 
122
134
  svg {
@@ -125,59 +137,97 @@
125
137
  }
126
138
  }
127
139
 
140
+ // Disabled state
128
141
  &--disabled {
129
- opacity: .5;
142
+ opacity: 0.38;
143
+
144
+ .#{$component}-input {
145
+ cursor: not-allowed;
146
+ }
130
147
  }
131
148
 
132
- // Variants
149
+ // Outlined variant
133
150
  &--outlined {
134
- .#{c.$prefix}-checkbox-icon {
151
+ .#{$component}-icon {
135
152
  background-color: transparent;
136
153
  }
137
154
 
138
- .#{c.$prefix}-checkbox-input:checked ~ .#{c.$prefix}-checkbox-icon {
155
+ .#{$component}-input:checked ~ .#{$component}-icon {
139
156
  background-color: transparent;
140
- border-color: var(--mtrl-sys-color-primary);
157
+ border-color: t.color('primary');
141
158
 
142
159
  svg {
143
- color: var(--mtrl-sys-color-primary);
160
+ color: t.color('primary');
144
161
  }
145
162
  }
146
163
 
147
- &.#{c.$prefix}-checkbox--indeterminate {
148
- .#{c.$prefix}-checkbox-icon::after {
149
- background-color: var(--mtrl-sys-color-primary);
164
+ &.#{$component}--indeterminate {
165
+ .#{$component}-icon::after {
166
+ background-color: t.color('primary');
150
167
  }
151
168
  }
152
169
  }
153
170
 
154
- // Hover effects
171
+ // State layer effects
155
172
  &:not(&--disabled) {
156
- .#{c.$prefix}-checkbox-input:hover ~ .#{c.$prefix}-checkbox-icon {
157
- &::before {
158
- content: '';
159
- position: absolute;
160
- top: -12px;
161
- left: -12px;
162
- right: -12px;
163
- bottom: -12px;
164
- background-color: var(--mtrl-sys-color-on-surface);
165
- opacity: 0.08;
166
- border-radius: 50%;
167
- }
173
+ // Common state layer structure
174
+ .#{$component}-input:hover ~ .#{$component}-icon::before,
175
+ .#{$component}-input:active ~ .#{$component}-icon::before {
176
+ content: '';
177
+ position: absolute;
178
+ top: -12px;
179
+ left: -12px;
180
+ right: -12px;
181
+ bottom: -12px;
182
+ border-radius: 50%;
183
+ background-color: t.color('on-surface');
184
+ }
185
+
186
+ // Hover state
187
+ .#{$component}-input:hover ~ .#{$component}-icon::before {
188
+ opacity: v.state('hover-state-layer-opacity');
189
+
190
+ }
191
+
192
+ // Hover checked state
193
+ .#{$component}-input:checked:hover ~ .#{$component}-icon::before {
194
+ opacity: v.state('pressed-state-layer-opacity');
195
+ background-color: t.color('primary');
196
+ }
197
+ // Active/pressed state
198
+ .#{$component}-input:active ~ .#{$component}-icon::before {
199
+ opacity: v.state('pressed-state-layer-opacity');
200
+ background-color: t.color('on-surface');
168
201
  }
169
202
  }
170
203
 
171
- @include c.reduced-motion {
172
- .#{c.$prefix}-checkbox-icon,
173
- .#{c.$prefix}-checkbox-icon svg {
204
+ // Accessibility
205
+ @include m.reduced-motion {
206
+ .#{$component}-icon,
207
+ .#{$component}-icon svg {
174
208
  transition: none;
175
209
  }
176
210
  }
177
211
 
178
- @include c.high-contrast {
179
- .#{c.$prefix}-checkbox-icon {
212
+ @include m.high-contrast {
213
+ .#{$component}-icon {
180
214
  border-width: 2px;
181
215
  }
182
216
  }
217
+
218
+ // Touch target
219
+ // @include m.touch-target;
220
+ }
221
+
222
+ // Checkbox group layout
223
+ .#{$component}-group {
224
+ display: flex;
225
+ flex-direction: column;
226
+ gap: 8px;
227
+
228
+ &--horizontal {
229
+ flex-direction: row;
230
+ gap: 16px;
231
+ flex-wrap: wrap;
232
+ }
183
233
  }
@@ -1,59 +1,66 @@
1
- // src/components/container/styles.scss
2
- @use 'sass:map';
3
- @use '../../styles/abstract/config' as c;
4
-
5
- .#{c.$prefix}-container {
6
- @include c.shape('medium');
7
- @include c.motion-transition(
8
- background-color,
9
- box-shadow
10
- );
11
-
12
- padding: 16px;
13
- background-color: var(--mtrl-sys-color-surface-container);
14
-
15
- &--low {
16
- background-color: var(--mtrl-sys-color-surface-container-low);
17
- }
18
-
19
- &--lowest {
20
- background-color: var(--mtrl-sys-color-surface-container-lowest);
21
- }
22
-
23
- &--high {
24
- background-color: var(--mtrl-sys-color-surface-container-high);
25
- }
26
-
27
- &--highest {
28
- background-color: var(--mtrl-sys-color-surface-container-highest);
29
- }
30
-
31
- // Elevation variants
32
- &--elevation-0 {
33
- @include c.elevation(0);
34
- }
35
-
36
- &--elevation-1 {
37
- @include c.elevation(1);
38
- }
39
-
40
- &--elevation-2 {
41
- @include c.elevation(2);
42
- }
43
-
44
- &--elevation-3 {
45
- @include c.elevation(3);
46
- }
47
-
48
- &--elevation-4 {
49
- @include c.elevation(4);
50
- }
51
-
52
- @include c.reduced-motion {
53
- transition: none;
54
- }
55
-
56
- @include c.high-contrast {
57
- border: 1px solid currentColor;
58
- }
1
+ // src/components/container/_container.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;
7
+
8
+ $component: '#{base.$prefix}-container';
9
+
10
+ .#{$component} {
11
+ @include m.shape('medium');
12
+ @include m.motion-transition(
13
+ background-color,
14
+ box-shadow
15
+ );
16
+
17
+ padding: 16px;
18
+ background-color: t.color('surface-container');
19
+
20
+ // Surface container variants
21
+ &--low {
22
+ background-color: t.color('surface-container-low');
23
+ }
24
+
25
+ &--lowest {
26
+ background-color: t.color('surface-container-lowest');
27
+ }
28
+
29
+ &--high {
30
+ background-color: t.color('surface-container-high');
31
+ }
32
+
33
+ &--highest {
34
+ background-color: t.color('surface-container-highest');
35
+ }
36
+
37
+ // Elevation variants
38
+ &--elevation-0 {
39
+ @include m.elevation(0);
40
+ }
41
+
42
+ &--elevation-1 {
43
+ @include m.elevation(1);
44
+ }
45
+
46
+ &--elevation-2 {
47
+ @include m.elevation(2);
48
+ }
49
+
50
+ &--elevation-3 {
51
+ @include m.elevation(3);
52
+ }
53
+
54
+ &--elevation-4 {
55
+ @include m.elevation(4);
56
+ }
57
+
58
+ // Accessibility
59
+ @include m.reduced-motion {
60
+ transition: none;
61
+ }
62
+
63
+ @include m.high-contrast {
64
+ border: 1px solid currentColor;
65
+ }
59
66
  }
@@ -30,11 +30,6 @@ export const LIST_CLASSES = {
30
30
  SECTION_TITLE: 'list-section-title'
31
31
  }
32
32
 
33
- export const LIST_ITEM_LAYOUTS = {
34
- HORIZONTAL: 'horizontal',
35
- VERTICAL: 'vertical'
36
- }
37
-
38
33
  /**
39
34
  * List configuration schema
40
35
  */
@@ -1,9 +1,17 @@
1
1
  // src/components/list/list-item.js
2
+
2
3
  import { PREFIX } from '../../core/config'
3
4
  import { pipe } from '../../core/compose'
4
5
  import { createBase, withElement } from '../../core/compose/component'
5
6
  import { withEvents, withDisabled } from '../../core/compose/features'
6
- import { LIST_ITEM_LAYOUTS } from './constants'
7
+
8
+ /**
9
+ * Supported list item layouts
10
+ */
11
+ export const LIST_ITEM_LAYOUTS = {
12
+ HORIZONTAL: 'horizontal', // Default horizontal layout
13
+ VERTICAL: 'vertical' // Stacked layout with vertical alignment
14
+ }
7
15
 
8
16
  /**
9
17
  * Creates a DOM element with optional class and content
@@ -48,7 +56,8 @@ const createListItem = (config = {}) => {
48
56
  }
49
57
 
50
58
  const createContent = (component) => {
51
- const { element, prefix } = component
59
+ const { element } = component
60
+ const { prefix } = baseConfig
52
61
  const isVertical = config.layout === LIST_ITEM_LAYOUTS.VERTICAL
53
62
 
54
63
  // Create content container
@@ -128,8 +137,7 @@ const createListItem = (config = {}) => {
128
137
  tag: 'div',
129
138
  role: config.role || 'listitem',
130
139
  componentName: 'list-item',
131
- // Ensure that every list item includes the prefix-based class
132
- className: `${baseConfig.prefix}-list-item ${config.layout === LIST_ITEM_LAYOUTS.VERTICAL ? 'vertical' : ''} ${config.class || ''}`.trim()
140
+ className: `${config.layout === LIST_ITEM_LAYOUTS.VERTICAL ? 'vertical' : ''} ${config.class || ''}`
133
141
  }),
134
142
  withDisabled(),
135
143
  createContent
@@ -1,3 +1,5 @@
1
+ // src/components/list/list.js
2
+
1
3
  import { PREFIX } from '../../core/config'
2
4
  import { pipe } from '../../core/compose'
3
5
  import { createBase, withElement } from '../../core/compose/component'
@@ -54,29 +56,29 @@ const createList = (config = {}) => {
54
56
  const focusedItem = document.activeElement
55
57
  if (!focusedItem?.classList.contains(`${prefix}-list-item`)) return
56
58
 
57
- const allItems = Array.from(element.querySelectorAll(`.${prefix}-list-item`))
58
- const currentIndex = allItems.indexOf(focusedItem)
59
+ const items = Array.from(element.querySelectorAll(`.${prefix}-list-item`))
60
+ const currentIndex = items.indexOf(focusedItem)
59
61
 
60
62
  switch (event.key) {
61
63
  case 'ArrowDown':
62
64
  case 'ArrowRight':
63
65
  event.preventDefault()
64
- const nextItem = allItems[currentIndex + 1]
66
+ const nextItem = items[currentIndex + 1]
65
67
  if (nextItem) nextItem.focus()
66
68
  break
67
69
  case 'ArrowUp':
68
70
  case 'ArrowLeft':
69
71
  event.preventDefault()
70
- const prevItem = allItems[currentIndex - 1]
72
+ const prevItem = items[currentIndex - 1]
71
73
  if (prevItem) prevItem.focus()
72
74
  break
73
75
  case 'Home':
74
76
  event.preventDefault()
75
- allItems[0]?.focus()
77
+ items[0]?.focus()
76
78
  break
77
79
  case 'End':
78
80
  event.preventDefault()
79
- allItems[allItems.length - 1]?.focus()
81
+ items[items.length - 1]?.focus()
80
82
  break
81
83
  case ' ':
82
84
  case 'Enter':
@@ -185,7 +187,7 @@ const createList = (config = {}) => {
185
187
 
186
188
  element.addEventListener('keydown', handleKeyDown)
187
189
 
188
- // Clean up lifecycle if defined
190
+ // Clean up
189
191
  if (component.lifecycle) {
190
192
  const originalDestroy = component.lifecycle.destroy
191
193
  component.lifecycle.destroy = () => {
@@ -246,14 +248,12 @@ const createList = (config = {}) => {
246
248
  }
247
249
  }
248
250
 
249
- const list = pipe(
251
+ return pipe(
250
252
  createBase,
251
253
  withEvents(),
252
254
  withElement({
253
255
  tag: 'div',
254
- // Use role "list" for default (non-selectable) lists,
255
- // and "listbox" for interactive, selectable types.
256
- role: (!config.type || config.type === LIST_TYPES.DEFAULT) ? 'list' : 'listbox',
256
+ role: config.type === LIST_TYPES.DEFAULT ? 'list' : 'listbox',
257
257
  'aria-multiselectable': config.type === LIST_TYPES.MULTI_SELECT ? 'true' : undefined,
258
258
  componentName: LIST_CLASSES.ROOT,
259
259
  className: config.class
@@ -262,14 +262,6 @@ const createList = (config = {}) => {
262
262
  withLifecycle(),
263
263
  createContent
264
264
  )(baseConfig)
265
-
266
- // Ensure that for default lists, the role is correctly "list"
267
- if (!config.type || config.type === LIST_TYPES.DEFAULT) {
268
- list.element.setAttribute('role', 'list')
269
- }
270
- // Expose the prefix on the returned component for testing purposes.
271
- list.prefix = baseConfig.prefix
272
- return list
273
265
  }
274
266
 
275
267
  export default createList