noph-ui 0.49.0 → 0.51.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 (104) hide show
  1. package/dist/app-bar/AppBar.svelte +68 -26
  2. package/dist/autocomplete/AutoComplete.svelte +3 -4
  3. package/dist/autocomplete/types.d.ts +3 -3
  4. package/dist/badge/Badge.svelte +1 -1
  5. package/dist/button/Button.svelte +7 -15
  6. package/dist/button/ButtonGroup.svelte +2 -2
  7. package/dist/button/IconButton.svelte +6 -41
  8. package/dist/button/SegmentedButton.svelte +9 -9
  9. package/dist/button/SplitButton.svelte +2 -2
  10. package/dist/button/types.d.ts +13 -11
  11. package/dist/card/Card.svelte +16 -7
  12. package/dist/card/types.d.ts +4 -4
  13. package/dist/carousel/Carousel.svelte +16 -12
  14. package/dist/carousel/CarouselItem.svelte +24 -15
  15. package/dist/carousel/carouselMorph.js +1 -1
  16. package/dist/carousel/types.d.ts +5 -4
  17. package/dist/checkbox/Checkbox.svelte +1 -1
  18. package/dist/checkbox/types.d.ts +2 -3
  19. package/dist/chip/AssistChip.svelte +1 -1
  20. package/dist/chip/ChipSet.svelte +1 -1
  21. package/dist/chip/FilterChip.svelte +1 -1
  22. package/dist/chip/InputChip.svelte +1 -1
  23. package/dist/chip/SuggestionChip.svelte +3 -3
  24. package/dist/chip/types.d.ts +5 -8
  25. package/dist/date-picker/Calendar.svelte +8 -7
  26. package/dist/date-picker/DatePickerDialog.svelte +2 -1
  27. package/dist/date-picker/DateRangePicker.svelte +3 -6
  28. package/dist/date-picker/DockedDatePicker.svelte +1 -1
  29. package/dist/date-picker/DockedDateTimePicker.svelte +1 -1
  30. package/dist/date-picker/SelectionList.svelte +2 -2
  31. package/dist/date-picker/SelectionList.svelte.d.ts +1 -1
  32. package/dist/date-picker/TimeColumn.svelte +2 -2
  33. package/dist/date-picker/TimeColumn.svelte.d.ts +1 -1
  34. package/dist/date-picker/YearGrid.svelte +1 -1
  35. package/dist/date-picker/internalTypes.d.ts +7 -0
  36. package/dist/date-picker/internalTypes.js +1 -0
  37. package/dist/date-picker/types.d.ts +10 -44
  38. package/dist/dialog/Dialog.svelte +3 -4
  39. package/dist/divider/Divider.svelte +1 -1
  40. package/dist/fab/ExtendedFab.svelte +5 -9
  41. package/dist/fab/Fab.svelte +5 -10
  42. package/dist/fab/FabMenu.svelte +7 -7
  43. package/dist/icons/Icon.svelte +1 -1
  44. package/dist/internal/elementTag.d.ts +26 -0
  45. package/dist/internal/elementTag.js +23 -0
  46. package/dist/internal/fieldTypes.d.ts +24 -0
  47. package/dist/internal/fieldTypes.js +1 -0
  48. package/dist/list/Item.svelte +17 -6
  49. package/dist/list/List.svelte +1 -1
  50. package/dist/list/ListItem.svelte +1 -1
  51. package/dist/list/types.d.ts +7 -6
  52. package/dist/loading-indicator/LoadingIndicator.svelte +2 -2
  53. package/dist/menu/Menu.svelte +1 -1
  54. package/dist/menu/MenuItem.svelte +8 -4
  55. package/dist/navigation-bar/NavigationBar.svelte +1 -1
  56. package/dist/navigation-bar/NavigationBarItem.svelte +6 -7
  57. package/dist/navigation-drawer/NavigationDrawer.svelte +9 -7
  58. package/dist/navigation-drawer/NavigationDrawerItem.svelte +2 -3
  59. package/dist/navigation-drawer/types.d.ts +6 -2
  60. package/dist/navigation-rail/NavigationRail.svelte +1 -1
  61. package/dist/navigation-rail/NavigationRailItem.svelte +3 -3
  62. package/dist/progress/CircularProgress.svelte +10 -4
  63. package/dist/progress/LinearProgress.svelte +2 -2
  64. package/dist/radio/Radio.svelte +2 -2
  65. package/dist/radio/types.d.ts +2 -3
  66. package/dist/ripple/Ripple.svelte +1 -1
  67. package/dist/search/Search.svelte +2 -2
  68. package/dist/select/NativeSelect.svelte +4 -2
  69. package/dist/select/Option.svelte +1 -1
  70. package/dist/select/Select.svelte +42 -20
  71. package/dist/select/types.d.ts +4 -8
  72. package/dist/shared/types.d.ts +9 -0
  73. package/dist/shared/types.js +1 -0
  74. package/dist/sheet/Sheet.svelte +16 -4
  75. package/dist/slider/Slider.svelte +4 -3
  76. package/dist/slider/types.d.ts +2 -4
  77. package/dist/snackbar/Snackbar.svelte +7 -4
  78. package/dist/switch/Switch.svelte +12 -7
  79. package/dist/switch/types.d.ts +2 -4
  80. package/dist/tabs/Tab.svelte +2 -2
  81. package/dist/tabs/Tabs.svelte +2 -2
  82. package/dist/text-field/TextField.svelte +1 -1
  83. package/dist/text-field/types.d.ts +1 -21
  84. package/dist/themes/defaultTheme.css +1 -0
  85. package/dist/time-picker/ClockDial.svelte +3 -3
  86. package/dist/time-picker/DockedTimePicker.svelte +1 -1
  87. package/dist/time-picker/PeriodSelector.svelte +4 -7
  88. package/dist/time-picker/PeriodSelector.svelte.d.ts +1 -1
  89. package/dist/time-picker/TimeInputs.svelte +1 -1
  90. package/dist/time-picker/TimeInputs.svelte.d.ts +1 -1
  91. package/dist/time-picker/TimePickerDialog.svelte +1 -1
  92. package/dist/time-picker/TimePickerPanel.svelte +4 -2
  93. package/dist/time-picker/TimePickerPanel.svelte.d.ts +1 -1
  94. package/dist/time-picker/TimeSelectors.svelte +2 -2
  95. package/dist/time-picker/TimeSelectors.svelte.d.ts +1 -1
  96. package/dist/time-picker/internalTypes.d.ts +76 -0
  97. package/dist/time-picker/internalTypes.js +1 -0
  98. package/dist/time-picker/types.d.ts +7 -95
  99. package/dist/toolbar/Toolbar.svelte +9 -9
  100. package/dist/tooltip/RichTooltip.svelte +2 -2
  101. package/dist/tooltip/Tooltip.svelte +1 -1
  102. package/dist/types.d.ts +1 -0
  103. package/dist/types.js +34 -33
  104. package/package.json +49 -49
@@ -1,5 +1,5 @@
1
1
  <script lang="ts">
2
- import type { AppBarProps } from './types.js'
2
+ import type { AppBarProps } from './types.ts'
3
3
 
4
4
  let {
5
5
  variant = 'small',
@@ -15,9 +15,9 @@
15
15
  ...attributes
16
16
  }: AppBarProps = $props()
17
17
 
18
- const twoLine = $derived(variant === 'medium' || variant === 'large')
19
- const isSearch = $derived(variant === 'search')
20
- const collapses = $derived(collapsible && twoLine)
18
+ let twoLine = $derived(variant === 'medium' || variant === 'large')
19
+ let isSearch = $derived(variant === 'search')
20
+ let collapses = $derived(collapsible && twoLine)
21
21
  </script>
22
22
 
23
23
  {#snippet titles(inline: boolean)}
@@ -36,7 +36,7 @@
36
36
  'np-app-bar',
37
37
  `np-app-bar-${variant}`,
38
38
  collapses && 'np-app-bar-collapsible',
39
- collapses && scroller === 'nearest' && 'np-app-bar-scroller-nearest',
39
+ scroller === 'nearest' && 'np-app-bar-scroller-nearest',
40
40
  attributes.class,
41
41
  ]}
42
42
  >
@@ -75,6 +75,11 @@
75
75
  background-color: var(--np-app-bar-container-color, var(--np-color-surface));
76
76
  color: var(--np-color-on-surface);
77
77
  --np-icon-button-icon-color: var(--np-color-on-surface-variant);
78
+ --_timeline: scroll(root block);
79
+ --_fill-range: 0 var(--np-app-bar-fill-distance, 0.5rem);
80
+ }
81
+ .np-app-bar-scroller-nearest {
82
+ --_timeline: scroll(nearest block);
78
83
  }
79
84
 
80
85
  .np-app-bar:has(:global(.np-search-expanded)) {
@@ -110,21 +115,18 @@
110
115
  }
111
116
 
112
117
  .np-app-bar-subtitle {
113
- font-size: 0.875rem;
114
- line-height: 1.25rem;
118
+ font-size: var(--_subtitle-size);
119
+ line-height: var(--_subtitle-line-height);
115
120
  font-weight: 500;
116
121
  color: var(--np-app-bar-subtitle-color, var(--np-color-on-surface-variant));
117
122
  overflow: hidden;
118
123
  text-overflow: ellipsis;
119
124
  white-space: nowrap;
120
125
  }
121
- .np-app-bar-titles-inline .np-app-bar-headline {
122
- font-size: 1.375rem;
123
- }
124
126
 
125
127
  .np-app-bar-headline {
126
128
  font-size: var(--_headline-size);
127
- line-height: 1.2;
129
+ line-height: var(--_headline-line-height);
128
130
  font-weight: 400;
129
131
  color: var(--np-app-bar-headline-color, var(--np-color-on-surface));
130
132
  overflow: hidden;
@@ -132,10 +134,22 @@
132
134
  white-space: nowrap;
133
135
  min-width: 0;
134
136
  }
137
+ .np-app-bar-second-row .np-app-bar-headline {
138
+ display: -webkit-box;
139
+ -webkit-box-orient: vertical;
140
+ -webkit-line-clamp: 2;
141
+ line-clamp: 2;
142
+ white-space: normal;
143
+ overflow-wrap: anywhere;
144
+ }
135
145
 
136
146
  .np-app-bar-small,
137
- .np-app-bar-search {
147
+ .np-app-bar-search,
148
+ .np-app-bar-inline {
138
149
  --_headline-size: 1.375rem;
150
+ --_headline-line-height: 1.75rem;
151
+ --_subtitle-size: 0.75rem;
152
+ --_subtitle-line-height: 1rem;
139
153
  }
140
154
 
141
155
  .np-app-bar-search-field {
@@ -145,17 +159,26 @@
145
159
  align-items: center;
146
160
  padding-inline: 0.25rem;
147
161
  }
162
+ .np-app-bar-search-field :global(.np-search:not(.np-search-expanded) .np-search-bar) {
163
+ background-color: var(--np-search-container-color, var(--np-color-surface-container));
164
+ }
148
165
  .np-app-bar-small .np-app-bar-titles {
149
166
  padding-inline-start: 0.75rem;
150
167
  flex: 1;
151
168
  }
152
169
 
153
170
  .np-app-bar-medium {
154
- --_headline-size: 1.5rem;
171
+ --_headline-size: 1.75rem;
172
+ --_headline-line-height: 2.25rem;
173
+ --_subtitle-size: 0.875rem;
174
+ --_subtitle-line-height: 1.25rem;
155
175
  --_second-row-height: 3rem;
156
176
  }
157
177
  .np-app-bar-large {
158
- --_headline-size: 1.75rem;
178
+ --_headline-size: 2.25rem;
179
+ --_headline-line-height: 2.75rem;
180
+ --_subtitle-size: 1rem;
181
+ --_subtitle-line-height: 1.5rem;
159
182
  --_second-row-height: 5.5rem;
160
183
  }
161
184
 
@@ -170,18 +193,15 @@
170
193
  padding-block-end: 1rem;
171
194
  overflow: hidden;
172
195
  }
196
+ .np-app-bar-medium .np-app-bar-second-row {
197
+ padding-block-end: 0.75rem;
198
+ }
173
199
 
174
200
  .np-app-bar-inline {
175
- font-size: 1.375rem;
176
- line-height: 1.2;
177
- font-weight: 400;
178
- color: var(--np-app-bar-headline-color, var(--np-color-on-surface));
179
201
  padding-inline-start: 0.75rem;
180
202
  flex: 1;
181
203
  min-width: 0;
182
204
  overflow: hidden;
183
- text-overflow: ellipsis;
184
- white-space: nowrap;
185
205
  opacity: 0;
186
206
  }
187
207
 
@@ -189,23 +209,45 @@
189
209
  :global(html:has(.np-app-bar-collapsible)) {
190
210
  overflow-anchor: none;
191
211
  }
192
- :global(:has(.np-app-bar-scroller-nearest)) {
212
+ :global(:has(.np-app-bar-collapsible.np-app-bar-scroller-nearest)) {
193
213
  overflow-anchor: none;
194
214
  }
195
215
 
216
+ .np-app-bar {
217
+ animation: np-app-bar-fill linear both;
218
+ animation-timeline: var(--_timeline);
219
+ animation-range: var(--_fill-range);
220
+ }
221
+ .np-app-bar-search-field :global(.np-search:not(.np-search-expanded) .np-search-bar) {
222
+ animation: np-app-bar-search-fill linear both;
223
+ animation-timeline: var(--_timeline);
224
+ animation-range: var(--_fill-range);
225
+ }
226
+
196
227
  .np-app-bar-collapsible .np-app-bar-second-row {
197
228
  animation: np-app-bar-collapse linear both;
198
- animation-timeline: scroll(root block);
229
+ animation-timeline: var(--_timeline);
199
230
  animation-range: 0 var(--_second-row-height);
200
231
  }
201
232
  .np-app-bar-collapsible .np-app-bar-inline {
202
233
  animation: np-app-bar-reveal linear both;
203
- animation-timeline: scroll(root block);
234
+ animation-timeline: var(--_timeline);
204
235
  animation-range: 0 var(--_second-row-height);
205
236
  }
206
- .np-app-bar-scroller-nearest .np-app-bar-second-row,
207
- .np-app-bar-scroller-nearest .np-app-bar-inline {
208
- animation-timeline: scroll(nearest block);
237
+ }
238
+
239
+ @keyframes np-app-bar-fill {
240
+ to {
241
+ background-color: var(
242
+ --np-app-bar-scrolled-container-color,
243
+ var(--np-color-surface-container)
244
+ );
245
+ }
246
+ }
247
+
248
+ @keyframes np-app-bar-search-fill {
249
+ to {
250
+ background-color: var(--np-search-container-color, var(--np-color-surface-container-highest));
209
251
  }
210
252
  }
211
253
 
@@ -3,7 +3,7 @@
3
3
  import Menu from '../menu/Menu.svelte'
4
4
  import VirtualList from '../select/VirtualList.svelte'
5
5
  import TextField from '../text-field/TextField.svelte'
6
- import type { AutoCompleteOption, AutoCompleteProps } from './types.js'
6
+ import type { AutoCompleteOption, AutoCompleteProps } from './types.ts'
7
7
 
8
8
  let {
9
9
  options = [],
@@ -28,8 +28,8 @@
28
28
  }: AutoCompleteProps = $props()
29
29
 
30
30
  const uid = $props.id()
31
- const query = $derived(value ? `${value}`.toLocaleLowerCase() : '')
32
- const filterFn = $derived(
31
+ let query = $derived(value ? `${value}`.toLocaleLowerCase() : '')
32
+ let filterFn = $derived(
33
33
  optionsFilter ||
34
34
  ((option: AutoCompleteOption) => !query || option.label.toLocaleLowerCase().includes(query)),
35
35
  )
@@ -121,7 +121,6 @@
121
121
  setActive(index)
122
122
  selectOption(option)
123
123
  }}
124
- variant="button"
125
124
  start={option.start}
126
125
  end={option.end}
127
126
  supportingText={option.supportingText}
@@ -1,8 +1,8 @@
1
- import type { ItemProps } from '../list/types.js';
1
+ import type { BaseOption } from '../shared/types.js';
2
2
  import type { TextFieldProps } from '../types.js';
3
- export interface AutoCompleteOption extends Pick<ItemProps, 'start' | 'end' | 'supportingText' | 'class' | 'style'> {
3
+ export interface AutoCompleteOption extends BaseOption {
4
+ /** Falls back to `label`. */
4
5
  value?: string | number;
5
- label: string;
6
6
  }
7
7
  export interface AutoCompleteProps extends Omit<TextFieldProps, 'clientWidth' | 'clientHeight'> {
8
8
  options: AutoCompleteOption[];
@@ -1,5 +1,5 @@
1
1
  <script lang="ts">
2
- import type { BadgeProps } from './types.js'
2
+ import type { BadgeProps } from './types.ts'
3
3
 
4
4
  let {
5
5
  label,
@@ -5,7 +5,7 @@
5
5
  import Ripple from '../ripple/Ripple.svelte'
6
6
  import Tooltip from '../tooltip/Tooltip.svelte'
7
7
  import type { HTMLButtonAttributes } from 'svelte/elements'
8
- import type { ButtonProps } from './types.js'
8
+ import type { ButtonProps } from './types.ts'
9
9
 
10
10
  let {
11
11
  variant = 'outlined',
@@ -26,9 +26,9 @@
26
26
 
27
27
  const uid = $props.id()
28
28
 
29
- const isLink = $derived(attributes.href != null && !disabled && !loading)
29
+ let isLink = $derived(attributes.href != null && !disabled && !loading)
30
30
 
31
- const tooltipId = $derived(title && !disabled && !loading ? uid : undefined)
31
+ let tooltipId = $derived(title && !disabled && !loading ? uid : undefined)
32
32
 
33
33
  const morph = pressMorph()
34
34
 
@@ -149,6 +149,9 @@
149
149
  opacity: 0;
150
150
  }
151
151
  .np-button {
152
+ /* State layers take the content color of each variant and state. */
153
+ --np-ripple-hover-color: currentColor;
154
+ --np-ripple-pressed-color: currentColor;
152
155
  box-sizing: border-box;
153
156
  font: inherit;
154
157
  background-color: transparent;
@@ -279,14 +282,9 @@
279
282
  }
280
283
  }
281
284
  .text {
282
- --np-ripple-hover-color: var(--np-text-button-label-text-color, var(--np-color-primary));
283
- --np-ripple-pressed-color: var(--np-text-button-label-text-color, var(--np-color-primary));
284
285
  color: var(--np-text-button-label-text-color, var(--np-color-primary));
285
286
  }
286
287
  .filled {
287
- --np-ripple-hover-opacity: 0.12;
288
- --np-ripple-hover-color: var(--np-color-surface);
289
- --np-ripple-pressed-color: var(--np-color-surface);
290
288
  color: var(--np-filled-button-label-text-color, var(--np-color-on-primary));
291
289
  background-color: var(--np-filled-button-container-color, var(--np-color-primary));
292
290
  }
@@ -312,8 +310,6 @@
312
310
  }
313
311
 
314
312
  .tonal {
315
- --np-ripple-hover-color: var(--np-tonal-button-label-text-color, var(--np-color-primary));
316
- --np-ripple-pressed-color: var(--np-tonal-button-label-text-color, var(--np-color-primary));
317
313
  color: var(--np-tonal-button-label-text-color, var(--np-color-on-secondary-container));
318
314
  background-color: var(--np-tonal-button-container-color, var(--np-color-secondary-container));
319
315
  }
@@ -336,8 +332,6 @@
336
332
  }
337
333
 
338
334
  .elevated {
339
- --np-ripple-hover-color: var(--np-elevated-button-label-text-color, var(--np-color-primary));
340
- --np-ripple-pressed-color: var(--np-elevated-button-label-text-color, var(--np-color-primary));
341
335
  color: var(--np-elevated-button-label-text-color, var(--np-color-primary));
342
336
  background-color: var(
343
337
  --np-elevated-button-container-color,
@@ -364,9 +358,7 @@
364
358
  --np-outlined-button-outline-color,
365
359
  var(--np-color-outline-variant)
366
360
  );
367
- --np-ripple-hover-color: var(--np-outlined-button-label-text-color, var(--np-color-primary));
368
- --np-ripple-pressed-color: var(--np-outlined-button-label-text-color, var(--np-color-primary));
369
- color: var(--np-outlined-button-label-text-color, var(--np-color-primary));
361
+ color: var(--np-outlined-button-label-text-color, var(--np-color-on-surface-variant));
370
362
  }
371
363
 
372
364
  .outlined:not(.selected)::after,
@@ -1,8 +1,8 @@
1
1
  <script lang="ts">
2
2
  import { reducedMotion } from '../media.js'
3
3
  import { onMount } from 'svelte'
4
- import { expandedWidths, parseMotion, resolveItem } from './buttonGroup.js'
5
- import type { ButtonGroupProps } from './types.js'
4
+ import { expandedWidths, parseMotion, resolveItem } from './buttonGroup.ts'
5
+ import type { ButtonGroupProps } from './types.ts'
6
6
 
7
7
  let {
8
8
  variant = 'standard',
@@ -5,7 +5,7 @@
5
5
  import Ripple from '../ripple/Ripple.svelte'
6
6
  import Tooltip from '../tooltip/Tooltip.svelte'
7
7
  import type { HTMLButtonAttributes } from 'svelte/elements'
8
- import type { IconButtonProps } from './types.js'
8
+ import type { IconButtonProps } from './types.ts'
9
9
 
10
10
  let {
11
11
  variant = 'text',
@@ -26,8 +26,8 @@
26
26
 
27
27
  const uid = $props.id()
28
28
 
29
- const isLink = $derived(attributes.href != null && !disabled && !loading)
30
- const tooltipId = $derived(title && !disabled && !loading ? uid : undefined)
29
+ let isLink = $derived(attributes.href != null && !disabled && !loading)
30
+ let tooltipId = $derived(title && !disabled && !loading ? uid : undefined)
31
31
 
32
32
  const morph = pressMorph()
33
33
 
@@ -123,6 +123,9 @@
123
123
 
124
124
  <style>
125
125
  .np-icon-button {
126
+ /* State layers take the content color of each variant and state. */
127
+ --np-ripple-hover-color: currentColor;
128
+ --np-ripple-pressed-color: currentColor;
126
129
  font: inherit;
127
130
  box-sizing: border-box;
128
131
  -webkit-tap-highlight-color: transparent;
@@ -281,30 +284,16 @@
281
284
  }
282
285
  }
283
286
  .text {
284
- --np-ripple-hover-color: var(--np-icon-button-icon-color, var(--np-color-on-surface-variant));
285
- --np-ripple-pressed-color: var(--np-icon-button-icon-color, var(--np-color-on-surface-variant));
286
287
  color: var(--np-icon-button-icon-color, var(--np-color-on-surface-variant));
287
288
  }
288
289
  .text.selected {
289
- --np-ripple-hover-color: var(--np-color-primary);
290
- --np-ripple-pressed-color: var(--np-color-primary);
291
290
  color: var(--np-color-primary);
292
291
  }
293
292
  .filled {
294
- --np-ripple-hover-color: var(--np-filled-icon-button-icon-color, var(--np-color-on-primary));
295
- --np-ripple-pressed-color: var(--np-filled-icon-button-icon-color, var(--np-color-on-primary));
296
293
  color: var(--np-filled-icon-button-icon-color, var(--np-color-on-primary));
297
294
  background-color: var(--np-filled-icon-button-container-color, var(--np-color-primary));
298
295
  }
299
296
  .filled.toggle {
300
- --np-ripple-hover-color: var(
301
- --np-filled-icon-button-unselected-icon-color,
302
- var(--np-color-on-surface-variant)
303
- );
304
- --np-ripple-pressed-color: var(
305
- --np-filled-icon-button-unselected-icon-color,
306
- var(--np-color-on-surface-variant)
307
- );
308
297
  color: var(--np-filled-icon-button-unselected-icon-color, var(--np-color-on-surface-variant));
309
298
  background-color: var(
310
299
  --np-filled-icon-button-unselected-container-color,
@@ -312,14 +301,6 @@
312
301
  );
313
302
  }
314
303
  .filled.selected {
315
- --np-ripple-hover-color: var(
316
- --np-filled-icon-button-selected-icon-color,
317
- var(--np-color-on-primary)
318
- );
319
- --np-ripple-pressed-color: var(
320
- --np-filled-icon-button-selected-icon-color,
321
- var(--np-color-on-primary)
322
- );
323
304
  color: var(--np-filled-icon-button-selected-icon-color, var(--np-color-on-primary));
324
305
  background-color: var(
325
306
  --np-filled-icon-button-selected-container-color,
@@ -328,14 +309,6 @@
328
309
  }
329
310
 
330
311
  .tonal {
331
- --np-ripple-hover-color: var(
332
- --np-tonal-icon-button-icon-color,
333
- var(--np-color-on-secondary-container)
334
- );
335
- --np-ripple-pressed-color: var(
336
- --np-tonal-icon-button-icon-color,
337
- var(--np-color-on-secondary-container)
338
- );
339
312
  color: var(--np-tonal-icon-button-icon-color, var(--np-color-on-secondary-container));
340
313
  background-color: var(
341
314
  --np-tonal-icon-button-container-color,
@@ -343,14 +316,10 @@
343
316
  );
344
317
  }
345
318
  .tonal.toggle {
346
- --np-ripple-hover-color: var(--np-color-on-secondary-container);
347
- --np-ripple-pressed-color: var(--np-color-on-secondary-container);
348
319
  color: var(--np-color-on-secondary-container);
349
320
  background-color: var(--np-color-secondary-container);
350
321
  }
351
322
  .tonal.selected {
352
- --np-ripple-hover-color: var(--np-color-on-secondary);
353
- --np-ripple-pressed-color: var(--np-color-on-secondary);
354
323
  color: var(--np-color-on-secondary);
355
324
  background-color: var(--np-color-secondary);
356
325
  }
@@ -360,8 +329,6 @@
360
329
  --np-outlined-icon-button-outline-color,
361
330
  var(--np-color-outline-variant)
362
331
  );
363
- --np-ripple-hover-color: var(--np-color-on-surface-variant);
364
- --np-ripple-pressed-color: var(--np-color-on-surface-variant);
365
332
  color: var(--np-color-on-surface-variant);
366
333
  }
367
334
 
@@ -376,8 +343,6 @@
376
343
  }
377
344
 
378
345
  .outlined.selected {
379
- --np-ripple-hover-color: var(--np-color-on-surface-variant);
380
- --np-ripple-pressed-color: var(--np-color-on-surface-variant);
381
346
  color: var(--np-color-inverse-on-surface);
382
347
  background-color: var(--np-color-inverse-surface);
383
348
  }
@@ -2,7 +2,7 @@
2
2
  import '../internal/focus-ring.css'
3
3
  import CheckIcon from '../icons/CheckIcon.svelte'
4
4
  import Ripple from '../ripple/Ripple.svelte'
5
- import type { SegmentedButtonProps } from './types.js'
5
+ import type { SegmentedButtonProps } from './types.ts'
6
6
 
7
7
  let {
8
8
  name,
@@ -24,7 +24,8 @@
24
24
  style="{attributes.style};grid-template-columns: repeat({options.length}, minmax(max-content, 1fr));"
25
25
  >
26
26
  {#each options as option, i (i)}
27
- {@const optionLabel = option.label ?? `${name}-${i}`}
27
+ {let optionLabel = $derived(option.label ?? `${name}-${i}`)}
28
+ {let optionValue = $derived(option.value ?? optionLabel)}
28
29
  <label class={['np-segmented-button', option.icon && 'width-icon']}>
29
30
  <div class="check-icon-wrapper">
30
31
  <div class="check-icon">
@@ -56,7 +57,7 @@
56
57
  bind:group
57
58
  {name}
58
59
  aria-label={optionLabel}
59
- value={optionLabel}
60
+ value={optionValue}
60
61
  disabled={option.disabled}
61
62
  checked={option.selected}
62
63
  />
@@ -67,7 +68,7 @@
67
68
  bind:group
68
69
  {name}
69
70
  aria-label={optionLabel}
70
- value={optionLabel}
71
+ value={optionValue}
71
72
  disabled={option.disabled}
72
73
  checked={option.selected}
73
74
  />
@@ -78,7 +79,7 @@
78
79
  onclick={option.onclick}
79
80
  {name}
80
81
  aria-label={optionLabel}
81
- value={optionLabel}
82
+ value={optionValue}
82
83
  disabled={option.disabled}
83
84
  checked={option.selected}
84
85
  />
@@ -89,8 +90,9 @@
89
90
 
90
91
  <style>
91
92
  .np-segmented-buttons {
92
- --np-ripple-hover-color: var(--np-color-primary);
93
- --np-ripple-pressed-color: var(--np-color-primary);
93
+ /* State layers take the content color of each variant and state. */
94
+ --np-ripple-hover-color: currentColor;
95
+ --np-ripple-pressed-color: currentColor;
94
96
  display: grid;
95
97
  color: var(--np-color-on-surface);
96
98
  border: 1px solid var(--np-color-outline);
@@ -99,8 +101,6 @@
99
101
  }
100
102
 
101
103
  .np-segmented-buttons.np-error {
102
- --np-ripple-hover-color: var(--np-color-error);
103
- --np-ripple-pressed-color: var(--np-color-error);
104
104
  border-color: var(--np-color-error);
105
105
  color: var(--np-color-error);
106
106
  }
@@ -3,7 +3,7 @@
3
3
  import Menu from '../menu/Menu.svelte'
4
4
  import Button from './Button.svelte'
5
5
  import ButtonGroup from './ButtonGroup.svelte'
6
- import type { SplitButtonProps } from './types.js'
6
+ import type { SplitButtonProps } from './types.ts'
7
7
 
8
8
  let {
9
9
  label = '',
@@ -49,7 +49,7 @@
49
49
  aria-label={menuLabel}
50
50
  start={caret}
51
51
  />
52
- <Menu id={menuId} anchor={trigger} bind:open coverAnchor={false}>
52
+ <Menu id={menuId} anchor={trigger} bind:open coverAnchor={false} style="--np-menu-margin: 4px">
53
53
  {@render menu?.(menuId)}
54
54
  </Menu>
55
55
  </ButtonGroup>
@@ -1,3 +1,4 @@
1
+ import type { Issue } from '../shared/types.js';
1
2
  import type { Snippet } from 'svelte';
2
3
  import type { HTMLAnchorAttributes, HTMLAttributes, HTMLButtonAttributes, MouseEventHandler } from 'svelte/elements';
3
4
  export type ButtonElement = HTMLButtonElement | HTMLAnchorElement;
@@ -40,20 +41,21 @@ export interface SplitButtonProps extends Omit<HTMLAttributes<HTMLDivElement>, '
40
41
  onclick?: MouseEventHandler<ButtonElement>;
41
42
  element?: HTMLElement;
42
43
  }
44
+ export interface SegmentedButtonOption {
45
+ /** Stored in `group` and submitted with the form. Falls back to `label`. */
46
+ value?: string | number;
47
+ label?: string;
48
+ labelIcon?: Snippet;
49
+ selected?: boolean;
50
+ disabled?: boolean | null;
51
+ icon?: Snippet;
52
+ onclick?: (event: Event) => void;
53
+ }
43
54
  export interface SegmentedButtonProps extends HTMLAttributes<HTMLDivElement> {
44
55
  name: string;
45
56
  multiSelect?: boolean;
46
- options: {
47
- label?: string;
48
- labelIcon?: Snippet;
49
- selected?: boolean;
50
- disabled?: boolean;
51
- icon?: Snippet;
52
- onclick?: (event: Event) => void;
53
- }[];
57
+ options: SegmentedButtonOption[];
54
58
  group?: string | number | (string | number)[] | null;
55
59
  element?: HTMLElement;
56
- issues?: {
57
- message: string;
58
- }[];
60
+ issues?: Issue[];
59
61
  }
@@ -1,10 +1,12 @@
1
1
  <script lang="ts">
2
2
  import '../internal/focus-ring.css'
3
+ import { buttonType, elementTag } from '../internal/elementTag.js'
3
4
  import Ripple from '../ripple/Ripple.svelte'
4
- import type { CardProps } from './types.js'
5
+ import type { CardProps } from './types.ts'
5
6
 
6
7
  let {
7
8
  image,
9
+ imageAlt,
8
10
  variant = 'outlined',
9
11
  disabled = false,
10
12
  element = $bindable(),
@@ -16,11 +18,18 @@
16
18
  type,
17
19
  ...attributes
18
20
  }: CardProps = $props()
21
+
22
+ let tag = $derived(elementTag(attributes, type))
19
23
  </script>
20
24
 
21
25
  {#snippet content()}
22
26
  {#if image}
23
- <div class="np-card-image" style="background-image: url({image})"></div>
27
+ <div
28
+ class="np-card-image"
29
+ style="background-image: url({image})"
30
+ role={imageAlt ? 'img' : undefined}
31
+ aria-label={imageAlt || undefined}
32
+ ></div>
24
33
  {/if}
25
34
  <div class="np-card-content">
26
35
  {#if headline}
@@ -45,12 +54,12 @@
45
54
  <div class="np-card-action">{@render action()}</div>
46
55
  {/if}
47
56
  </div>
48
- {#if !disabled && type !== 'text'}
57
+ {#if !disabled && tag !== 'div'}
49
58
  <Ripple />
50
59
  {/if}
51
60
  {/snippet}
52
61
 
53
- {#if type === 'text'}
62
+ {#if tag === 'div'}
54
63
  <div
55
64
  {...attributes}
56
65
  bind:this={element}
@@ -64,11 +73,11 @@
64
73
  >
65
74
  {@render content()}
66
75
  </div>
67
- {:else if type === 'button'}
76
+ {:else if tag === 'button'}
68
77
  <button
69
78
  {...attributes}
70
79
  aria-disabled={disabled}
71
- type="button"
80
+ type={buttonType(type)}
72
81
  bind:this={element}
73
82
  {disabled}
74
83
  class={[
@@ -80,7 +89,7 @@
80
89
  >
81
90
  {@render content()}
82
91
  </button>
83
- {:else if type === 'link'}
92
+ {:else}
84
93
  <a
85
94
  {...attributes}
86
95
  bind:this={element}
@@ -1,14 +1,14 @@
1
1
  import type { Snippet } from 'svelte';
2
- import type { HTMLAnchorAttributes, HTMLAttributes, HTMLButtonAttributes } from 'svelte/elements';
2
+ import type { ElementKindProps } from '../internal/elementTag.js';
3
3
  export type CardElement = HTMLDivElement | HTMLButtonElement | HTMLAnchorElement;
4
- export interface CardProps extends HTMLAttributes<CardElement>, Omit<HTMLButtonAttributes, keyof HTMLAttributes<HTMLButtonElement> | 'type'>, Omit<HTMLAnchorAttributes, keyof HTMLAttributes<HTMLAnchorElement> | 'type'> {
4
+ export interface CardProps extends ElementKindProps<CardElement> {
5
5
  variant?: 'elevated' | 'filled' | 'outlined';
6
- disabled?: boolean | null;
7
6
  image?: string | null;
7
+ /** Describes `image` for assistive technology. Leave it out for a decorative image. */
8
+ imageAlt?: string;
8
9
  element?: HTMLElement;
9
10
  headline?: string | null;
10
11
  subhead?: string | null;
11
12
  supportingText?: string | null;
12
13
  action?: Snippet<[]>;
13
- type: 'text' | 'button' | 'link';
14
14
  }