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
@@ -8,7 +8,7 @@
8
8
  <button type="button" aria-pressed={selected} {disabled} onclick={onSelect}>
9
9
  {#if icon}<span data-icon><Icon {...icon} /></span>{/if}
10
10
  <span data-label>{label}</span>
11
- {#if selected}<span data-icon><Icon name="check" /></span>{/if}
11
+ {#if selected}<span data-icon data-check><Icon name="check" /></span>{/if}
12
12
  </button>
13
13
 
14
14
  <style>
@@ -18,15 +18,17 @@
18
18
  align-items: center;
19
19
  gap: 0.6rem;
20
20
  width: 100%;
21
- min-height: var(--internal-height);
21
+ min-width: 0;
22
+ min-height: var(--internal-row-height);
22
23
  padding: 0.375rem 0.65rem;
23
24
  border: 0;
24
- border-radius: 0.4rem;
25
+ border-radius: 0.5rem;
25
26
  background: transparent;
26
27
  color: var(--color-foreground);
27
28
  font: inherit;
28
29
  text-align: left;
29
30
  cursor: pointer;
31
+ transition: background-color 150ms ease;
30
32
  }
31
33
  [data-label] {
32
34
  flex: 1;
@@ -40,11 +42,18 @@
40
42
  flex: 0 0 auto;
41
43
  font-size: var(--internal-icon-size);
42
44
  }
43
- button:hover:not(:disabled),
44
- button[aria-pressed='true'] {
45
- background: color-mix(in srgb, var(--color-primary) 12%, transparent);
45
+ [data-check] {
46
46
  color: var(--color-primary);
47
47
  }
48
+ button[aria-pressed='true'] {
49
+ background: color-mix(in srgb, var(--color-primary) 8%, transparent);
50
+ }
51
+ button:hover:not(:disabled) {
52
+ background: color-mix(in srgb, var(--color-foreground) 6%, transparent);
53
+ }
54
+ button[aria-pressed='true']:hover:not(:disabled) {
55
+ background: color-mix(in srgb, var(--color-primary) 14%, transparent);
56
+ }
48
57
  button:focus-visible {
49
58
  outline: 2px solid var(--color-primary);
50
59
  outline-offset: -2px;
@@ -53,6 +62,11 @@
53
62
  opacity: 0.5;
54
63
  cursor: not-allowed;
55
64
  }
65
+ @media (prefers-reduced-motion: reduce) {
66
+ button {
67
+ transition: none;
68
+ }
69
+ }
56
70
  @media (forced-colors: active) {
57
71
  button:focus-visible {
58
72
  outline-color: Highlight;
@@ -32,7 +32,7 @@
32
32
  </script>
33
33
 
34
34
  <div data-size={size}>
35
- <Popover bind:open variant="filled" spacing="small" radius="small" shadow scroll {disabled}>
35
+ <Popover bind:open spacing="x-small" radius="0.75rem" width="fill" shadow scroll {disabled}>
36
36
  {#snippet trigger()}
37
37
  <Trigger
38
38
  {id}
@@ -51,7 +51,8 @@
51
51
  <style>
52
52
  div {
53
53
  --internal-height: 2.5rem;
54
- --internal-padding: 0.85rem;
54
+ --internal-row-height: 2.125rem;
55
+ --internal-padding: 1rem;
55
56
  --internal-icon-size: 1.25rem;
56
57
  position: relative;
57
58
  width: 100%;
@@ -59,12 +60,14 @@
59
60
  }
60
61
  div[data-size='small'] {
61
62
  --internal-height: 2rem;
62
- --internal-padding: 0.65rem;
63
+ --internal-row-height: 1.875rem;
64
+ --internal-padding: 0.75rem;
63
65
  --internal-icon-size: 1rem;
64
66
  }
65
67
  div[data-size='large'] {
66
68
  --internal-height: 3rem;
67
- --internal-padding: 1rem;
69
+ --internal-row-height: 2.375rem;
70
+ --internal-padding: 1.125rem;
68
71
  --internal-icon-size: 1.5rem;
69
72
  }
70
73
  </style>
@@ -9,13 +9,12 @@
9
9
  <button {id} type="button" data-size={size} {disabled} aria-expanded={open} onclick={onToggle}>
10
10
  <Surface
11
11
  as="span"
12
- variant="soft"
12
+ variant="neutral"
13
13
  color="foreground"
14
14
  orientation="horizontal"
15
- radius="small"
15
+ radius="0.625rem"
16
16
  width="fill"
17
17
  height="fill"
18
- hover={disabled ? undefined : 'small'}
19
18
  >
20
19
  {#if selected.length === 1 && selected[0].icon}
21
20
  <span data-icon><Icon {...selected[0].icon} /></span>
@@ -23,7 +22,7 @@
23
22
  <span data-value data-placeholder={selected.length === 0 || undefined}>
24
23
  {selected.length ? selected.map((item) => item.label).join(', ') : placeholder}
25
24
  </span>
26
- <span data-icon><Icon name="arrow_drop_down" /></span>
25
+ <span data-icon data-chevron><Icon name="keyboard_arrow_down" /></span>
27
26
  </Surface>
28
27
  </button>
29
28
 
@@ -35,12 +34,16 @@
35
34
  height: var(--internal-height);
36
35
  padding: 0;
37
36
  border: 0;
38
- border-radius: 0.5rem;
39
- background: transparent;
37
+ border-radius: 0.625rem;
38
+ background: light-dark(
39
+ color-mix(in srgb, var(--color-foreground) 6%, var(--color-background)),
40
+ color-mix(in srgb, var(--color-foreground) 10%, var(--color-background))
41
+ );
40
42
  color: var(--color-foreground);
41
43
  font: inherit;
42
44
  text-align: left;
43
45
  cursor: pointer;
46
+ transition: background-color 150ms ease;
44
47
  }
45
48
  span:first-child {
46
49
  margin-inline-start: var(--internal-padding);
@@ -61,7 +64,9 @@
61
64
  font-weight: 200;
62
65
  line-height: 1.4;
63
66
  }
64
- [data-placeholder],
67
+ [data-placeholder] {
68
+ color: color-mix(in srgb, var(--color-muted) 90%, var(--color-foreground));
69
+ }
65
70
  [data-icon] {
66
71
  color: var(--color-muted);
67
72
  }
@@ -70,6 +75,18 @@
70
75
  flex: 0 0 auto;
71
76
  font-size: var(--internal-icon-size);
72
77
  }
78
+ button:hover:not(:disabled) {
79
+ background: light-dark(
80
+ color-mix(in srgb, var(--color-foreground) 8%, var(--color-background)),
81
+ color-mix(in srgb, var(--color-foreground) 12%, var(--color-background))
82
+ );
83
+ }
84
+ [data-chevron] {
85
+ transition: transform 150ms ease;
86
+ }
87
+ button[aria-expanded='true'] [data-chevron] {
88
+ transform: rotate(180deg);
89
+ }
73
90
  button:focus-visible {
74
91
  outline: 2px solid var(--color-primary);
75
92
  outline-offset: 2px;
@@ -78,6 +95,12 @@
78
95
  opacity: 0.5;
79
96
  cursor: not-allowed;
80
97
  }
98
+ @media (prefers-reduced-motion: reduce) {
99
+ button,
100
+ [data-chevron] {
101
+ transition: none;
102
+ }
103
+ }
81
104
  @media (forced-colors: active) {
82
105
  button:focus-visible {
83
106
  outline-color: Highlight;
@@ -16,23 +16,15 @@
16
16
  initial-value: 90deg;
17
17
  }
18
18
  div {
19
- --spinner-track: color-mix(
20
- in srgb,
21
- color-mix(in srgb, var(--color-foreground) 16%, var(--color-background)) 85%,
22
- transparent
23
- );
24
- --spinner-fill: color-mix(
25
- in srgb,
26
- color-mix(in srgb, var(--color-primary) 88%, var(--color-background)) 85%,
27
- transparent
28
- );
19
+ --spinner-track: color-mix(in srgb, currentColor 18%, transparent);
20
+ --spinner-fill: currentColor;
29
21
  display: inline-flex;
30
22
  align-items: center;
31
23
  justify-content: center;
32
24
  width: 1em;
33
25
  height: 1em;
34
26
  font-size: inherit;
35
- color: var(--color-primary);
27
+ color: inherit;
36
28
  }
37
29
  div[data-size='x-small'] {
38
30
  font-size: 0.875rem;
@@ -53,14 +45,11 @@
53
45
  box-sizing: border-box;
54
46
  width: 80%;
55
47
  height: 80%;
56
- padding: max(2px, 0.12em);
48
+ padding: max(1px, 0.055em);
57
49
  background: conic-gradient(
58
50
  var(--spinner-fill) 0deg var(--spinner-sweep),
59
51
  var(--spinner-track) var(--spinner-sweep) 360deg
60
52
  );
61
- box-shadow: inset 0 1px 0 color-mix(in srgb, var(--color-foreground) 28%, transparent);
62
- -webkit-backdrop-filter: blur(0.5rem) saturate(120%);
63
- backdrop-filter: blur(0.5rem) saturate(120%);
64
53
  mask:
65
54
  linear-gradient(black, black) content-box,
66
55
  linear-gradient(black, black);
@@ -85,8 +74,8 @@
85
74
  transform: rotate(0deg);
86
75
  }
87
76
  50% {
88
- --spinner-sweep: 270deg;
89
- transform: rotate(180deg);
77
+ --spinner-sweep: 210deg;
78
+ transform: rotate(90deg);
90
79
  }
91
80
  100% {
92
81
  --spinner-sweep: 30deg;
@@ -95,6 +84,7 @@
95
84
  }
96
85
  @media (prefers-reduced-motion: reduce) {
97
86
  span {
87
+ --spinner-sweep: 90deg;
98
88
  animation: none;
99
89
  }
100
90
  }
@@ -102,13 +92,10 @@
102
92
  span {
103
93
  padding: 0;
104
94
  mask: none;
105
- border: max(2px, 0.12em) solid CanvasText;
95
+ border: max(1px, 0.055em) solid CanvasText;
106
96
  border-color: CanvasText;
107
97
  border-top-color: Highlight;
108
98
  background: none;
109
- box-shadow: none;
110
- -webkit-backdrop-filter: none;
111
- backdrop-filter: none;
112
99
  }
113
100
  }
114
101
  </style>
@@ -7,6 +7,7 @@
7
7
  children,
8
8
  color = 'background',
9
9
  variant = 'neutral',
10
+ glass = false,
10
11
  spacing,
11
12
  orientation = 'vertical',
12
13
  width = 'fill',
@@ -35,7 +36,7 @@
35
36
  this={as}
36
37
  {...attributes}
37
38
  data-surface
38
- class={[surface({ variant, color, shadow, hover }), className]}
39
+ class={[surface({ variant, color, glass, shadow, hover }), className]}
39
40
  data-variant={variant}
40
41
  data-spacing={spacing}
41
42
  data-orientation={orientation}
@@ -64,6 +65,10 @@
64
65
  border-radius: var(--surface-radius, 0);
65
66
  overflow: visible;
66
67
  }
68
+ [data-surface][data-surface-motion] {
69
+ opacity: var(--surface-motion-opacity, 1);
70
+ translate: 0 var(--surface-motion-offset, 0px);
71
+ }
67
72
  [data-orientation='horizontal'] {
68
73
  flex-direction: row;
69
74
  align-items: center;
@@ -9,6 +9,8 @@ export interface SurfaceBaseProps {
9
9
  shadow?: BaseSize | boolean;
10
10
  /** Optional hover treatment strength. */
11
11
  hover?: BaseSize;
12
+ /** Enable translucent paint and backdrop blur. */
13
+ glass?: boolean;
12
14
  }
13
15
  export interface SurfaceProps extends SurfaceBaseProps, Omit<HTMLAttributes<HTMLElement>, 'color'> {
14
16
  /** Native root element; defaults to div. */
@@ -11,9 +11,9 @@
11
11
 
12
12
  <style>
13
13
  [data-switch] {
14
- --track-width: 3.25rem;
15
- --track-height: 2rem;
16
- --thumb-size: 1.5rem;
14
+ --track-width: 2.75rem;
15
+ --track-height: 1.625rem;
16
+ --thumb-size: 1.125rem;
17
17
  --inset: 0.25rem;
18
18
  position: relative;
19
19
  display: inline-flex;
@@ -35,43 +35,24 @@
35
35
 
36
36
  [data-switch] > span {
37
37
  --internal-tint: var(--color-foreground, black);
38
- --internal-strength-light: 4%;
39
- --internal-strength-dark: 16%;
40
- --internal-highlight: color-mix(in srgb, var(--color-foreground, black) 16%, transparent);
38
+ --internal-strength: 48%;
39
+ --internal-fill: color-mix(
40
+ in srgb,
41
+ var(--internal-tint) var(--internal-strength),
42
+ var(--color-background, white)
43
+ );
44
+ --internal-border: var(--internal-fill);
41
45
  box-sizing: border-box;
42
46
  position: relative;
43
47
  width: 100%;
44
48
  height: 100%;
45
- border: 1px solid
46
- light-dark(
47
- color-mix(in srgb, var(--color-foreground, black) 12%, transparent),
48
- color-mix(in srgb, var(--color-foreground, black) 30%, transparent)
49
- );
49
+ border: 1px solid var(--internal-border);
50
50
  border-radius: 999px;
51
- background: color-mix(
52
- in srgb,
53
- light-dark(
54
- color-mix(
55
- in srgb,
56
- var(--internal-tint) var(--internal-strength-light),
57
- var(--color-background, white)
58
- ),
59
- color-mix(
60
- in srgb,
61
- var(--internal-tint) var(--internal-strength-dark),
62
- var(--color-background, white)
63
- )
64
- )
65
- 85%,
66
- transparent
67
- );
68
- box-shadow:
69
- inset 0 1px 0 var(--internal-highlight),
70
- 0 2px 6px color-mix(in srgb, black 8%, transparent);
71
- -webkit-backdrop-filter: blur(0.5rem) saturate(120%);
72
- backdrop-filter: blur(0.5rem) saturate(120%);
51
+ background: var(--internal-fill);
73
52
  pointer-events: none;
74
- transition: all 150ms ease;
53
+ transition:
54
+ background-color 150ms ease,
55
+ border-color 150ms ease;
75
56
  }
76
57
 
77
58
  [data-switch] > span > span {
@@ -84,27 +65,42 @@
84
65
  width: var(--thumb-size);
85
66
  height: var(--thumb-size);
86
67
  border-radius: 50%;
87
- background: var(--color-foreground);
88
- color: var(--color-background);
68
+ background: var(--color-background, white);
69
+ color: var(--color-foreground, black);
70
+ box-shadow: 0 1px 2px color-mix(in srgb, var(--color-background, white) 12%, transparent);
89
71
  font-size: calc(var(--thumb-size) * 0.7);
90
72
  transform: translateY(-50%);
91
- transition:
92
- left 150ms ease,
93
- background 150ms ease;
73
+ transition: left 150ms ease;
74
+ }
75
+
76
+ input:hover:not(:disabled) + span {
77
+ --internal-strength: 52%;
78
+ }
79
+
80
+ input:active:not(:disabled) + span {
81
+ --internal-strength: 56%;
94
82
  }
95
83
 
96
84
  input:checked + span {
97
85
  --internal-tint: var(--color-primary, blue);
98
- --internal-strength-light: 88%;
99
- --internal-strength-dark: 88%;
100
- --internal-highlight: color-mix(in srgb, var(--color-foreground, black) 28%, transparent);
101
- border-color: color-mix(in srgb, var(--internal-tint) 68%, var(--color-foreground, black));
86
+ --internal-strength: 88%;
87
+ }
88
+
89
+ input:checked:hover:not(:disabled) + span {
90
+ --internal-strength: 92%;
91
+ }
92
+
93
+ input:checked:active:not(:disabled) + span {
94
+ --internal-strength: 96%;
102
95
  }
103
96
 
104
97
  input:checked + span > span {
105
98
  left: calc(100% - var(--thumb-size) - var(--inset));
106
- background: var(--color-background);
107
- color: var(--color-foreground);
99
+ }
100
+
101
+ input:user-invalid + span,
102
+ input[aria-invalid='true'] + span {
103
+ --internal-border: var(--color-error, #bc2436);
108
104
  }
109
105
 
110
106
  input:focus-visible + span {
@@ -121,15 +117,15 @@
121
117
  }
122
118
 
123
119
  [data-size='small'] {
124
- --track-width: 2.75rem;
125
- --track-height: 1.75rem;
126
- --thumb-size: 1.25rem;
120
+ --track-width: 2.25rem;
121
+ --track-height: 1.375rem;
122
+ --thumb-size: 0.875rem;
127
123
  }
128
124
 
129
125
  [data-size='large'] {
130
- --track-width: 3.75rem;
131
- --track-height: 2.25rem;
132
- --thumb-size: 1.625rem;
126
+ --track-width: 3.25rem;
127
+ --track-height: 1.875rem;
128
+ --thumb-size: 1.375rem;
133
129
  }
134
130
 
135
131
  @media (prefers-reduced-motion: reduce) {
@@ -150,6 +146,7 @@
150
146
  [data-switch] > span > span {
151
147
  background: ButtonText;
152
148
  color: Canvas;
149
+ box-shadow: none;
153
150
  }
154
151
  input:checked + span {
155
152
  background: Highlight;
@@ -4,5 +4,5 @@
4
4
  "exports": {
5
5
  "TextArea": "text-area.svelte"
6
6
  },
7
- "dependencies": ["icon"]
7
+ "dependencies": ["icon", "theme"]
8
8
  }
@@ -1,6 +1,6 @@
1
1
  <script lang="ts">
2
- /* eslint-disable max-lines -- Keep the native control and its scoped style recipe together. */
3
2
  import Icon from '../icon/icon.svelte';
3
+ import { surface } from '../theme/surface.js';
4
4
  import type { TextAreaProps } from './types.js';
5
5
 
6
6
  let {
@@ -9,7 +9,8 @@
9
9
  icon,
10
10
  size = 'medium',
11
11
  wide = false,
12
- variant = 'neutral',
12
+ variant = 'soft',
13
+ glass = false,
13
14
  resizable = true,
14
15
  oninput,
15
16
  defaultValue,
@@ -23,7 +24,17 @@
23
24
  };
24
25
  </script>
25
26
 
26
- <div data-size={size} data-variant={variant} data-wide={wide || undefined}>
27
+ <div
28
+ class={surface({
29
+ color: 'background',
30
+ variant,
31
+ glass,
32
+ hover: variant === 'soft' && !attributes.disabled ? 'small' : undefined
33
+ })}
34
+ data-size={size}
35
+ data-variant={variant}
36
+ data-wide={wide || undefined}
37
+ >
27
38
  {#if icon}
28
39
  <span><Icon {...icon} label={undefined} /></span>
29
40
  {/if}
@@ -38,76 +49,39 @@
38
49
  <style>
39
50
  div {
40
51
  --internal-height: 2.5rem;
41
- --internal-padding: 0.85rem;
52
+ --internal-padding: 1rem;
42
53
  --internal-icon-size: 1.25rem;
43
- --internal-tint: var(--color-foreground, black);
44
- --internal-strength-light: 3%;
45
- --internal-strength-dark: 3%;
46
- --internal-opacity: 15%;
47
- --internal-border: transparent;
48
- --internal-highlight: color-mix(in srgb, var(--color-foreground, black) 12%, transparent);
54
+ --internal-placeholder: color-mix(in srgb, var(--color-muted,) 35%, var(--color-foreground));
49
55
  box-sizing: border-box;
50
56
  display: inline-flex;
51
57
  align-items: flex-start;
52
58
  align-self: start;
53
- gap: 0.6rem;
59
+ gap: 0.625rem;
54
60
  width: min(100%, 18rem);
55
61
  min-width: 0;
56
62
  max-width: 100%;
57
63
  min-height: var(--internal-height);
58
64
  padding-inline: var(--internal-padding);
59
- padding-block: 0.65rem;
60
- border: 1px solid var(--internal-border);
61
- border-radius: 0.5rem;
62
- background: color-mix(
63
- in srgb,
64
- light-dark(
65
- color-mix(
66
- in srgb,
67
- var(--internal-tint) var(--internal-strength-light),
68
- var(--color-background, white)
69
- ),
70
- color-mix(
71
- in srgb,
72
- var(--internal-tint) var(--internal-strength-dark),
73
- var(--color-background, white)
74
- )
75
- )
76
- var(--internal-opacity),
77
- transparent
78
- );
79
- color: var(--color-foreground, black);
80
- transition: all 150ms ease;
81
- -webkit-backdrop-filter: blur(0.5rem) saturate(120%);
82
- backdrop-filter: blur(0.5rem) saturate(120%);
65
+ padding-block: 0.75rem;
66
+ border-radius: 0.625rem;
83
67
  }
84
68
  div[data-wide] {
85
69
  width: 100%;
86
70
  }
87
71
  div[data-size='small'] {
88
72
  --internal-height: 2rem;
89
- --internal-padding: 0.65rem;
73
+ --internal-padding: 0.75rem;
90
74
  --internal-icon-size: 1rem;
91
75
  }
92
76
  div[data-size='large'] {
93
77
  --internal-height: 3rem;
94
- --internal-padding: 1rem;
78
+ --internal-padding: 1.125rem;
95
79
  --internal-icon-size: 1.5rem;
96
80
  }
97
- div[data-variant='soft'] {
98
- --internal-strength-light: 4%;
99
- --internal-strength-dark: 16%;
100
- --internal-opacity: 85%;
101
- --internal-border: light-dark(
102
- color-mix(in srgb, var(--color-foreground, black) 12%, transparent),
103
- color-mix(in srgb, var(--color-foreground, black) 30%, transparent)
104
- );
105
- box-shadow: inset 0 1px 0 var(--internal-highlight);
106
- }
107
81
  span {
108
82
  display: inline-flex;
109
83
  flex: 0 0 auto;
110
- color: var(--color-muted, #666);
84
+ color: var(--color-muted);
111
85
  font-size: var(--internal-icon-size);
112
86
  padding-top: 0.1rem;
113
87
  pointer-events: none;
@@ -124,7 +98,7 @@
124
98
  font: inherit;
125
99
  font-size: 1rem;
126
100
  font-weight: 200;
127
- line-height: 1.4;
101
+ line-height: 1.5;
128
102
  }
129
103
  textarea {
130
104
  resize: vertical;
@@ -133,41 +107,27 @@
133
107
  resize: none;
134
108
  }
135
109
  textarea::placeholder {
136
- color: var(--color-muted, #666);
137
- }
138
- div[data-variant='soft']:hover:not(:has(:disabled)) {
139
- --internal-border: light-dark(
140
- color-mix(in srgb, var(--color-foreground, black) 18%, transparent),
141
- color-mix(in srgb, var(--color-foreground, black) 38%, transparent)
142
- );
143
- --internal-strength-light: 6%;
144
- --internal-strength-dark: 20%;
110
+ color: var(--internal-placeholder);
111
+ opacity: 1;
145
112
  }
146
113
  div:has(textarea:focus) {
147
- --internal-tint: var(--color-primary, blue);
148
- --internal-strength-light: 6%;
149
- --internal-strength-dark: 12%;
150
- --internal-border: color-mix(in srgb, var(--internal-tint) 55%, transparent);
151
- box-shadow:
152
- inset 0 1px 0 var(--internal-highlight),
153
- 0 0 0 3px color-mix(in srgb, var(--internal-tint) 16%, transparent);
114
+ outline: 2px solid var(--color-primary);
115
+ outline-offset: 2px;
154
116
  }
155
- div:has(textarea:invalid, [aria-invalid='true']) {
156
- --internal-tint: var(--color-error, #bc2436);
157
- --internal-border: color-mix(in srgb, var(--internal-tint) 65%, transparent);
117
+ div:has(textarea:user-invalid, textarea[aria-invalid='true']) {
118
+ --surface-border: var(--color-error);
119
+ outline-color: var(--color-error);
158
120
  }
159
121
  div:has(:disabled) {
160
122
  opacity: 0.5;
161
123
  cursor: not-allowed;
162
124
  }
125
+ div[data-variant='soft']:has(textarea:disabled):hover {
126
+ --surface-strength: var(--surface-soft-strength);
127
+ }
163
128
  textarea:disabled {
164
129
  cursor: inherit;
165
130
  }
166
- @media (prefers-reduced-motion: reduce) {
167
- div {
168
- transition: none;
169
- }
170
- }
171
131
  @media (forced-colors: active) {
172
132
  div {
173
133
  border-color: ButtonText;
@@ -8,5 +8,6 @@ export type TextAreaProps = Omit<HTMLTextareaAttributes, 'value'> & {
8
8
  size?: BaseSize;
9
9
  wide?: boolean;
10
10
  variant?: Exclude<Variant, 'filled'>;
11
+ glass?: boolean;
11
12
  resizable?: boolean;
12
13
  };
@@ -4,5 +4,5 @@
4
4
  "exports": {
5
5
  "TextInput": "text-input.svelte"
6
6
  },
7
- "dependencies": ["icon"]
7
+ "dependencies": ["icon", "theme"]
8
8
  }