bref-ui 2.0.0 → 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 +30 -3
  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
  import { prefersReducedMotion } from 'svelte/motion';
3
3
  import { fly } from 'svelte/transition';
4
+ import { surface } from '../../theme/surface.js';
4
5
  import ChevronButton from './chevron-button.svelte';
5
6
  import DeleteButton from './delete-button.svelte';
6
7
  import Content from './content.svelte';
@@ -11,6 +12,7 @@
11
12
  item,
12
13
  selection = $bindable(),
13
14
  defaultExpanded = false,
15
+ glass = false,
14
16
  onDelete,
15
17
  indent = 0
16
18
  }: TreeNodeProps = $props();
@@ -32,10 +34,12 @@
32
34
  </script>
33
35
 
34
36
  <div
35
- in:fly|global={{ y: -4, duration: prefersReducedMotion.current ? 0 : 150 }}
37
+ class={selected ? surface({ variant: 'soft', color: 'primary', glass }) : undefined}
38
+ in:fly|global={{ x: -8, duration: prefersReducedMotion.current ? 0 : 200 }}
39
+ out:fly|global={{ x: -8, duration: prefersReducedMotion.current ? 0 : 150 }}
36
40
  data-selected={selected}
37
41
  data-disabled={item.disabled}
38
- style:padding-left={`calc(0.25rem + ${indent * 1.25}rem)`}
42
+ style:padding-left={`calc(0.25rem + ${indent} * var(--tree-action-size))`}
39
43
  >
40
44
  {#if item.children?.length}
41
45
  <ChevronButton
@@ -56,18 +60,20 @@
56
60
  />
57
61
  {/if}
58
62
  </div>
59
- {#if item.children?.length}
60
- <div role="group" id={childrenId} hidden={!expanded}>
61
- {#each item.children as child (child.id)}
62
- <RecursiveNode indent={indent + 1} item={child} bind:selection {defaultExpanded} {onDelete} />
63
- {/each}
64
- </div>
63
+ {#if item.children?.length && expanded}
64
+ {#each item.children as child (child.id)}
65
+ <RecursiveNode
66
+ indent={indent + 1}
67
+ item={child}
68
+ bind:selection
69
+ {defaultExpanded}
70
+ {onDelete}
71
+ {glass}
72
+ />
73
+ {/each}
65
74
  {/if}
66
75
 
67
76
  <style>
68
- * {
69
- transition: all 150ms;
70
- }
71
77
  div[data-selected] {
72
78
  box-sizing: border-box;
73
79
  position: relative;
@@ -79,33 +85,26 @@
79
85
  border: 1px solid transparent;
80
86
  border-radius: 999px;
81
87
  overflow-wrap: anywhere;
88
+ transition: all 150ms ease;
82
89
  }
83
90
  [data-spacer] {
84
91
  flex: 0 0 var(--tree-action-size);
85
92
  }
86
- div[role='group'] {
87
- display: contents;
88
- }
89
- div[hidden] {
90
- display: none;
91
- }
92
93
  div[data-selected='true'] {
93
- border-color: color-mix(in srgb, var(--color-primary) 30%, transparent);
94
- background: color-mix(in srgb, var(--color-primary) 16%, transparent);
95
- color: var(--color-primary);
94
+ --tree-action-color: var(--color-primary);
96
95
  }
97
96
  div[data-selected='false']:not([data-disabled='true']):hover {
98
- background: color-mix(in srgb, var(--color-primary) 12%, transparent);
97
+ background: color-mix(in srgb, var(--color-foreground) 5%, transparent);
99
98
  }
100
99
  @media (prefers-reduced-motion: reduce) {
101
- * {
100
+ div[data-selected] {
102
101
  transition: none;
103
102
  }
104
103
  }
105
104
  @media (forced-colors: active) {
106
105
  div[data-selected='true'] {
107
- border-color: Highlight;
108
- color: Highlight;
106
+ outline: 1px solid Highlight;
107
+ outline-offset: -1px;
109
108
  }
110
109
  }
111
110
  </style>
@@ -18,13 +18,10 @@
18
18
  display: flex;
19
19
  flex-direction: column;
20
20
  align-items: flex-start;
21
- gap: 0.25rem;
22
- padding: 0.75rem 0.5rem 0.25rem;
21
+ gap: 0.375rem;
22
+ padding: 1rem 0.75rem 0.75rem;
23
23
  color: var(--color-muted);
24
- font-size: 0.8em;
25
- font-weight: 600;
26
- letter-spacing: 0.06em;
27
- text-transform: uppercase;
24
+ line-height: 1.4;
28
25
  overflow-wrap: anywhere;
29
26
  }
30
27
  div {
@@ -32,11 +29,14 @@
32
29
  align-items: center;
33
30
  gap: 0.5rem;
34
31
  min-width: 0;
32
+ color: var(--color-foreground);
33
+ font-size: 0.875em;
34
+ font-weight: 500;
35
+ letter-spacing: 0.06em;
36
+ text-transform: uppercase;
35
37
  }
36
38
  p {
37
39
  margin: 0;
38
- font-weight: 400;
39
- letter-spacing: normal;
40
- text-transform: none;
40
+ font-size: 0.75em;
41
41
  }
42
42
  </style>
@@ -8,6 +8,7 @@
8
8
  sections = [],
9
9
  label = 'Tree view',
10
10
  size = 'medium',
11
+ glass = false,
11
12
  selection = $bindable(),
12
13
  defaultExpanded = false,
13
14
  onDelete,
@@ -21,7 +22,7 @@
21
22
  {#if node}
22
23
  {@render node(item)}
23
24
  {:else}
24
- <TreeNode {item} bind:selection {defaultExpanded} {onDelete} />
25
+ <TreeNode {item} bind:selection {defaultExpanded} {onDelete} {glass} />
25
26
  {/if}
26
27
  {/each}
27
28
  </div>
@@ -38,28 +39,35 @@
38
39
  </div>
39
40
 
40
41
  <style>
41
- div {
42
- --tree-height: 2.75rem;
43
- --tree-action-size: 2.25rem;
42
+ div[role='group'] {
43
+ --tree-height: 2rem;
44
+ --tree-action-size: 1.5rem;
44
45
  display: grid;
45
46
  gap: 0.375rem;
46
47
  min-width: 0;
47
48
  color: var(--color-foreground);
48
49
  font: inherit;
50
+ line-height: 1.25;
49
51
  }
50
52
  div[data-size='small'] {
51
- --tree-height: 2.5rem;
52
- --tree-action-size: 2rem;
53
+ --tree-height: 1.75rem;
53
54
  font-size: 0.875rem;
54
55
  }
55
56
  div[data-size='large'] {
56
- --tree-height: 3.25rem;
57
- --tree-action-size: 2.5rem;
57
+ --tree-height: 2.5rem;
58
+ --tree-action-size: 2rem;
58
59
  font-size: 1.125rem;
59
60
  }
60
61
  div[data-nodes] {
61
62
  display: flex;
62
63
  flex-direction: column;
63
- gap: 0.25rem;
64
+ gap: 0.125rem;
65
+ min-width: 0;
66
+ }
67
+ @media (pointer: coarse) {
68
+ div[role='group'] {
69
+ --tree-height: 2.75rem;
70
+ --tree-action-size: 2.75rem;
71
+ }
64
72
  }
65
73
  </style>
@@ -17,12 +17,13 @@ export interface TreeViewProps {
17
17
  sections?: TreeSectionProps[];
18
18
  label?: string;
19
19
  size?: BaseSize;
20
+ glass?: boolean;
20
21
  selection?: string | string[];
21
22
  defaultExpanded?: boolean;
22
23
  onDelete?: (id: string) => void;
23
24
  node?: Snippet<[TreeItemProps]>;
24
25
  }
25
- export interface TreeNodeProps extends Pick<TreeViewProps, 'selection' | 'defaultExpanded' | 'onDelete'> {
26
+ export interface TreeNodeProps extends Pick<TreeViewProps, 'selection' | 'defaultExpanded' | 'onDelete' | 'glass'> {
26
27
  item: TreeItemProps;
27
28
  indent?: number;
28
29
  }
package/package.json CHANGED
@@ -1,6 +1,16 @@
1
1
  {
2
2
  "name": "bref-ui",
3
- "version": "2.0.0",
3
+ "version": "2.1.0",
4
+ "description": "A Svelte-first, human-first, agent-first UI library for building beautiful sites and apps with components you can make your own.",
5
+ "license": "MIT",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/feuersteiner/bref-ui.git"
9
+ },
10
+ "homepage": "https://github.com/feuersteiner/bref-ui#readme",
11
+ "bugs": {
12
+ "url": "https://github.com/feuersteiner/bref-ui/issues"
13
+ },
4
14
  "scripts": {
5
15
  "dev": "vite dev",
6
16
  "build": "vite build && npm run prepack",
@@ -67,12 +77,29 @@
67
77
  "vite": "^8.0.16"
68
78
  },
69
79
  "keywords": [
70
- "svelte"
80
+ "svelte",
81
+ "svelte5",
82
+ "sveltekit",
83
+ "svelte-components",
84
+ "ui",
85
+ "ui-library",
86
+ "component-library",
87
+ "components",
88
+ "design-system",
89
+ "copy-paste",
90
+ "cli",
91
+ "native-css",
92
+ "theming",
93
+ "dark-mode",
94
+ "accessible",
95
+ "a11y",
96
+ "ai-friendly",
97
+ "agent-friendly"
71
98
  ],
72
99
  "dependencies": {
73
100
  "@fontsource-variable/material-symbols-outlined": "^5.3.5"
74
101
  },
75
102
  "bin": {
76
- "bref-ui": "./cli/index.ts"
103
+ "bref-ui": "cli/index.ts"
77
104
  }
78
105
  }
package/readme.md CHANGED
@@ -86,7 +86,9 @@ Button use the same paint recipe; apply it to native HTML with the typed helper:
86
86
 
87
87
  All options are optional: variant defaults to neutral and color to foreground;
88
88
  shadow and hover accept small, medium or large. Shadow also accepts true for medium
89
- and false for none. SurfaceProps extends SurfaceBaseProps with component props.
89
+ and false for none. Set `glass: true` for translucent paint and backdrop blur, or use
90
+ `<Surface glass>` and `<Button glass>`; glass defaults to false.
91
+ SurfaceProps extends SurfaceBaseProps with component props.
90
92
  Supply padding, radius and layout
91
93
  in element styles. The helper adds classes only; Theme loads their CSS. SurfaceBaseProps
92
94
  is available from `bref-ui/types`. Copied components include the helper in
@@ -3,12 +3,13 @@
3
3
  import Icon from '../icon/icon.svelte';
4
4
  import { surface } from '../theme/surface.js';
5
5
  import type { ButtonProps } from './types.js';
6
- import type { HTMLAnchorAttributes, HTMLButtonAttributes } from 'svelte/elements';
6
+ import type { HTMLButtonAttributes } from 'svelte/elements';
7
7
 
8
8
  let {
9
9
  color = 'foreground',
10
10
  size = 'medium',
11
11
  variant = 'neutral',
12
+ glass = false,
12
13
  label,
13
14
  icon,
14
15
  trailingIcon,
@@ -22,9 +23,8 @@
22
23
  }: ButtonProps = $props();
23
24
 
24
25
  const isIcon = $derived(label === undefined);
25
- const isLink = $derived(href !== undefined);
26
26
  const common = $derived({
27
- class: ['button', surface({ variant, color, shadow: 'small', hover: 'medium' }), className],
27
+ class: ['button', surface({ variant, color, glass, hover: 'medium' }), className],
28
28
  'aria-label': attributes['aria-label'] ?? (isIcon ? (icon?.label ?? icon?.name) : undefined),
29
29
  'data-color': color,
30
30
  'data-size': size,
@@ -33,9 +33,24 @@
33
33
  'data-wide': wide && !isIcon ? '' : undefined,
34
34
  'data-rounded': rounded && isIcon ? '' : undefined
35
35
  });
36
+ const native = $derived(
37
+ href !== undefined
38
+ ? {
39
+ href: disabled ? undefined : href,
40
+ 'aria-disabled': disabled ? true : attributes['aria-disabled'],
41
+ tabindex: disabled ? -1 : attributes.tabindex
42
+ }
43
+ : { type: (attributes as HTMLButtonAttributes).type ?? 'button', disabled }
44
+ );
36
45
  </script>
37
46
 
38
- {#snippet content()}
47
+ <svelte:element
48
+ this={href !== undefined ? 'a' : 'button'}
49
+ {...attributes}
50
+ {...common}
51
+ {...native}
52
+ onclick={disabled && href !== undefined ? (event: MouseEvent) => event.preventDefault() : onClick}
53
+ >
39
54
  {#if icon}
40
55
  <span><Icon {...icon} color={undefined} label={undefined} /></span>
41
56
  {/if}
@@ -45,77 +60,64 @@
45
60
  <span><Icon {...trailingIcon} color={undefined} label={undefined} /></span>
46
61
  {/if}
47
62
  {/if}
48
- {/snippet}
49
-
50
- {#if isLink}
51
- <!-- eslint-disable svelte/no-navigation-without-resolve -- Native library links also support consumers outside SvelteKit. -->
52
- <a
53
- {...attributes as HTMLAnchorAttributes}
54
- {...common}
55
- href={disabled ? undefined : href}
56
- aria-disabled={disabled ? true : attributes['aria-disabled']}
57
- tabindex={disabled ? -1 : attributes.tabindex}
58
- onclick={disabled ? (event) => event.preventDefault() : onClick}
59
- >
60
- {@render content()}
61
- </a>
62
- <!-- eslint-enable svelte/no-navigation-without-resolve -->
63
- {:else}
64
- <button
65
- {...attributes as HTMLButtonAttributes}
66
- {...common}
67
- type={(attributes as HTMLButtonAttributes).type ?? 'button'}
68
- {disabled}
69
- onclick={onClick}
70
- >
71
- {@render content()}
72
- </button>
73
- {/if}
63
+ </svelte:element>
74
64
 
75
65
  <style>
76
66
  .button {
77
- --internal-height: max(48px, 3rem);
78
- --internal-padding: 1.125rem;
79
- --internal-icon-size: calc(var(--internal-height) / 2 + 0.25rem);
67
+ --internal-height: max(40px, 2.5rem);
68
+ --internal-target: 24px;
69
+ --internal-padding: 1rem;
70
+ --internal-icon-size: 1.25rem;
71
+ --internal-font-size: 1rem;
80
72
  box-sizing: border-box;
81
73
  display: inline-flex;
82
74
  align-items: center;
83
75
  justify-content: center;
84
- text-transform: capitalize;
85
76
  gap: 0.5rem;
86
- min-height: var(--internal-height);
87
- min-width: 24px;
77
+ min-height: max(var(--internal-height), var(--internal-target));
78
+ min-width: var(--internal-target);
88
79
  padding: 0.25rem var(--internal-padding);
89
- border-radius: 0.45rem;
80
+ border-radius: 0.625rem;
90
81
  font: inherit;
91
- font-size: 1rem;
82
+ font-size: var(--internal-font-size);
83
+ line-height: 1.25;
92
84
  text-decoration: none;
93
85
  }
94
86
  .button[data-size='x-small'] {
95
87
  --internal-height: max(28px, 1.75rem);
96
- --internal-padding: 0.5rem;
88
+ --internal-padding: 0.625rem;
89
+ --internal-icon-size: 1rem;
90
+ --internal-font-size: 0.8125rem;
97
91
  }
98
92
  .button[data-size='small'] {
99
- --internal-height: max(40px, 2.5rem);
100
- --internal-padding: 0.875rem;
93
+ --internal-height: max(32px, 2rem);
94
+ --internal-padding: 0.75rem;
95
+ --internal-icon-size: 1.125rem;
96
+ --internal-font-size: 0.875rem;
101
97
  }
102
98
  .button[data-size='large'] {
103
- --internal-height: max(64px, 4rem);
104
- --internal-padding: 1.5rem;
99
+ --internal-height: max(48px, 3rem);
100
+ --internal-padding: 1.25rem;
101
+ --internal-icon-size: 1.5rem;
105
102
  }
106
103
  .button[data-size='x-large'] {
107
- --internal-height: max(80px, 5rem);
108
- --internal-padding: 2rem;
104
+ --internal-height: max(56px, 3.5rem);
105
+ --internal-padding: 1.5rem;
106
+ --internal-icon-size: 1.75rem;
107
+ --internal-font-size: 1.125rem;
109
108
  }
110
109
  .button[data-kind='icon'] {
111
- width: var(--internal-height);
112
- height: var(--internal-height);
110
+ width: max(var(--internal-height), var(--internal-target));
111
+ height: max(var(--internal-height), var(--internal-target));
113
112
  padding: 0;
114
113
  font-size: var(--internal-icon-size);
115
114
  }
116
115
  span {
117
116
  display: inline-flex;
117
+ align-items: center;
118
+ flex-shrink: 0;
118
119
  font-size: var(--internal-icon-size);
120
+ line-height: 1;
119
121
  }
120
122
  .button[data-wide] {
121
123
  width: 100%;
@@ -123,4 +125,22 @@
123
125
  .button[data-rounded] {
124
126
  border-radius: 50%;
125
127
  }
128
+ .button:is(:disabled, [aria-disabled='true']) {
129
+ --surface-color: var(--color-muted, gray);
130
+ }
131
+ .button[data-variant='filled']:not(:disabled, [aria-disabled='true']):is(:hover, :focus-visible) {
132
+ filter: brightness(1.1);
133
+ }
134
+ .button[data-variant='filled']:not(:disabled, [aria-disabled='true']):is(
135
+ :active,
136
+ [aria-pressed='true'],
137
+ [data-surface-pressed='true']
138
+ ) {
139
+ filter: brightness(0.85);
140
+ }
141
+ @media (pointer: coarse) {
142
+ .button {
143
+ --internal-target: 44px;
144
+ }
145
+ }
126
146
  </style>
@@ -35,6 +35,7 @@ export type ButtonProps = {
35
35
  color?: Exclude<Color, 'background'>;
36
36
  size?: Size;
37
37
  variant?: Variant;
38
+ glass?: boolean;
38
39
  disabled?: boolean;
39
40
  onClick?: DOMAttributes<HTMLButtonElement | HTMLAnchorElement>['onclick'];
40
41
  } & Omit<DOMAttributes<HTMLButtonElement | HTMLAnchorElement>, 'children' | 'onclick'> &
@@ -21,12 +21,24 @@
21
21
  --internal-size: 1.25rem;
22
22
  --internal-tint: var(--color-foreground, black);
23
23
  --internal-strength-light: 4%;
24
- --internal-strength-dark: 16%;
25
- --internal-border: light-dark(
26
- color-mix(in srgb, var(--color-foreground, black) 12%, transparent),
27
- color-mix(in srgb, var(--color-foreground, black) 30%, transparent)
24
+ --internal-strength-dark: 8%;
25
+ --internal-fill: light-dark(
26
+ color-mix(
27
+ in srgb,
28
+ var(--internal-tint) var(--internal-strength-light),
29
+ var(--color-background, white)
30
+ ),
31
+ color-mix(
32
+ in srgb,
33
+ var(--internal-tint) var(--internal-strength-dark),
34
+ var(--color-background, white)
35
+ )
36
+ );
37
+ --internal-border: color-mix(
38
+ in srgb,
39
+ var(--color-foreground, black) 48%,
40
+ var(--color-background, white)
28
41
  );
29
- --internal-highlight: color-mix(in srgb, var(--color-foreground, black) 12%, transparent);
30
42
  box-sizing: border-box;
31
43
  position: relative;
32
44
  display: inline-flex;
@@ -37,30 +49,14 @@
37
49
  block-size: var(--internal-size);
38
50
  border: 1px solid var(--internal-border);
39
51
  border-radius: calc(var(--internal-size) * 0.2);
40
- background: color-mix(
41
- in srgb,
42
- light-dark(
43
- color-mix(
44
- in srgb,
45
- var(--internal-tint) var(--internal-strength-light),
46
- var(--color-background, white)
47
- ),
48
- color-mix(
49
- in srgb,
50
- var(--internal-tint) var(--internal-strength-dark),
51
- var(--color-background, white)
52
- )
53
- )
54
- 85%,
55
- transparent
56
- );
57
- box-shadow: inset 0 1px 0 var(--internal-highlight);
52
+ background: var(--internal-fill);
58
53
  color: var(--color-foreground, black);
59
54
  font-size: calc(var(--internal-size) * 0.8);
60
55
  vertical-align: middle;
61
- transition: all 150ms ease;
62
- -webkit-backdrop-filter: blur(0.5rem) saturate(120%);
63
- backdrop-filter: blur(0.5rem) saturate(120%);
56
+ transition:
57
+ background-color 150ms ease,
58
+ border-color 150ms ease,
59
+ color 150ms ease;
64
60
  }
65
61
  input {
66
62
  position: absolute;
@@ -89,26 +85,35 @@
89
85
  --internal-size: 1.75rem;
90
86
  }
91
87
  div:has(input:hover:not(:disabled):not(:checked)) {
92
- --internal-strength-light: 6%;
93
- --internal-strength-dark: 20%;
88
+ --internal-strength-light: 8%;
89
+ --internal-strength-dark: 12%;
94
90
  }
95
91
  div:has(input:checked) {
96
92
  --internal-tint: var(--color-primary, blue);
97
93
  --internal-strength-light: 88%;
98
94
  --internal-strength-dark: 88%;
99
- --internal-border: color-mix(in srgb, var(--internal-tint) 68%, var(--color-foreground, black));
100
- --internal-highlight: color-mix(in srgb, var(--color-foreground, black) 28%, transparent);
101
- color: color-mix(in srgb, var(--color-background, white) 90%, var(--internal-tint));
95
+ --internal-border: var(--internal-fill);
96
+ color: var(--color-background, white);
102
97
  }
103
98
  div:has(input:checked:hover:not(:disabled)) {
104
99
  --internal-strength-light: 92%;
105
100
  --internal-strength-dark: 92%;
106
101
  }
102
+ div:has(input:active:not(:disabled)) {
103
+ --internal-strength-light: 12%;
104
+ --internal-strength-dark: 16%;
105
+ }
106
+ div:has(input:checked:active:not(:disabled)) {
107
+ --internal-strength-light: 96%;
108
+ --internal-strength-dark: 96%;
109
+ }
110
+ div:has(input:user-invalid),
111
+ div:has(input[aria-invalid='true']) {
112
+ --internal-border: var(--color-error, #bc2436);
113
+ }
107
114
  div:has(input:focus-visible) {
108
- --internal-border: color-mix(in srgb, var(--color-primary, blue) 55%, transparent);
109
- box-shadow:
110
- inset 0 1px 0 var(--internal-highlight),
111
- 0 0 0 3px color-mix(in srgb, var(--color-primary, blue) 16%, transparent);
115
+ outline: 2px solid var(--color-primary, blue);
116
+ outline-offset: 3px;
112
117
  }
113
118
  div:has(input:disabled) {
114
119
  opacity: 0.5;
@@ -91,9 +91,8 @@
91
91
  >
92
92
  <Popover
93
93
  bind:open={() => open, setOpen}
94
- variant="filled"
95
- spacing="small"
96
- radius="small"
94
+ spacing="x-small"
95
+ radius="0.75rem"
97
96
  shadow
98
97
  scroll
99
98
  disabled={disabled || !!attributes.readonly}
@@ -88,7 +88,7 @@
88
88
  ? undefined
89
89
  : selected.length === 1 && selected[0].icon
90
90
  ? { ...selected[0].icon }
91
- : undefined}
91
+ : { name: 'unfold_more' }}
92
92
  value={display}
93
93
  role="combobox"
94
94
  aria-autocomplete="list"
@@ -54,17 +54,17 @@
54
54
 
55
55
  <style>
56
56
  div {
57
- --internal-height: 2.5rem;
57
+ --internal-row-height: 2.125rem;
58
58
  --internal-icon-size: 1.25rem;
59
59
  display: grid;
60
- gap: 0.375rem;
60
+ gap: 0.125rem;
61
61
  }
62
62
  div[data-size='small'] {
63
- --internal-height: 2rem;
63
+ --internal-row-height: 1.875rem;
64
64
  --internal-icon-size: 1rem;
65
65
  }
66
66
  div[data-size='large'] {
67
- --internal-height: 3rem;
67
+ --internal-row-height: 2.375rem;
68
68
  --internal-icon-size: 1.5rem;
69
69
  }
70
70
  p {
@@ -72,7 +72,7 @@
72
72
  align-items: center;
73
73
  gap: 0.6rem;
74
74
  margin: 0;
75
- padding-block: 0.5rem;
75
+ padding: 0.5rem;
76
76
  color: var(--color-muted);
77
77
  }
78
78
  </style>