noph-ui 0.52.0 → 0.53.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 (103) hide show
  1. package/dist/animation.d.ts +1 -13
  2. package/dist/animation.js +18 -13
  3. package/dist/button/Button.svelte +2 -14
  4. package/dist/button/IconButton.svelte +5 -18
  5. package/dist/button/SegmentedButton.svelte +8 -11
  6. package/dist/card/Card.svelte +3 -19
  7. package/dist/card/types.d.ts +12 -2
  8. package/dist/carousel/Carousel.svelte +77 -14
  9. package/dist/carousel/CarouselItem.svelte +26 -14
  10. package/dist/carousel/carouselMorph.js +1 -1
  11. package/dist/carousel/carouselStrategy.js +25 -27
  12. package/dist/checkbox/Checkbox.svelte +1 -8
  13. package/dist/chip/FilterChip.svelte +1 -13
  14. package/dist/date-picker/Calendar.svelte +31 -30
  15. package/dist/date-picker/Calendar.svelte.d.ts +2 -0
  16. package/dist/date-picker/CalendarHeader.svelte +84 -62
  17. package/dist/date-picker/CalendarHeader.svelte.d.ts +3 -0
  18. package/dist/date-picker/DatePickerDialog.svelte +134 -75
  19. package/dist/date-picker/DatePickerDialog.svelte.d.ts +4 -3
  20. package/dist/date-picker/DateRangePicker.svelte +226 -78
  21. package/dist/date-picker/DateRangePicker.svelte.d.ts +3 -3
  22. package/dist/date-picker/DockedDatePicker.svelte +76 -464
  23. package/dist/date-picker/DockedDatePicker.svelte.d.ts +2 -2
  24. package/dist/date-picker/DockedDateTimePicker.svelte +128 -525
  25. package/dist/date-picker/DockedDateTimePicker.svelte.d.ts +2 -2
  26. package/dist/date-picker/DockedPickerField.svelte +221 -0
  27. package/dist/date-picker/DockedPickerField.svelte.d.ts +9 -0
  28. package/dist/date-picker/MonthPanel.svelte +291 -0
  29. package/dist/date-picker/MonthPanel.svelte.d.ts +12 -0
  30. package/dist/date-picker/SelectionList.svelte +7 -6
  31. package/dist/date-picker/SelectionList.svelte.d.ts +1 -0
  32. package/dist/date-picker/TimeColumn.svelte +2 -6
  33. package/dist/date-picker/TimeColumn.svelte.d.ts +1 -0
  34. package/dist/date-picker/YearGrid.svelte +2 -11
  35. package/dist/date-picker/YearGrid.svelte.d.ts +1 -0
  36. package/dist/date-picker/dateUtils.d.ts +4 -0
  37. package/dist/date-picker/dateUtils.js +15 -88
  38. package/dist/date-picker/exitVisibility.svelte.js +13 -6
  39. package/dist/date-picker/formatters.d.ts +16 -0
  40. package/dist/date-picker/formatters.js +48 -15
  41. package/dist/date-picker/internalTypes.d.ts +59 -1
  42. package/dist/date-picker/shortcuts.d.ts +11 -0
  43. package/dist/date-picker/shortcuts.js +40 -0
  44. package/dist/date-picker/timeUtils.d.ts +8 -3
  45. package/dist/date-picker/timeUtils.js +33 -51
  46. package/dist/date-picker/types.d.ts +22 -0
  47. package/dist/icons/Icon.svelte +1 -4
  48. package/dist/internal/exit.css +0 -14
  49. package/dist/internal/field.css +337 -0
  50. package/dist/internal/focus-ring.css +23 -8
  51. package/dist/internal/pop-in.css +19 -0
  52. package/dist/internal/visually-hidden.css +11 -0
  53. package/dist/list/Item.svelte +32 -18
  54. package/dist/menu/Menu.svelte +6 -25
  55. package/dist/menu/Menu.svelte.d.ts +1 -0
  56. package/dist/navigation-bar/NavigationBarItem.svelte +6 -30
  57. package/dist/navigation-drawer/NavigationDrawerItem.svelte +4 -11
  58. package/dist/navigation-rail/NavigationRailItem.svelte +2 -13
  59. package/dist/press.svelte.d.ts +0 -1
  60. package/dist/press.svelte.js +0 -11
  61. package/dist/progress/CircularProgress.svelte +26 -81
  62. package/dist/progress/CircularProgress.svelte.d.ts +1 -0
  63. package/dist/progress/LinearProgress.svelte +19 -151
  64. package/dist/progress/LinearProgress.svelte.d.ts +1 -0
  65. package/dist/progress/four-color.css +34 -0
  66. package/dist/radio/Radio.svelte +6 -9
  67. package/dist/ripple/Ripple.svelte +8 -16
  68. package/dist/search/Search.svelte +2 -9
  69. package/dist/search/Search.svelte.d.ts +1 -0
  70. package/dist/select/Check.svelte +8 -31
  71. package/dist/select/Check.svelte.d.ts +1 -1
  72. package/dist/select/NativeSelect.svelte +97 -366
  73. package/dist/select/NativeSelect.svelte.d.ts +1 -0
  74. package/dist/select/Option.svelte +1 -9
  75. package/dist/select/Select.svelte +139 -746
  76. package/dist/select/Select.svelte.d.ts +1 -0
  77. package/dist/select/VirtualList.svelte +0 -1
  78. package/dist/sheet/Sheet.svelte +11 -22
  79. package/dist/slider/Slider.svelte +13 -37
  80. package/dist/slider/Slider.svelte.d.ts +1 -0
  81. package/dist/switch/Switch.svelte +2 -8
  82. package/dist/tabs/Tab.svelte +2 -4
  83. package/dist/text-field/TextField.svelte +81 -609
  84. package/dist/text-field/TextField.svelte.d.ts +1 -0
  85. package/dist/time-picker/ClockDial.svelte +8 -25
  86. package/dist/time-picker/ClockDial.svelte.d.ts +2 -0
  87. package/dist/time-picker/DockedTimePicker.svelte +102 -244
  88. package/dist/time-picker/DockedTimePicker.svelte.d.ts +2 -2
  89. package/dist/time-picker/PeriodSelector.svelte +8 -7
  90. package/dist/time-picker/PeriodSelector.svelte.d.ts +1 -0
  91. package/dist/time-picker/TimeInputs.svelte +13 -2
  92. package/dist/time-picker/TimePickerDialog.svelte +32 -26
  93. package/dist/time-picker/TimePickerDialog.svelte.d.ts +2 -2
  94. package/dist/time-picker/TimePickerPanel.svelte +26 -4
  95. package/dist/time-picker/TimeSelectors.svelte +4 -9
  96. package/dist/time-picker/TimeSelectors.svelte.d.ts +1 -0
  97. package/dist/time-picker/internalTypes.d.ts +2 -0
  98. package/dist/time-picker/timePickerState.svelte.js +3 -9
  99. package/dist/tooltip/RichTooltip.svelte +2 -23
  100. package/dist/tooltip/RichTooltip.svelte.d.ts +1 -0
  101. package/dist/tooltip/Tooltip.svelte +2 -24
  102. package/dist/tooltip/Tooltip.svelte.d.ts +1 -0
  103. package/package.json +12 -12
@@ -0,0 +1,337 @@
1
+ .np-field {
2
+ --_outline-width: 1px;
3
+ --_outline-color: var(--np-color-outline);
4
+ --_indicator-color: var(--np-color-on-surface-variant);
5
+ --_indicator-focus-color: var(--_field-focus-indicator, var(--np-color-primary));
6
+ --_label-color: var(--np-color-on-surface-variant);
7
+ --_start-color: var(--np-color-on-surface-variant);
8
+ --_end-color: var(--np-color-on-surface-variant);
9
+ --_supporting-color: var(--np-color-on-surface-variant);
10
+ --_floating-label-top: 0.5rem;
11
+ --top-space: 1.5rem;
12
+ --bottom-space: 0.5rem;
13
+ display: flex;
14
+ flex: 1;
15
+ flex-direction: column;
16
+ writing-mode: horizontal-tb;
17
+ max-width: 100%;
18
+ }
19
+ .np-field:where(.np-field-outlined, .np-field-no-label) {
20
+ --top-space: 1rem;
21
+ --bottom-space: 1rem;
22
+ }
23
+ .np-field-outlined {
24
+ --_floating-label-top: -0.5rem;
25
+ }
26
+
27
+ .np-field:where(:hover) {
28
+ --_outline-color: var(--np-color-on-surface);
29
+ }
30
+ .np-field-outlined:where(:hover) {
31
+ --_label-color: var(--np-color-on-surface);
32
+ }
33
+ .np-field:where(:focus, .np-field-open, :has(:is(.np-field-input, .np-field-value):focus)) {
34
+ --_outline-width: 3px;
35
+ --_outline-color: var(--_field-focus-outline, var(--np-color-primary));
36
+ --_label-color: var(--_field-focus-label, var(--np-color-primary));
37
+ }
38
+ .np-field:where(
39
+ [aria-invalid='true'],
40
+ :has(:is(.np-field-input, .np-field-value):is(:user-invalid, [aria-invalid='true']))
41
+ ) {
42
+ --_outline-color: var(--np-color-error);
43
+ --_indicator-color: var(--np-color-error);
44
+ --_indicator-focus-color: var(--np-color-error);
45
+ --_label-color: var(--np-color-error);
46
+ --_end-color: var(--np-color-error);
47
+ --_supporting-color: var(--np-color-error);
48
+
49
+ &:where(:hover) {
50
+ --_outline-color: var(--np-color-on-error-container);
51
+ --_indicator-focus-color: var(--np-color-on-error-container);
52
+ --_label-color: var(--np-color-on-error-container);
53
+ --_end-color: var(--np-color-on-error-container);
54
+ }
55
+ }
56
+ .np-field-disabled {
57
+ --_outline-color: var(--np-color-on-surface);
58
+ --_indicator-color: var(--np-color-on-surface);
59
+ --_label-color: var(--np-color-on-surface);
60
+ --_start-color: var(--np-color-on-surface);
61
+ --_end-color: var(--np-color-on-surface);
62
+ --_supporting-color: var(--np-color-on-surface);
63
+ cursor: default;
64
+ pointer-events: none;
65
+ }
66
+
67
+ .np-field-box {
68
+ display: flex;
69
+ height: 100%;
70
+ position: relative;
71
+ border-radius: var(--_field-shape) var(--_field-shape) 0 0;
72
+ }
73
+ .np-field-outlined .np-field-box {
74
+ border-radius: var(--_field-shape);
75
+ }
76
+ .np-field-background,
77
+ .np-field-state-layer {
78
+ position: absolute;
79
+ inset: 0;
80
+ border-radius: inherit;
81
+ pointer-events: none;
82
+ }
83
+ .np-field-background {
84
+ background: var(--_field-container-color);
85
+ }
86
+ .np-field-disabled .np-field-background {
87
+ background: var(--np-color-on-surface);
88
+ opacity: 0.04;
89
+ }
90
+ .np-field:not(.np-field-disabled):hover .np-field-state-layer {
91
+ background: var(--np-color-on-surface);
92
+ opacity: 0.08;
93
+ }
94
+
95
+ .np-field-indicator {
96
+ position: absolute;
97
+ inset: auto 0 0 0;
98
+ pointer-events: none;
99
+ z-index: 1;
100
+
101
+ &::before,
102
+ &::after {
103
+ content: '';
104
+ position: absolute;
105
+ inset: auto 0 0 0;
106
+ border-bottom: 1px solid var(--_indicator-color);
107
+ }
108
+ &::after {
109
+ border-bottom: 3px solid var(--_indicator-focus-color);
110
+ opacity: 0;
111
+ transition: opacity var(--np-motion-expressive-fast-effects);
112
+ }
113
+ }
114
+ .np-field:where(:focus, .np-field-open, :has(:is(.np-field-input, .np-field-value):focus))
115
+ .np-field-indicator::after {
116
+ opacity: 1;
117
+ }
118
+ .np-field-disabled .np-field-indicator::before {
119
+ opacity: 0.38;
120
+ }
121
+
122
+ /* The outline is a fieldset, which leaves a gap for its legend where the floating label sits. The
123
+ border runs through the middle of the 1rem legend, so the fieldset starts half of it higher. */
124
+ .np-field-outline {
125
+ position: absolute;
126
+ inset: calc(var(--_outline-width) / 2 - 0.5rem) 0 0;
127
+ margin: 0;
128
+ padding: 0 calc(0.75rem - var(--_outline-width));
129
+ min-inline-size: 0;
130
+ border: var(--_outline-width) solid var(--_outline-color);
131
+ border-radius: inherit;
132
+ pointer-events: none;
133
+ text-align: start;
134
+ z-index: 1;
135
+ transition: border-color var(--np-motion-expressive-fast-effects);
136
+
137
+ & legend {
138
+ display: block;
139
+ float: none;
140
+ height: 1rem;
141
+ max-inline-size: 0.01px;
142
+ padding: 0;
143
+ overflow: hidden;
144
+ font-size: 0.75rem;
145
+ line-height: 1rem;
146
+ white-space: nowrap;
147
+ visibility: hidden;
148
+ }
149
+ & legend > span {
150
+ padding-inline: 0.25rem;
151
+ }
152
+ }
153
+ .np-field-disabled .np-field-outline {
154
+ opacity: 0.12;
155
+ }
156
+
157
+ .np-field-row {
158
+ display: flex;
159
+ flex: 1;
160
+ align-items: center;
161
+ position: relative;
162
+ min-width: 0;
163
+ min-height: 100%;
164
+ max-height: 100%;
165
+ border-radius: inherit;
166
+ }
167
+ .np-field-start,
168
+ .np-field-middle,
169
+ .np-field-end {
170
+ display: flex;
171
+ position: relative;
172
+ box-sizing: border-box;
173
+ height: 100%;
174
+ }
175
+ .np-field-start,
176
+ .np-field-end {
177
+ align-items: center;
178
+ justify-content: center;
179
+ }
180
+ .np-field-start {
181
+ color: var(--_start-color);
182
+ margin-inline: 0.75rem 1rem;
183
+ }
184
+ .np-field-end {
185
+ color: var(--_end-color);
186
+ margin-inline: 1rem 0.75rem;
187
+ }
188
+ .np-field-start:has(.np-icon-button) {
189
+ margin-inline: 0.25rem 0.5rem;
190
+ }
191
+ .np-field-end:has(.np-icon-button) {
192
+ margin-inline: 0.5rem 0.25rem;
193
+ }
194
+ .np-field-disabled :is(.np-field-start, .np-field-end) {
195
+ opacity: 0.38;
196
+ }
197
+ .np-field-icon {
198
+ display: flex;
199
+ position: relative;
200
+ align-items: center;
201
+ justify-content: center;
202
+ fill: currentColor;
203
+
204
+ & svg {
205
+ fill: currentColor;
206
+ }
207
+ }
208
+ .np-field-middle {
209
+ flex: 1;
210
+ align-self: baseline;
211
+ align-items: stretch;
212
+ min-width: 0;
213
+ }
214
+
215
+ .np-field-content {
216
+ display: flex;
217
+ flex: 1 1 0%;
218
+ min-width: 0;
219
+ color: var(--np-color-on-surface);
220
+ opacity: 0;
221
+ transition: opacity 83ms cubic-bezier(0.2, 0, 0, 1);
222
+ }
223
+
224
+ .np-field-label-wrapper {
225
+ position: absolute;
226
+ inset: 0;
227
+ pointer-events: none;
228
+ user-select: none;
229
+ text-align: initial;
230
+ margin-inline: 1rem;
231
+ }
232
+ .np-field-with-start:not(.np-field-outlined) .np-field-label-wrapper {
233
+ margin-inline-start: 0;
234
+ }
235
+ .np-field-with-end:not(.np-field-outlined) .np-field-label-wrapper {
236
+ margin-inline-end: 0;
237
+ }
238
+ .np-field-label {
239
+ position: absolute;
240
+ top: 1rem;
241
+ inset-inline-start: 0;
242
+ z-index: 1;
243
+ box-sizing: border-box;
244
+ width: min-content;
245
+ max-width: 100%;
246
+ overflow: hidden;
247
+ color: var(--_label-color);
248
+ font-size: 1rem;
249
+ line-height: 1.5rem;
250
+ text-overflow: ellipsis;
251
+ white-space: nowrap;
252
+ transition: color var(--np-motion-expressive-fast-effects);
253
+ }
254
+ .np-field-disabled .np-field-label {
255
+ opacity: 0.38;
256
+ }
257
+ .np-field-required::after {
258
+ content: '*';
259
+ }
260
+
261
+ .np-field-outlined.np-field-with-start .np-field-label-wrapper {
262
+ margin-inline-start: 3.25rem;
263
+ }
264
+ .np-field-outlined.np-field-with-end .np-field-label-wrapper {
265
+ margin-inline-end: 3.25rem;
266
+ }
267
+
268
+ .np-field:is(
269
+ .np-field-populated,
270
+ :focus,
271
+ .np-field-open,
272
+ :has(.np-field-input:is(:focus, :not(:placeholder-shown), :autofill)),
273
+ :has(.np-field-value:focus)
274
+ ) {
275
+ & .np-field-content {
276
+ opacity: 1;
277
+ }
278
+ & .np-field-label {
279
+ top: var(--_floating-label-top);
280
+ font-size: 0.75rem;
281
+ line-height: 1rem;
282
+ }
283
+ &.np-field-outlined .np-field-label-wrapper {
284
+ margin-inline: 1rem;
285
+ }
286
+ & .np-field-outline legend {
287
+ max-inline-size: 100%;
288
+ }
289
+ }
290
+ .np-field-no-label .np-field-content {
291
+ opacity: 1;
292
+ }
293
+ .np-field-disabled:is(
294
+ .np-field-no-label,
295
+ .np-field-populated,
296
+ :has(.np-field-input:is(:not(:placeholder-shown), :autofill))
297
+ )
298
+ .np-field-content {
299
+ opacity: 0.38;
300
+ }
301
+
302
+ .np-field-supporting-text {
303
+ display: flex;
304
+ justify-content: space-between;
305
+ gap: 1rem;
306
+ padding: 0.25rem 1rem 0;
307
+ color: var(--_supporting-color);
308
+ font-size: 0.75rem;
309
+ line-height: 1rem;
310
+ }
311
+ .np-field-disabled .np-field-supporting-text {
312
+ opacity: 0.38;
313
+ }
314
+
315
+ @media (prefers-reduced-motion: no-preference) {
316
+ .np-field-label {
317
+ transition:
318
+ color var(--np-motion-expressive-fast-effects),
319
+ top var(--np-motion-expressive-fast-effects),
320
+ font-size var(--np-motion-expressive-fast-effects),
321
+ line-height var(--np-motion-expressive-fast-effects);
322
+ }
323
+ .np-field-label-wrapper {
324
+ transition: margin var(--np-motion-expressive-fast-effects);
325
+ }
326
+ .np-field-outline {
327
+ transition:
328
+ border-color var(--np-motion-expressive-fast-effects),
329
+ border-width var(--np-motion-expressive-fast-effects),
330
+ inset var(--np-motion-expressive-fast-effects),
331
+ padding var(--np-motion-expressive-fast-effects);
332
+
333
+ & legend {
334
+ transition: max-inline-size var(--np-motion-expressive-fast-effects);
335
+ }
336
+ }
337
+ }
@@ -25,18 +25,33 @@
25
25
  }
26
26
  }
27
27
 
28
- .np-focus-ring:focus-visible {
29
- outline-style: solid;
30
- outline-color: var(--np-color-secondary);
31
- outline-width: 3px;
32
- outline-offset: 2px;
28
+ @property --np-focus-ring-offset {
29
+ syntax: '<length>';
30
+ inherits: false;
31
+ initial-value: 2px;
33
32
  }
34
- /* The focused state layer, on top of the ring. */
35
- .np-focus-ring:focus-visible .np-ripple-surface::before {
33
+
34
+ /* A part draws the ring of the focused element around it, for a shape smaller than its target. */
35
+ .np-focus-ring:is(:focus-visible, .np-focus-ring-shown),
36
+ .np-focus-ring-within:has(input:focus-visible),
37
+ :focus-visible > .np-focus-ring-part {
38
+ outline: 3px solid var(--np-color-secondary);
39
+ outline-offset: var(--np-focus-ring-offset);
40
+ }
41
+ .np-focus-ring-inset:is(:focus-visible, .np-focus-ring-shown, :has(input:focus-visible)) {
42
+ outline-offset: -3px;
43
+ }
44
+ .np-focus-ring:is(:focus-visible, .np-focus-ring-shown) .np-ripple-surface::before,
45
+ :focus-visible > .np-focus-ring-part .np-ripple-surface::before {
36
46
  opacity: var(--np-ripple-focus-opacity, 0.1);
37
47
  }
38
48
  @media (prefers-reduced-motion: no-preference) {
39
- .np-focus-ring:focus-visible {
49
+ .np-focus-ring:is(:focus-visible, .np-focus-ring-shown),
50
+ .np-focus-ring-within:has(input:focus-visible),
51
+ :focus-visible > .np-focus-ring-part {
40
52
  animation: focusAnimation var(--np-motion-expressive-slow-effects) forwards;
41
53
  }
54
+ .np-focus-ring-inset:is(:focus-visible, .np-focus-ring-shown, :has(input:focus-visible)) {
55
+ animation-name: focusAnimationInset;
56
+ }
42
57
  }
@@ -0,0 +1,19 @@
1
+ @media (prefers-reduced-motion: no-preference) {
2
+ .np-pop-in:popover-open {
3
+ animation:
4
+ np-fade-in var(--np-motion-expressive-fast-effects),
5
+ np-scale-in var(--np-motion-expressive-fast-spatial);
6
+ }
7
+ }
8
+
9
+ @keyframes np-fade-in {
10
+ from {
11
+ opacity: 0;
12
+ }
13
+ }
14
+
15
+ @keyframes np-scale-in {
16
+ from {
17
+ scale: 0.8;
18
+ }
19
+ }
@@ -0,0 +1,11 @@
1
+ .np-visually-hidden {
2
+ position: absolute;
3
+ width: 1px;
4
+ height: 1px;
5
+ margin: -1px;
6
+ padding: 0;
7
+ border: 0;
8
+ overflow: hidden;
9
+ clip-path: inset(50%);
10
+ white-space: nowrap;
11
+ }
@@ -77,7 +77,14 @@
77
77
  {...attributes}
78
78
  bind:this={element}
79
79
  aria-disabled="true"
80
- class={['np-item disabled', lazy && 'np-item-lazy', attributes.class]}
80
+ class={[
81
+ 'np-item',
82
+ 'disabled',
83
+ 'np-focus-ring',
84
+ 'np-focus-ring-inset',
85
+ lazy && 'np-item-lazy',
86
+ attributes.class,
87
+ ]}
81
88
  >
82
89
  {@render content()}
83
90
  </div>
@@ -85,7 +92,14 @@
85
92
  <div
86
93
  {...attributes}
87
94
  bind:this={element}
88
- class={['np-item', selected && 'selected', lazy && 'np-item-lazy', attributes.class]}
95
+ class={[
96
+ 'np-item',
97
+ 'np-focus-ring',
98
+ 'np-focus-ring-inset',
99
+ selected && 'selected',
100
+ lazy && 'np-item-lazy',
101
+ attributes.class,
102
+ ]}
89
103
  >
90
104
  {@render content()}
91
105
  </div>
@@ -94,7 +108,14 @@
94
108
  {...attributes}
95
109
  bind:this={element}
96
110
  type={buttonType(type)}
97
- class={['np-item', selected && 'selected', lazy && 'np-item-lazy', attributes.class]}
111
+ class={[
112
+ 'np-item',
113
+ 'np-focus-ring',
114
+ 'np-focus-ring-inset',
115
+ selected && 'selected',
116
+ lazy && 'np-item-lazy',
117
+ attributes.class,
118
+ ]}
98
119
  >
99
120
  {@render content()}
100
121
  <Ripple forceHover={softFocus} />
@@ -103,7 +124,14 @@
103
124
  <a
104
125
  {...attributes}
105
126
  bind:this={element}
106
- class={['np-item', selected && 'selected', lazy && 'np-item-lazy', attributes.class]}
127
+ class={[
128
+ 'np-item',
129
+ 'np-focus-ring',
130
+ 'np-focus-ring-inset',
131
+ selected && 'selected',
132
+ lazy && 'np-item-lazy',
133
+ attributes.class,
134
+ ]}
107
135
  >
108
136
  {@render content()}
109
137
  <Ripple forceHover={softFocus} />
@@ -145,7 +173,6 @@
145
173
  cursor: pointer;
146
174
  display: flex;
147
175
  flex: 1;
148
- flex-grow: 1;
149
176
  user-select: none;
150
177
  align-items: center;
151
178
  text-align: start;
@@ -203,22 +230,9 @@
203
230
  contain-intrinsic-block-size: auto var(--np-item-container-height, 4.5rem);
204
231
  }
205
232
 
206
- .np-item:focus-visible :global(.np-ripple-surface)::before {
207
- opacity: var(--np-ripple-focus-opacity, 0.1);
208
- }
209
-
210
233
  .np-item:focus-visible {
211
- outline-style: solid;
212
- outline-color: var(--np-color-secondary);
213
- outline-width: 3px;
214
- outline-offset: -3px;
215
234
  border-radius: var(--np-shape-corner-extra-small);
216
235
  }
217
- @media (prefers-reduced-motion: no-preference) {
218
- .np-item:focus-visible {
219
- animation: focusAnimationInset var(--np-motion-expressive-slow-effects) forwards;
220
- }
221
- }
222
236
 
223
237
  div.np-item {
224
238
  cursor: unset;
@@ -1,8 +1,7 @@
1
1
  <script lang="ts">
2
2
  import '../internal/exit.css'
3
- import { exitAnimation, motionOf } from '../animation.js'
4
- import { reducedMotion } from '../media.js'
5
- import type { Attachment } from 'svelte/attachments'
3
+ import '../internal/pop-in.css'
4
+ import { exitAnimation } from '../animation.js'
6
5
  import { arrowKeyNav, rovingTabindex, typeahead } from '../keyboard-nav.js'
7
6
  import { popoverController, syncOpenEffect } from '../popover.svelte.js'
8
7
  import type { MenuProps } from './types.js'
@@ -33,24 +32,6 @@
33
32
 
34
33
  const controller = popoverController(() => element)
35
34
 
36
- // Opens with a fade and a scale from the anchor, started just before the menu shows. Driven by
37
- // script rather than @starting-style, which Safari does not apply again when a popover reopens.
38
- const openAnimation: Attachment<HTMLElement> = (menu) => {
39
- const onBeforeToggle = (event: Event) => {
40
- if ((event as ToggleEvent).newState !== 'open' || reducedMotion.current) return
41
- menu.animate(
42
- [{ opacity: 0 }, { opacity: 1 }],
43
- motionOf(menu, '--np-motion-expressive-fast-effects'),
44
- )
45
- menu.animate(
46
- [{ scale: 0.8 }, { scale: 1 }],
47
- motionOf(menu, '--np-motion-expressive-fast-spatial'),
48
- )
49
- }
50
- menu.addEventListener('beforetoggle', onBeforeToggle)
51
- return () => menu.removeEventListener('beforetoggle', onBeforeToggle)
52
- }
53
-
54
35
  export const show = () => controller.show()
55
36
  export const close = () => controller.close()
56
37
 
@@ -101,7 +82,6 @@
101
82
  {role}
102
83
  bind:this={element}
103
84
  {@attach exitAnimation}
104
- {@attach openAnimation}
105
85
  ontoggle={(event) => {
106
86
  let { newState } = event
107
87
  open = newState === 'open'
@@ -111,7 +91,7 @@
111
91
  ontoggle?.(event)
112
92
  }}
113
93
  {popover}
114
- class={['np-menu-container', !coverAnchor && 'np-menu-no-cover', attributes.class]}
94
+ class={['np-menu-container', 'np-pop-in', !coverAnchor && 'np-menu-no-cover', attributes.class]}
115
95
  {style}
116
96
  onkeydown={(event) => {
117
97
  attributes.onkeydown?.(event)
@@ -162,7 +142,7 @@
162
142
  border: none;
163
143
  border-radius: var(--np-menu-container-shape, var(--np-shape-corner-extra-small));
164
144
  padding: 0;
165
- box-shadow: var(--np-elevation-2);
145
+ box-shadow: var(--np-menu-elevation, var(--np-elevation-2));
166
146
  margin: var(--np-menu-margin, 2px);
167
147
  scrollbar-color: var(--np-color-on-surface-variant) transparent;
168
148
  scrollbar-width: thin;
@@ -191,7 +171,8 @@
191
171
 
192
172
  @media (prefers-reduced-motion: no-preference) {
193
173
  /* Closes with a fade and a slight shrink towards the anchor, on the default effects curve,
194
- which is gentler at the start than the fast one. It opens through openAnimation.
174
+ which is gentler at the start than the fast one. It opens through np-pop-in, from its
175
+ keyframes, which run again on every opening where @starting-style does not in Safari.
195
176
  NativeSelect's list closes the same way. */
196
177
  .np-menu-container[popover] {
197
178
  --_exit: var(--np-motion-expressive-default-effects);
@@ -1,4 +1,5 @@
1
1
  import '../internal/exit.css';
2
+ import '../internal/pop-in.css';
2
3
  import type { MenuProps } from './types.js';
3
4
  declare const Menu: import("svelte").Component<MenuProps, {
4
5
  show: () => void;
@@ -28,6 +28,7 @@
28
28
  let isLink = $derived(attributes.href != null)
29
29
  let classes = $derived([
30
30
  'np-navigation-bar-item',
31
+ 'np-focus-ring',
31
32
  selected && 'np-navigation-bar-item-selected',
32
33
  labelBehavior === 'selected' && 'np-navigation-bar-item-label-selected',
33
34
  orientation === 'horizontal' && 'np-navigation-bar-item-horizontal',
@@ -115,7 +116,9 @@
115
116
  z-index: 1;
116
117
  }
117
118
 
118
- .np-navigation-bar-item-icon::before {
119
+ /* The active indicator: behind the icon, or behind the whole item when it lies horizontal. */
120
+ .np-navigation-bar-item-icon::before,
121
+ .np-navigation-bar-item-horizontal::before {
119
122
  content: '';
120
123
  position: absolute;
121
124
  inset: 0;
@@ -128,7 +131,8 @@
128
131
  border-radius: inherit;
129
132
  z-index: -1;
130
133
  }
131
- .np-navigation-bar-item-selected .np-navigation-bar-item-icon::before {
134
+ .np-navigation-bar-item-selected .np-navigation-bar-item-icon::before,
135
+ .np-navigation-bar-item-horizontal.np-navigation-bar-item-selected::before {
132
136
  opacity: 1;
133
137
  transform: scaleX(1);
134
138
  }
@@ -171,23 +175,6 @@
171
175
  align-self: center;
172
176
  z-index: 0;
173
177
  }
174
- .np-navigation-bar-item-horizontal::before {
175
- content: '';
176
- position: absolute;
177
- inset: 0;
178
- opacity: 0;
179
- transform: scaleX(0.32);
180
- background-color: var(
181
- --np-navigation-bar-item-active-indicator-color,
182
- var(--np-color-secondary-container)
183
- );
184
- border-radius: inherit;
185
- z-index: -1;
186
- }
187
- .np-navigation-bar-item-horizontal.np-navigation-bar-item-selected::before {
188
- opacity: 1;
189
- transform: scaleX(1);
190
- }
191
178
  .np-navigation-bar-item-horizontal .np-navigation-bar-item-icon {
192
179
  width: 1.5rem;
193
180
  height: 1.5rem;
@@ -227,16 +214,8 @@
227
214
  }
228
215
 
229
216
  .np-navigation-bar-item:focus-visible {
230
- outline-style: solid;
231
- outline-color: var(--np-color-secondary);
232
- outline-width: 3px;
233
- outline-offset: 2px;
234
217
  border-radius: 1rem;
235
218
  }
236
- /* The focused state layer, on top of the ring. */
237
- .np-navigation-bar-item:focus-visible :global(.np-ripple-surface)::before {
238
- opacity: var(--np-ripple-focus-opacity, 0.1);
239
- }
240
219
 
241
220
  @media (prefers-reduced-motion: no-preference) {
242
221
  .np-navigation-bar-item-icon::before {
@@ -247,8 +226,5 @@
247
226
  .np-navigation-bar-item-label {
248
227
  transition: color var(--np-motion-expressive-default-effects);
249
228
  }
250
- .np-navigation-bar-item:focus-visible {
251
- animation: focusAnimation var(--np-motion-expressive-slow-effects) forwards;
252
- }
253
229
  }
254
230
  </style>
@@ -43,6 +43,8 @@
43
43
  href={attributes.href}
44
44
  class={[
45
45
  'np-navigation-drawer-item',
46
+ 'np-focus-ring',
47
+ 'np-focus-ring-inset',
46
48
  selected && 'np-navigation-drawer-item-selected',
47
49
  attributes.class,
48
50
  ]}
@@ -57,6 +59,8 @@
57
59
  bind:this={element}
58
60
  class={[
59
61
  'np-navigation-drawer-item',
62
+ 'np-focus-ring',
63
+ 'np-focus-ring-inset',
60
64
  selected && 'np-navigation-drawer-item-selected',
61
65
  attributes.class,
62
66
  ]}
@@ -113,17 +117,6 @@
113
117
  }
114
118
  }
115
119
 
116
- .np-navigation-drawer-item:focus-visible {
117
- outline-style: solid;
118
- outline-color: var(--np-color-secondary);
119
- outline-width: 3px;
120
- outline-offset: -3px;
121
- }
122
- @media (prefers-reduced-motion: no-preference) {
123
- .np-navigation-drawer-item:focus-visible {
124
- animation: focusAnimationInset var(--np-motion-expressive-slow-effects) forwards;
125
- }
126
- }
127
120
  .np-navigation-drawer-item-icon {
128
121
  color: var(--np-color-on-surface-variant);
129
122
  display: flex;