bref-ui 2.0.1 → 2.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 (109) hide show
  1. package/dist/button/button.svelte +67 -47
  2. package/dist/button/types.d.ts +1 -0
  3. package/dist/checkbox/checkbox.svelte +40 -35
  4. package/dist/combobox/combobox.svelte +2 -3
  5. package/dist/combobox/input.svelte +1 -1
  6. package/dist/combobox/list.svelte +5 -5
  7. package/dist/combobox/option.svelte +29 -9
  8. package/dist/dialog/dialog.svelte +15 -12
  9. package/dist/dialog/footer.svelte +2 -2
  10. package/dist/dialog/header.svelte +4 -4
  11. package/dist/icon/icon.svelte +6 -4
  12. package/dist/pill/content.svelte +11 -4
  13. package/dist/pill/content.svelte.d.ts +1 -1
  14. package/dist/pill/on-delete.svelte +42 -13
  15. package/dist/pill/pill.svelte +74 -94
  16. package/dist/pill/registry.json +1 -1
  17. package/dist/pill/types.d.ts +1 -0
  18. package/dist/pill-choice-group/pill-choice-group.svelte +19 -0
  19. package/dist/pill-choice-group/types.d.ts +1 -0
  20. package/dist/pill-group/pill-group.svelte +2 -0
  21. package/dist/pill-group/types.d.ts +1 -0
  22. package/dist/popover/popover.svelte +31 -6
  23. package/dist/popover/types.d.ts +1 -1
  24. package/dist/progress/progress.svelte +42 -55
  25. package/dist/select/empty.svelte +2 -3
  26. package/dist/select/list.svelte +2 -1
  27. package/dist/select/option.svelte +20 -6
  28. package/dist/select/select.svelte +7 -4
  29. package/dist/select/trigger.svelte +30 -7
  30. package/dist/spinner/spinner.svelte +8 -21
  31. package/dist/surface/surface.svelte +6 -1
  32. package/dist/surface/types.d.ts +2 -0
  33. package/dist/switch/switch.svelte +48 -51
  34. package/dist/text-area/registry.json +1 -1
  35. package/dist/text-area/text-area.svelte +33 -73
  36. package/dist/text-area/types.d.ts +1 -0
  37. package/dist/text-input/registry.json +1 -1
  38. package/dist/text-input/text-input.svelte +33 -73
  39. package/dist/text-input/types.d.ts +1 -0
  40. package/dist/theme/cli-theme.svelte +2 -0
  41. package/dist/theme/glass.svelte +60 -0
  42. package/dist/theme/glass.svelte.d.ts +26 -0
  43. package/dist/theme/registry.json +1 -0
  44. package/dist/theme/styles.svelte +6 -0
  45. package/dist/theme/surface.d.ts +1 -1
  46. package/dist/theme/surface.js +3 -2
  47. package/dist/theme/surfaces.svelte +23 -45
  48. package/dist/theme/theme.svelte +2 -0
  49. package/dist/tree-view/tree-node/chevron-button.svelte +9 -9
  50. package/dist/tree-view/tree-node/content.svelte +15 -12
  51. package/dist/tree-view/tree-node/delete-button.svelte +6 -5
  52. package/dist/tree-view/tree-node/tree-node.svelte +23 -24
  53. package/dist/tree-view/tree-view-section.svelte +9 -9
  54. package/dist/tree-view/tree-view.svelte +17 -9
  55. package/dist/tree-view/types.d.ts +2 -1
  56. package/package.json +1 -1
  57. package/readme.md +3 -1
  58. package/src/lib/button/button.svelte +67 -47
  59. package/src/lib/button/types.ts +1 -0
  60. package/src/lib/checkbox/checkbox.svelte +40 -35
  61. package/src/lib/combobox/combobox.svelte +2 -3
  62. package/src/lib/combobox/input.svelte +1 -1
  63. package/src/lib/combobox/list.svelte +5 -5
  64. package/src/lib/combobox/option.svelte +29 -9
  65. package/src/lib/dialog/dialog.svelte +15 -12
  66. package/src/lib/dialog/footer.svelte +2 -2
  67. package/src/lib/dialog/header.svelte +4 -4
  68. package/src/lib/icon/icon.svelte +6 -4
  69. package/src/lib/pill/content.svelte +11 -4
  70. package/src/lib/pill/on-delete.svelte +42 -13
  71. package/src/lib/pill/pill.svelte +74 -94
  72. package/src/lib/pill/registry.json +1 -1
  73. package/src/lib/pill/types.ts +1 -0
  74. package/src/lib/pill-choice-group/pill-choice-group.svelte +19 -0
  75. package/src/lib/pill-choice-group/types.ts +1 -0
  76. package/src/lib/pill-group/pill-group.svelte +2 -0
  77. package/src/lib/pill-group/types.ts +1 -0
  78. package/src/lib/popover/popover.svelte +31 -6
  79. package/src/lib/popover/types.ts +16 -1
  80. package/src/lib/progress/progress.svelte +42 -55
  81. package/src/lib/select/empty.svelte +2 -3
  82. package/src/lib/select/list.svelte +2 -1
  83. package/src/lib/select/option.svelte +20 -6
  84. package/src/lib/select/select.svelte +7 -4
  85. package/src/lib/select/trigger.svelte +30 -7
  86. package/src/lib/spinner/spinner.svelte +8 -21
  87. package/src/lib/surface/surface.svelte +6 -1
  88. package/src/lib/surface/types.ts +2 -0
  89. package/src/lib/switch/switch.svelte +48 -51
  90. package/src/lib/text-area/registry.json +1 -1
  91. package/src/lib/text-area/text-area.svelte +33 -73
  92. package/src/lib/text-area/types.ts +1 -0
  93. package/src/lib/text-input/registry.json +1 -1
  94. package/src/lib/text-input/text-input.svelte +33 -73
  95. package/src/lib/text-input/types.ts +1 -0
  96. package/src/lib/theme/cli-theme.svelte +2 -0
  97. package/src/lib/theme/glass.svelte +60 -0
  98. package/src/lib/theme/registry.json +1 -0
  99. package/src/lib/theme/styles.svelte +6 -0
  100. package/src/lib/theme/surface.ts +4 -2
  101. package/src/lib/theme/surfaces.svelte +23 -45
  102. package/src/lib/theme/theme.svelte +2 -0
  103. package/src/lib/tree-view/tree-node/chevron-button.svelte +9 -9
  104. package/src/lib/tree-view/tree-node/content.svelte +15 -12
  105. package/src/lib/tree-view/tree-node/delete-button.svelte +6 -5
  106. package/src/lib/tree-view/tree-node/tree-node.svelte +23 -24
  107. package/src/lib/tree-view/tree-view-section.svelte +9 -9
  108. package/src/lib/tree-view/tree-view.svelte +17 -9
  109. package/src/lib/tree-view/types.ts +2 -1
@@ -1,6 +1,7 @@
1
1
  <script lang="ts">
2
2
  /* eslint-disable max-lines -- Component recipe and animation remain colocated. */
3
3
  import type { PillProps } from './types.js';
4
+ import { surface } from '../theme/surface.js';
4
5
  import Content from './content.svelte';
5
6
  import OnDelete from './on-delete.svelte';
6
7
 
@@ -15,32 +16,46 @@
15
16
  size = 'medium',
16
17
  variant = 'neutral',
17
18
  wide = false,
19
+ class: className,
20
+ glass,
18
21
  ...attributes
19
22
  }: PillProps = $props();
23
+
24
+ const nativeAttributes = $derived({
25
+ ...attributes,
26
+ onclick: onClick,
27
+ role: onClick ? 'button' : attributes.role,
28
+ tabindex: onClick ? (attributes.tabindex ?? 0) : attributes.tabindex,
29
+ 'aria-label': attributes['aria-labelledby']
30
+ ? undefined
31
+ : (attributes['aria-label'] ?? (onClick ? label : undefined)),
32
+ onkeydown: onClick
33
+ ? (event: KeyboardEvent & { currentTarget: EventTarget & HTMLDivElement }) => {
34
+ attributes.onkeydown?.(event);
35
+ if (
36
+ !event.defaultPrevented &&
37
+ event.target === event.currentTarget &&
38
+ (event.key === 'Enter' || event.key === ' ')
39
+ ) {
40
+ event.preventDefault();
41
+ if (!event.repeat) event.currentTarget.click();
42
+ }
43
+ }
44
+ : attributes.onkeydown
45
+ });
20
46
  </script>
21
47
 
22
- <!-- svelte-ignore a11y_no_noninteractive_tabindex (The button role and tab stop are enabled together.) -->
23
48
  <div
24
- {...attributes}
25
- onclick={onClick}
26
- role={onClick ? 'button' : attributes.role}
27
- tabindex={onClick ? (attributes.tabindex ?? 0) : attributes.tabindex}
28
- aria-label={attributes['aria-labelledby']
29
- ? undefined
30
- : (attributes['aria-label'] ?? (onClick ? label : undefined))}
31
- onkeydown={onClick
32
- ? (event) => {
33
- attributes.onkeydown?.(event);
34
- if (
35
- !event.defaultPrevented &&
36
- event.target === event.currentTarget &&
37
- (event.key === 'Enter' || event.key === ' ')
38
- ) {
39
- event.preventDefault();
40
- if (!event.repeat) event.currentTarget.click();
41
- }
42
- }
43
- : attributes.onkeydown}
49
+ {...nativeAttributes}
50
+ class={[
51
+ surface({
52
+ color,
53
+ variant,
54
+ glass,
55
+ hover: onClick || onDelete ? 'small' : undefined
56
+ }),
57
+ className
58
+ ]}
44
59
  data-swoosh={swoosh || undefined}
45
60
  data-clickable={onClick ? '' : undefined}
46
61
  data-icon={icon ? '' : undefined}
@@ -48,85 +63,69 @@
48
63
  data-color={color}
49
64
  data-size={size}
50
65
  data-variant={variant}
51
- style:--pill-color={`var(--color-${color}, var(--color-foreground))`}
52
66
  data-wide={wide}
53
67
  >
54
- <Content {label} {icon} {size} />
68
+ <Content {label} {icon} />
55
69
  {@render children?.()}
70
+ <!-- todo: the on delete should always be at right msot of the pill, content has to "fill the parent" -->
56
71
  {#if onDelete}<OnDelete {label} {onDelete} />{/if}
57
72
  </div>
58
73
 
59
74
  <style>
60
75
  div {
61
- --pill-tint: 3%;
62
- --pill-background: color-mix(
63
- in srgb,
64
- color-mix(in srgb, var(--pill-color) var(--pill-tint), var(--color-background, white)) 85%,
65
- transparent
66
- );
67
- --pill-border: color-mix(in srgb, var(--pill-color) 20%, transparent);
68
- --pill-highlight: color-mix(in srgb, var(--color-foreground, black) 16%, transparent);
69
- --pill-content: var(--pill-color);
70
- --pill-padding: 1.125rem;
71
- --pill-height: 3rem;
76
+ --pill-glint: color-mix(in srgb, var(--color-foreground, black) 8%, transparent);
77
+ --pill-padding: 0.75rem;
78
+ --pill-height: 2rem;
79
+ --pill-icon-size: 1.125rem;
80
+ --pill-delete-size: 1.5rem;
72
81
  box-sizing: border-box;
73
82
  display: inline-flex;
74
83
  position: relative;
75
84
  isolation: isolate;
76
85
  align-items: center;
77
- gap: 0.5rem;
78
- justify-content: center;
86
+ gap: 0.375rem;
87
+ justify-content: flex-start;
79
88
  min-width: 0;
89
+ width: fit-content;
80
90
  max-width: 100%;
81
91
  min-height: var(--pill-height);
82
- padding: 0.25rem var(--pill-padding);
83
- border: 1px solid var(--pill-border);
92
+ padding: 0.125rem var(--pill-padding);
84
93
  border-radius: 999px;
85
- background: var(--pill-background);
86
- box-shadow:
87
- inset 0 1px 0 var(--pill-highlight),
88
- 0 2px 6px color-mix(in srgb, var(--color-foreground, black) 8%, transparent);
89
- -webkit-backdrop-filter: blur(0.5rem) saturate(120%);
90
- backdrop-filter: blur(0.5rem) saturate(120%);
91
- transition: all 150ms;
92
- color: var(--pill-content);
93
94
  font: inherit;
94
95
  line-height: 1.25;
95
- text-align: center;
96
+ text-align: left;
96
97
  white-space: normal;
97
98
  overflow-wrap: anywhere;
98
99
  overflow: hidden;
99
100
  user-select: none;
100
101
  }
101
102
  div[data-size='x-small'] {
102
- --pill-height: 1.75rem;
103
+ --pill-height: 1.5rem;
103
104
  --pill-padding: 0.5rem;
104
- font-size: 0.8rem;
105
+ --pill-icon-size: 0.875rem;
106
+ font-size: 0.8125rem;
105
107
  }
106
108
  div[data-size='small'] {
107
- --pill-height: 2.5rem;
108
- --pill-padding: 0.875rem;
109
+ --pill-height: 1.75rem;
110
+ --pill-padding: 0.625rem;
111
+ --pill-icon-size: 1rem;
112
+ font-size: 0.875rem;
109
113
  }
110
114
  div[data-size='large'] {
111
- --pill-height: 4rem;
112
- --pill-padding: 1.5rem;
113
- font-size: 1.25rem;
115
+ --pill-height: 2.25rem;
116
+ --pill-padding: 0.875rem;
117
+ --pill-icon-size: 1.25rem;
118
+ --pill-delete-size: 1.75rem;
114
119
  }
115
120
  div[data-size='x-large'] {
116
- --pill-height: 5rem;
117
- --pill-padding: 2rem;
118
- font-size: 1.5rem;
119
- }
120
- div[data-variant='soft'] {
121
- --pill-tint: 16%;
122
- --pill-border: color-mix(in srgb, var(--pill-color) 30%, transparent);
123
- --pill-content: color-mix(in srgb, var(--pill-color) 80%, var(--color-foreground, black));
121
+ --pill-height: 2.5rem;
122
+ --pill-padding: 1rem;
123
+ --pill-icon-size: 1.375rem;
124
+ --pill-delete-size: 2rem;
125
+ font-size: 1.125rem;
124
126
  }
125
127
  div[data-variant='filled'] {
126
- --pill-tint: 88%;
127
- --pill-border: color-mix(in srgb, var(--pill-color) 68%, var(--color-foreground, black));
128
- --pill-highlight: color-mix(in srgb, var(--color-foreground, black) 28%, transparent);
129
- --pill-content: color-mix(in srgb, var(--color-background, white) 90%, var(--pill-color));
128
+ --pill-glint: color-mix(in srgb, var(--surface-color) 28%, transparent);
130
129
  }
131
130
  div[data-icon] {
132
131
  padding-left: calc(var(--pill-padding) * 0.6);
@@ -134,12 +133,8 @@
134
133
  div[data-removable] {
135
134
  padding-right: calc(var(--pill-padding) / 2);
136
135
  }
137
- div[data-clickable] {
138
- cursor: pointer;
139
- }
140
- div[data-clickable]:focus-visible {
141
- outline: 2px solid var(--color-primary, currentColor);
142
- outline-offset: 3px;
136
+ div[data-wide='true'] {
137
+ width: 100%;
143
138
  }
144
139
  div[data-swoosh]::before {
145
140
  position: absolute;
@@ -148,21 +143,13 @@
148
143
  inset-inline-start: -45%;
149
144
  width: 40%;
150
145
  content: '';
151
- background: linear-gradient(
152
- 90deg,
153
- transparent,
154
- color-mix(in srgb, var(--pill-color) 28%, transparent),
155
- transparent
156
- );
146
+ background: linear-gradient(90deg, transparent, var(--pill-glint), transparent);
157
147
  filter: blur(0.3rem);
158
148
  opacity: 0;
159
149
  pointer-events: none;
160
150
  transform: skewX(-18deg);
161
151
  animation: pill-swoosh 2s ease-in-out infinite;
162
152
  }
163
- div[data-wide='true'] {
164
- width: 100%;
165
- }
166
153
  @keyframes pill-swoosh {
167
154
  0%,
168
155
  20% {
@@ -179,22 +166,15 @@
179
166
  transform: translateX(400%) skewX(-18deg);
180
167
  }
181
168
  }
182
- @media (prefers-reduced-motion: reduce) {
183
- div {
184
- transition: none;
169
+ @media (pointer: coarse) {
170
+ div[data-clickable] {
171
+ min-width: 44px;
172
+ min-height: 44px;
185
173
  }
174
+ }
175
+ @media (prefers-reduced-motion: reduce) {
186
176
  div[data-swoosh]::before {
187
177
  animation: none;
188
178
  }
189
179
  }
190
- @media (forced-colors: active) {
191
- div {
192
- border-color: CanvasText;
193
- color: CanvasText;
194
- background: Canvas;
195
- box-shadow: none;
196
- -webkit-backdrop-filter: none;
197
- backdrop-filter: none;
198
- }
199
- }
200
180
  </style>
@@ -2,5 +2,5 @@
2
2
  "id": "pill",
3
3
  "files": ["pill.svelte", "content.svelte", "on-delete.svelte"],
4
4
  "exports": { "Pill": "pill.svelte" },
5
- "dependencies": ["theme", "icon", "button"]
5
+ "dependencies": ["theme", "icon"]
6
6
  }
@@ -16,4 +16,5 @@ export type PillProps = Omit<HTMLAttributes<HTMLDivElement>, 'children' | 'color
16
16
  onDelete?: () => void;
17
17
  swoosh?: boolean;
18
18
  wide?: boolean;
19
+ glass?: boolean;
19
20
  };
@@ -12,6 +12,7 @@
12
12
  size = 'medium',
13
13
  variant = 'neutral',
14
14
  color = 'foreground',
15
+ glass,
15
16
  animateSelected = false,
16
17
  ...attributes
17
18
  }: PillChoiceGroupProps = $props();
@@ -80,6 +81,7 @@
80
81
  label={item.label}
81
82
  icon={item.icon}
82
83
  {color}
84
+ {glass}
83
85
  swoosh={selected && animateSelected}
84
86
  variant={selected ? selectedVariant : variant}
85
87
  {size}
@@ -110,6 +112,15 @@
110
112
  clip-path: inset(50%);
111
113
  overflow: hidden;
112
114
  }
115
+ div:has(input:checked) {
116
+ --pill-choice-emphasis: 18%;
117
+ }
118
+ div:not([data-disabled]):is(:hover, :has(input:focus-visible)) {
119
+ --pill-choice-hover: 2%;
120
+ }
121
+ div:not([data-disabled]):active {
122
+ --pill-choice-hover: 4%;
123
+ }
113
124
  div:has(input:focus-visible) {
114
125
  outline: 2px solid var(--color-primary, currentColor);
115
126
  outline-offset: 3px;
@@ -122,6 +133,14 @@
122
133
  opacity: 0.45;
123
134
  cursor: not-allowed;
124
135
  }
136
+ @media (pointer: coarse) {
137
+ div {
138
+ display: inline-grid;
139
+ min-width: 44px;
140
+ min-height: 44px;
141
+ align-items: stretch;
142
+ }
143
+ }
125
144
  @media (forced-colors: active) {
126
145
  div:has(input:checked) {
127
146
  outline: 2px solid Highlight;
@@ -9,5 +9,6 @@ export type PillChoiceGroupProps = Omit<HTMLAttributes<HTMLUListElement>, 'child
9
9
  size?: Size;
10
10
  variant?: Exclude<Variant, 'filled'>;
11
11
  color?: PillProps['color'];
12
+ glass?: PillProps['glass'];
12
13
  animateSelected?: boolean;
13
14
  };
@@ -9,6 +9,7 @@
9
9
  color = 'foreground',
10
10
  size = 'medium',
11
11
  variant = 'neutral',
12
+ glass,
12
13
  ...attributes
13
14
  }: PillGroupProps = $props();
14
15
  </script>
@@ -21,6 +22,7 @@
21
22
  {color}
22
23
  {size}
23
24
  {variant}
25
+ {glass}
24
26
  onDelete={onDelete ? () => onDelete?.(item.id) : undefined}
25
27
  />
26
28
  {/snippet}
@@ -7,6 +7,7 @@ export type PillGroupProps = Omit<HTMLAttributes<HTMLUListElement>, 'children' |
7
7
  color?: PillProps['color'];
8
8
  size?: PillProps['size'];
9
9
  variant?: PillProps['variant'];
10
+ glass?: PillProps['glass'];
10
11
  };
11
12
  export type PillGroupContainerProps = Omit<HTMLAttributes<HTMLUListElement>, 'children'> & Pick<PillGroupProps, 'items' | 'size'> & {
12
13
  renderItem: Snippet<[PillDataProps]>;
@@ -1,8 +1,10 @@
1
1
  <script lang="ts">
2
- import { fly } from 'svelte/transition';
2
+ import { cubicOut } from 'svelte/easing';
3
3
  import { prefersReducedMotion } from 'svelte/motion';
4
+ import type { TransitionConfig } from 'svelte/transition';
4
5
  import Surface from '../surface/surface.svelte';
5
6
  import type { PopoverProps } from './types.js';
7
+ import type { SurfaceProps } from '../surface/types.js';
6
8
 
7
9
  let {
8
10
  trigger,
@@ -15,11 +17,27 @@
15
17
  let anchor: HTMLDivElement;
16
18
  let returnFocus: HTMLElement | null = null;
17
19
 
20
+ const flyGlass: (node: Element) => TransitionConfig = () => ({
21
+ duration: prefersReducedMotion.current ? 0 : 250,
22
+ easing: cubicOut,
23
+ css: (t: number, u: number) =>
24
+ `--surface-motion-offset: ${u * 10}px; --surface-motion-opacity: ${t}`
25
+ });
26
+
18
27
  const showPopover = (node: HTMLDivElement) => {
19
28
  if (node.matches(':popover-open')) return;
20
29
  returnFocus = document.activeElement instanceof HTMLElement ? document.activeElement : null;
21
30
  node.showPopover({ source: anchor.firstElementChild as HTMLElement });
22
31
  };
32
+
33
+ const surfacePropsWithDefaults: SurfaceProps = $derived({
34
+ variant: 'soft',
35
+ spacing: 'medium',
36
+ radius: 'small',
37
+ shadow: 'medium',
38
+ ...surfaceProps,
39
+ glass: true
40
+ });
23
41
  </script>
24
42
 
25
43
  <div data-trigger bind:this={anchor}>
@@ -30,7 +48,8 @@
30
48
  use:showPopover
31
49
  {id}
32
50
  popover="auto"
33
- transition:fly={{ y: 10, duration: prefersReducedMotion.current ? 0 : 250 }}
51
+ data-width={surfaceProps.width}
52
+ transition:flyGlass
34
53
  onintrostart={(event) => showPopover(event.currentTarget)}
35
54
  onoutrostart={(event) => {
36
55
  if (!event.currentTarget.matches(':popover-open')) return;
@@ -42,7 +61,7 @@
42
61
  open = false;
43
62
  }}
44
63
  >
45
- <Surface {...surfaceProps}>
64
+ <Surface {...surfacePropsWithDefaults} data-surface-motion>
46
65
  <div data-content>{@render children()}</div>
47
66
  </Surface>
48
67
  </div>
@@ -59,17 +78,23 @@
59
78
  inset: auto;
60
79
  position-area: block-end;
61
80
  position-try-fallbacks: flip-block;
62
- width: anchor-size(width);
81
+ width: fit-content;
82
+ min-width: min(anchor-size(width), calc(100vw - 1rem));
63
83
  max-width: calc(100vw - 1rem);
64
84
  max-height: min(24rem, calc(100% - 0.75rem));
65
85
  margin: 0.375rem 0;
66
86
  padding: 0;
67
87
  overflow: visible;
68
- overflow-wrap: anywhere;
88
+ overflow-wrap: break-word;
69
89
  border: 0;
70
90
  background: transparent;
71
91
  color: var(--color-foreground, black);
72
- transition: all var(--internal-duration) allow-discrete;
92
+ transition:
93
+ display var(--internal-duration) allow-discrete,
94
+ overlay var(--internal-duration) allow-discrete;
95
+ }
96
+ div[popover][data-width='fill'] {
97
+ width: anchor-size(width);
73
98
  }
74
99
  div:popover-open {
75
100
  display: flex;
@@ -1,6 +1,6 @@
1
1
  import type { SurfaceProps } from '../surface/types.ts';
2
2
  import type { Snippet } from 'svelte';
3
- export interface PopoverProps extends SurfaceProps {
3
+ export interface PopoverProps extends Pick<SurfaceProps, 'as' | 'variant' | 'color' | 'shadow' | 'hover' | 'spacing' | 'orientation' | 'width' | 'height' | 'radius' | 'scroll' | 'class' | 'style'> {
4
4
  trigger: Snippet;
5
5
  children: Snippet;
6
6
  open?: boolean;
@@ -75,24 +75,15 @@
75
75
  onpointercancel?.(event);
76
76
  }}
77
77
  />
78
- <span style:inset-inline-start={`${percentage}%`} aria-hidden="true"></span>
78
+ <span class={['glass']} style:inset-inline-start={`${percentage}%`} aria-hidden="true"></span>
79
79
  {/if}
80
80
  </div>
81
81
 
82
82
  <style>
83
83
  div {
84
84
  --progress-height: 0.375rem;
85
- --progress-track: color-mix(
86
- in srgb,
87
- color-mix(in srgb, var(--color-foreground) 16%, var(--color-background)) 85%,
88
- transparent
89
- );
90
- --progress-fill: color-mix(
91
- in srgb,
92
- color-mix(in srgb, var(--color-primary) 88%, var(--color-background)) 85%,
93
- transparent
94
- );
95
- --progress-highlight: color-mix(in srgb, var(--color-foreground) 28%, transparent);
85
+ --progress-track: color-mix(in srgb, var(--color-foreground) 22%, var(--color-background));
86
+ --progress-fill: var(--color-primary);
96
87
  position: relative;
97
88
  display: block;
98
89
  width: 100%;
@@ -112,11 +103,6 @@
112
103
  border-radius: 999px;
113
104
  appearance: none;
114
105
  background: var(--progress-track);
115
- box-shadow:
116
- inset 0 1px 0 color-mix(in srgb, var(--color-foreground) 16%, transparent),
117
- 0 2px 6px color-mix(in srgb, var(--color-foreground) 8%, transparent);
118
- -webkit-backdrop-filter: blur(0.5rem) saturate(120%);
119
- backdrop-filter: blur(0.5rem) saturate(120%);
120
106
  overflow: hidden;
121
107
  }
122
108
  progress::-webkit-progress-bar {
@@ -125,12 +111,10 @@
125
111
  progress::-webkit-progress-value {
126
112
  background: var(--progress-fill);
127
113
  border-radius: 999px;
128
- box-shadow: inset 0 1px 0 var(--progress-highlight);
129
114
  }
130
115
  progress::-moz-progress-bar {
131
116
  background: var(--progress-fill);
132
117
  border-radius: 999px;
133
- box-shadow: inset 0 1px 0 var(--progress-highlight);
134
118
  }
135
119
  div[data-unknown='true'] progress::-webkit-progress-value {
136
120
  background: transparent;
@@ -142,14 +126,13 @@
142
126
  content: '';
143
127
  position: absolute;
144
128
  top: calc(50% - var(--progress-height) / 2);
145
- left: 0;
146
- width: 100%;
129
+ inset-inline: 0 100%;
147
130
  height: var(--progress-height);
148
131
  border-radius: 999px;
149
132
  background: var(--progress-fill);
150
- box-shadow: inset 0 1px 0 var(--progress-highlight);
151
- transform-origin: left center;
152
- animation: sweep 1.8s cubic-bezier(0.4, 0, 0.2, 1) infinite;
133
+ animation:
134
+ sweep-leading 2s cubic-bezier(0.15, 0, 0.45, 1) infinite,
135
+ sweep-trailing 2s cubic-bezier(0.55, 0, 0.85, 1) infinite;
153
136
  pointer-events: none;
154
137
  }
155
138
  div[data-seekable='true'] {
@@ -160,6 +143,10 @@
160
143
  div[data-seekable='true'] progress {
161
144
  pointer-events: none;
162
145
  }
146
+ div:has(input:disabled) {
147
+ --progress-fill: var(--color-muted);
148
+ filter: grayscale(1);
149
+ }
163
150
  input {
164
151
  position: absolute;
165
152
  inset: 0;
@@ -174,19 +161,15 @@
174
161
  cursor: not-allowed;
175
162
  }
176
163
  span {
164
+ --glass-paint: color-mix(in srgb, var(--color-background) 92%, var(--progress-fill));
165
+ --surface-shadow: 0 2px 6px color-mix(in srgb, var(--color-foreground) 18%, transparent);
177
166
  box-sizing: border-box;
178
167
  position: absolute;
179
168
  top: 50%;
180
169
  width: 1.25rem;
181
170
  height: 1.25rem;
171
+ border: 3px solid var(--progress-fill);
182
172
  border-radius: 50%;
183
- background: color-mix(in srgb, var(--color-background) 25%, transparent);
184
- border: 1px solid color-mix(in srgb, var(--color-foreground) 30%, transparent);
185
- box-shadow:
186
- inset 0 1px 0 var(--progress-highlight),
187
- 0 2px 6px color-mix(in srgb, var(--color-foreground) 12%, transparent);
188
- -webkit-backdrop-filter: blur(0.125rem) saturate(120%);
189
- backdrop-filter: blur(0.125rem) saturate(120%);
190
173
  transform: translate(var(--internal-thumb-offset, -50%), -50%);
191
174
  transition:
192
175
  transform 150ms ease,
@@ -196,37 +179,30 @@
196
179
  @media (hover: hover) {
197
180
  input:not(:disabled):hover + span {
198
181
  transform: translate(var(--internal-thumb-offset, -50%), -50%) scale(1.3);
199
- box-shadow:
200
- inset 0 1px 0 var(--progress-highlight),
201
- 0 3px 10px color-mix(in srgb, var(--color-foreground) 20%, transparent);
182
+ --surface-shadow: 0 3px 10px color-mix(in srgb, var(--color-foreground) 22%, transparent);
202
183
  }
203
184
  }
204
185
  input:not(:disabled):active + span {
205
186
  transform: translate(var(--internal-thumb-offset, -50%), -50%) scale(1.5);
206
- box-shadow:
207
- inset 0 1px 0 var(--progress-highlight),
187
+ --surface-shadow:
208
188
  0 4px 14px color-mix(in srgb, var(--color-foreground) 24%, transparent),
209
- 0 0 0 4px color-mix(in srgb, var(--color-foreground) 12%, transparent);
189
+ 0 0 0 4px color-mix(in srgb, var(--progress-fill) 18%, transparent);
210
190
  }
211
191
  span:dir(rtl) {
212
192
  --internal-thumb-offset: 50%;
213
193
  }
214
194
  input:focus-visible + span {
215
- outline: 2px solid var(--color-foreground);
195
+ outline: 2px solid var(--color-primary);
216
196
  outline-offset: 3px;
217
197
  }
218
- input:disabled + span {
219
- opacity: 0.5;
220
- }
221
- @keyframes sweep {
222
- 0% {
223
- transform: translateX(0) scaleX(0);
198
+ @keyframes sweep-leading {
199
+ to {
200
+ inset-inline-end: 0;
224
201
  }
225
- 50% {
226
- transform: translateX(25%) scaleX(0.5);
227
- }
228
- 100% {
229
- transform: translateX(100%) scaleX(0);
202
+ }
203
+ @keyframes sweep-trailing {
204
+ to {
205
+ inset-inline-start: 100%;
230
206
  }
231
207
  }
232
208
  @media (prefers-reduced-motion: reduce) {
@@ -235,22 +211,33 @@
235
211
  }
236
212
  div[data-unknown='true']::after {
237
213
  animation: none;
238
- transform: translateX(32.5%) scaleX(0.35);
214
+ inset-inline: 35%;
239
215
  }
240
216
  }
241
217
  @media (forced-colors: active) {
218
+ div {
219
+ --progress-track: Canvas;
220
+ --progress-fill: Highlight;
221
+ }
222
+ div:has(input:disabled) {
223
+ --progress-fill: GrayText;
224
+ filter: none;
225
+ }
226
+ div[data-unknown='true']::after {
227
+ forced-color-adjust: none;
228
+ }
229
+ input:focus-visible + span {
230
+ outline-color: Highlight;
231
+ }
242
232
  progress {
233
+ forced-color-adjust: none;
243
234
  border: 1px solid CanvasText;
244
235
  box-shadow: none;
245
- -webkit-backdrop-filter: none;
246
- backdrop-filter: none;
247
236
  }
248
237
  span {
249
238
  background: Highlight;
250
- border-color: HighlightText;
239
+ border: 1px solid HighlightText;
251
240
  box-shadow: none;
252
- -webkit-backdrop-filter: none;
253
- backdrop-filter: none;
254
241
  }
255
242
  }
256
243
  </style>
@@ -14,9 +14,8 @@
14
14
  p {
15
15
  display: flex;
16
16
  align-items: center;
17
- flex-direction: column;
18
- justify-content: center;
19
- gap: 0.5rem;
17
+ justify-content: flex-start;
18
+ gap: 0.6rem;
20
19
  min-height: fit-content;
21
20
  margin: 0;
22
21
  padding: 0.5rem;
@@ -21,7 +21,8 @@
21
21
  <style>
22
22
  div {
23
23
  display: grid;
24
- gap: 0.375rem;
24
+ grid-template-columns: minmax(0, 1fr);
25
+ gap: 0.125rem;
25
26
  min-width: 0;
26
27
  }
27
28
  </style>