@fusion.dev/helix 0.144.0 → 0.146.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 (213) hide show
  1. package/custom-elements.json +17914 -4368
  2. package/dist/accordion/item/accordion-item.css.js +25 -10
  3. package/dist/accordion/item/accordion-item.d.ts +10 -0
  4. package/dist/aura/palette.d.ts +38 -0
  5. package/dist/aura/palette.js +22 -0
  6. package/dist/aura/root/aura-root.css.js +27 -3
  7. package/dist/aura/root/aura-root.d.ts +61 -14
  8. package/dist/aura/root/aura-root.js +40 -23
  9. package/dist/badge/root/badge-root.css.js +86 -16
  10. package/dist/badge/root/badge-root.d.ts +18 -0
  11. package/dist/badge/root/badge-root.js +6 -0
  12. package/dist/blockquote/root/blockquote-root.css.js +2 -2
  13. package/dist/blockquote/root/blockquote-root.d.ts +2 -0
  14. package/dist/breadcrumbs/breadcrumb/breadcrumb.css.js +7 -7
  15. package/dist/breadcrumbs/breadcrumb/breadcrumb.d.ts +5 -0
  16. package/dist/breadcrumbs/root/breadcrumbs-root.css.js +24 -1
  17. package/dist/breadcrumbs/root/breadcrumbs-root.d.ts +4 -0
  18. package/dist/breadcrumbs/root/breadcrumbs-root.js +2 -0
  19. package/dist/button/root/button-root.css.js +4 -3
  20. package/dist/button/root/button-root.d.ts +2 -0
  21. package/dist/button-group/root/button-group-root.d.ts +3 -0
  22. package/dist/callout/root/callout-root.css.js +4 -3
  23. package/dist/callout/root/callout-root.d.ts +6 -2
  24. package/dist/canvas/root/canvas-root.css.js +1 -0
  25. package/dist/card/root/card-root.d.ts +6 -0
  26. package/dist/carousel/root/carousel-root.css.js +17 -1
  27. package/dist/chat/message/chat-message.css.js +0 -62
  28. package/dist/chat/message/chat-message.js +2 -1
  29. package/dist/chat/root/chat-root.css.js +7 -0
  30. package/dist/chat/root/chat-root.d.ts +2 -0
  31. package/dist/chat/root/chat-root.js +2 -1
  32. package/dist/chat/typing.css.d.ts +1 -0
  33. package/dist/chat/typing.css.js +62 -0
  34. package/dist/checkbox/root/checkbox-root.css.js +15 -0
  35. package/dist/checkbox/root/checkbox-root.d.ts +2 -1
  36. package/dist/code/root/code-root.css.js +2 -2
  37. package/dist/code/root/code-root.d.ts +2 -0
  38. package/dist/code-block/root/code-block-root.css.js +24 -0
  39. package/dist/code-block/root/code-block-root.d.ts +2 -1
  40. package/dist/color-input/root/color-input-root.css.js +10 -0
  41. package/dist/color-picker/root/color-picker-root.css.js +8 -0
  42. package/dist/color-picker/root/color-picker-root.d.ts +1 -0
  43. package/dist/copy-button/root/copy-button-root.css.js +15 -6
  44. package/dist/copy-button/root/copy-button-root.d.ts +7 -0
  45. package/dist/data-table/root/data-table-root.css.js +70 -0
  46. package/dist/data-table/root/data-table-root.d.ts +6 -0
  47. package/dist/date-input/root/date-input-root.css.js +10 -0
  48. package/dist/description-list/item/description-list-item.css.js +11 -0
  49. package/dist/donut-chart/root/donut-chart-root.css.js +19 -0
  50. package/dist/donut-chart/root/donut-chart-root.d.ts +3 -0
  51. package/dist/drawer/root/drawer-root.css.js +47 -0
  52. package/dist/drawer/root/drawer-root.d.ts +5 -0
  53. package/dist/drilldown/item/drilldown-item.css.js +8 -7
  54. package/dist/drilldown/item/drilldown-item.d.ts +6 -0
  55. package/dist/drilldown/root/drilldown-root.css.js +27 -13
  56. package/dist/drilldown/root/drilldown-root.d.ts +10 -0
  57. package/dist/element-events.generated.js +72 -13
  58. package/dist/file-input/root/file-input-root.css.js +12 -11
  59. package/dist/file-input/root/file-input-root.d.ts +11 -0
  60. package/dist/filter-input/root/filter-input-root.css.js +6 -5
  61. package/dist/filter-input/root/filter-input-root.d.ts +5 -0
  62. package/dist/flex/root/flex-root.css.js +143 -9
  63. package/dist/flex/root/flex-root.d.ts +62 -5
  64. package/dist/flex/root/flex-root.js +17 -1
  65. package/dist/graph/root/graph-root.css.js +11 -8
  66. package/dist/graph/root/graph-root.d.ts +8 -0
  67. package/dist/grid/root/grid-root.css.js +114 -11
  68. package/dist/grid/root/grid-root.d.ts +40 -0
  69. package/dist/grid/root/grid-root.js +13 -1
  70. package/dist/heatmap/root/heatmap-root.css.js +2 -1
  71. package/dist/icon-button/root/icon-button-root.css.js +68 -13
  72. package/dist/icon-button/root/icon-button-root.d.ts +4 -0
  73. package/dist/kbd/root/kbd-root.css.js +27 -0
  74. package/dist/kbd/root/kbd-root.d.ts +2 -0
  75. package/dist/lightbox/item/lightbox-item.css.js +4 -0
  76. package/dist/lightbox/root/lightbox-root.css.js +41 -0
  77. package/dist/line-chart/root/line-chart-root.css.js +16 -0
  78. package/dist/line-chart/root/line-chart-root.d.ts +2 -0
  79. package/dist/list/item/list-item.css.js +21 -1
  80. package/dist/list/item/list-item.d.ts +3 -0
  81. package/dist/list/root/list-root.css.js +13 -1
  82. package/dist/listbox/root/listbox-root.css.js +9 -0
  83. package/dist/listbox/root/listbox-root.d.ts +1 -0
  84. package/dist/loader/root/loader-root.css.js +3 -3
  85. package/dist/loader/root/loader-root.d.ts +2 -0
  86. package/dist/megamenu/root/megamenu-root.css.js +16 -13
  87. package/dist/megamenu/root/megamenu-root.d.ts +11 -0
  88. package/dist/menu/index.d.ts +1 -0
  89. package/dist/menu/index.js +1 -0
  90. package/dist/menu/item/menu-item.css.js +39 -4
  91. package/dist/menu/item/menu-item.d.ts +4 -0
  92. package/dist/menu/item/menu-item.js +2 -0
  93. package/dist/menu/section/menu-section.css.d.ts +1 -0
  94. package/dist/menu/section/menu-section.css.js +14 -0
  95. package/dist/menu/section/menu-section.d.ts +24 -0
  96. package/dist/menu/section/menu-section.js +16 -0
  97. package/dist/meter/root/meter-root.css.js +6 -3
  98. package/dist/meter/root/meter-root.d.ts +1 -0
  99. package/dist/navigation/root/navigation-root.css.js +3 -2
  100. package/dist/navigation/root/navigation-root.d.ts +13 -0
  101. package/dist/notifications/root/notifications-root.css.js +30 -1
  102. package/dist/notifications/root/notifications-root.d.ts +1 -0
  103. package/dist/number-input/root/number-input-root.css.js +12 -9
  104. package/dist/number-input/root/number-input-root.d.ts +8 -0
  105. package/dist/object-inspector/root/object-inspector-root.css.js +38 -7
  106. package/dist/object-inspector/root/object-inspector-root.d.ts +4 -1
  107. package/dist/object-inspector/root/object-inspector-root.js +2 -1
  108. package/dist/option/root/option-root.css.js +1 -1
  109. package/dist/orb/index.d.ts +1 -1
  110. package/dist/orb/index.js +1 -1
  111. package/dist/orb/root/orb-root.d.ts +40 -6
  112. package/dist/orb/root/orb-root.js +29 -11
  113. package/dist/outline/root/outline-root.css.js +10 -0
  114. package/dist/outline/root/outline-root.d.ts +7 -0
  115. package/dist/pin-input/root/pin-input-root.css.js +4 -4
  116. package/dist/pin-input/root/pin-input-root.d.ts +4 -0
  117. package/dist/progress/root/progress-root.css.js +4 -2
  118. package/dist/progress/root/progress-root.d.ts +1 -0
  119. package/dist/qr-code/root/qr-code-root.css.js +11 -0
  120. package/dist/quote/root/quote-root.css.js +3 -0
  121. package/dist/quote/root/quote-root.d.ts +2 -0
  122. package/dist/radio/root/radio-root.css.js +16 -0
  123. package/dist/radio/root/radio-root.d.ts +2 -1
  124. package/dist/sandbox/frame.d.ts +64 -0
  125. package/dist/sandbox/frame.js +146 -0
  126. package/dist/sandbox/index.d.ts +2 -0
  127. package/dist/sandbox/index.js +1 -0
  128. package/dist/sandbox/levels.d.ts +10 -0
  129. package/dist/sandbox/levels.js +5 -0
  130. package/dist/sandbox/root/sandbox-root.d.ts +3 -0
  131. package/dist/sandbox/root/sandbox-root.js +13 -3
  132. package/dist/sandbox/source/sandbox-source.css.js +70 -0
  133. package/dist/sandbox/source/sandbox-source.d.ts +35 -0
  134. package/dist/sandbox/source/sandbox-source.js +22 -3
  135. package/dist/sandbox/terminal/sandbox-terminal.css.js +39 -0
  136. package/dist/sandbox/terminal/sandbox-terminal.d.ts +50 -1
  137. package/dist/sandbox/terminal/sandbox-terminal.js +61 -4
  138. package/dist/sandbox/tokens.d.ts +39 -0
  139. package/dist/sandbox/tokens.js +9 -0
  140. package/dist/scatter-chart/root/scatter-chart-root.css.js +9 -0
  141. package/dist/scatter-chart/root/scatter-chart-root.d.ts +2 -0
  142. package/dist/scroll-area/root/scroll-area-root.css.js +83 -1
  143. package/dist/scroll-area/root/scroll-area-root.d.ts +6 -0
  144. package/dist/skeleton/root/skeleton-root.css.js +11 -11
  145. package/dist/skeleton/root/skeleton-root.d.ts +1 -0
  146. package/dist/skip-link/root/skip-link-root.css.js +20 -8
  147. package/dist/skip-link/root/skip-link-root.d.ts +12 -3
  148. package/dist/slider/root/slider-root.css.js +26 -0
  149. package/dist/sparkline/root/sparkline-root.css.js +49 -0
  150. package/dist/sparkline/root/sparkline-root.d.ts +6 -0
  151. package/dist/sparkline/root/sparkline-root.js +2 -1
  152. package/dist/speed-dial/item/speed-dial-item.css.js +18 -10
  153. package/dist/speed-dial/item/speed-dial-item.d.ts +7 -0
  154. package/dist/speed-dial/root/speed-dial-root.css.js +17 -8
  155. package/dist/speed-dial/root/speed-dial-root.d.ts +16 -1
  156. package/dist/speed-dial/root/speed-dial-root.js +4 -1
  157. package/dist/stat/root/stat-root.css.js +4 -0
  158. package/dist/steps/root/steps-root.css.js +28 -0
  159. package/dist/steps/root/steps-root.d.ts +5 -0
  160. package/dist/steps/step/step.js +2 -1
  161. package/dist/table/root/table-root.css.js +18 -0
  162. package/dist/table/root/table-root.d.ts +6 -0
  163. package/dist/table/root/table-root.js +2 -0
  164. package/dist/table/row/table-row.css.js +18 -0
  165. package/dist/table/row/table-row.d.ts +2 -0
  166. package/dist/table/row/table-row.js +2 -0
  167. package/dist/tabs/panel/tab-panel.css.js +5 -1
  168. package/dist/tabs/root/tabs-root.css.js +58 -3
  169. package/dist/tabs/root/tabs-root.d.ts +4 -0
  170. package/dist/tabs/tab/tab.css.js +21 -10
  171. package/dist/tabs/tab/tab.d.ts +7 -0
  172. package/dist/tag/group/tag-group.css.js +6 -0
  173. package/dist/text/clamp.d.ts +11 -0
  174. package/dist/text/clamp.js +3 -0
  175. package/dist/text/root/text-root.css.js +79 -45
  176. package/dist/text/root/text-root.d.ts +49 -7
  177. package/dist/text/root/text-root.js +17 -11
  178. package/dist/textarea/root/textarea-root.css.js +27 -0
  179. package/dist/time-input/root/time-input-root.css.js +12 -0
  180. package/dist/time-picker/root/time-picker-root.css.js +1 -1
  181. package/dist/timeline/group/timeline-group.css.d.ts +1 -0
  182. package/dist/timeline/group/timeline-group.css.js +32 -0
  183. package/dist/timeline/group/timeline-group.d.ts +25 -0
  184. package/dist/timeline/group/timeline-group.js +16 -0
  185. package/dist/timeline/index.d.ts +1 -0
  186. package/dist/timeline/index.js +1 -0
  187. package/dist/timeline/item/timeline-item.css.js +43 -16
  188. package/dist/timeline/root/timeline-root.css.js +56 -0
  189. package/dist/timeline/root/timeline-root.d.ts +26 -0
  190. package/dist/timeline/root/timeline-root.js +7 -0
  191. package/dist/toast/item/toast-item.css.js +10 -0
  192. package/dist/toast/root/toast-root.d.ts +1 -0
  193. package/dist/toast/root/toast-root.js +1 -0
  194. package/dist/toggle/group/toggle-group.css.js +10 -7
  195. package/dist/toggle/group/toggle-group.d.ts +6 -1
  196. package/dist/toggle/root/toggle-root.d.ts +6 -0
  197. package/dist/toolbar/root/toolbar-root.css.js +4 -0
  198. package/dist/toolbar/root/toolbar-root.d.ts +5 -0
  199. package/dist/tooltip/root/tooltip-root.css.js +5 -5
  200. package/dist/tooltip/root/tooltip-root.d.ts +4 -0
  201. package/dist/tree/item/tree-item.css.js +89 -7
  202. package/dist/tree/item/tree-item.d.ts +19 -0
  203. package/dist/trend/root/trend-root.css.js +3 -5
  204. package/dist/trend/root/trend-root.d.ts +2 -2
  205. package/dist/video/root/video-root.css.js +6 -0
  206. package/dist/video/root/video-root.d.ts +3 -0
  207. package/dist/virtual-list/root/virtual-list-root.css.js +19 -0
  208. package/dist/virtual-list/root/virtual-list-root.d.ts +10 -3
  209. package/lint.json +1 -1
  210. package/package.json +9 -9
  211. package/types/jsx.d.ts +8253 -1233
  212. package/types/svelte.d.ts +2 -0
  213. package/types/vue.d.ts +2357 -312
@@ -8,11 +8,11 @@ all: unset;
8
8
 
9
9
  box-sizing: border-box;
10
10
  align-items: center;
11
- color: var(--helix-color-foreground);
11
+ color: var(--accordion-item-color, var(--helix-color-foreground));
12
12
  cursor: pointer;
13
13
  display: flex;
14
- font-size: var(--helix-text-md);
15
- font-weight: var(--helix-weight-medium);
14
+ font-size: var(--accordion-item-font-size, var(--helix-text-md));
15
+ font-weight: var(--accordion-item-font-weight, var(--helix-weight-medium));
16
16
  inline-size: 100%;
17
17
 
18
18
  gap: var(--helix-space-xs);
@@ -24,28 +24,43 @@ transition: background-color var(--helix-duration-fast) var(--helix-easing-stand
24
24
 
25
25
  [part="chevron"] {
26
26
  block-size: 1em;
27
- color: var(--helix-color-foreground-subtle);
27
+ color: var(--accordion-item-chevron-color, var(--helix-color-foreground-subtle));
28
28
  flex: 0 0 auto;
29
29
  inline-size: 1em;
30
30
  transform: scaleY(1);
31
31
  transition: transform var(--helix-duration-fast) var(--helix-easing-standard);
32
32
  }
33
33
 
34
- :host(:not([chevron-placement="start"])) [part="chevron"] {
34
+ ::slotted([slot="chevron"]) {
35
+ block-size: 1em;
36
+ color: var(--accordion-item-chevron-color, var(--helix-color-foreground-subtle));
37
+ flex: 0 0 auto;
38
+ inline-size: 1em;
39
+ transition: transform var(--helix-duration-fast) var(--helix-easing-standard);
40
+ }
41
+
42
+ :host(:not([chevron-placement="start"])) [part="chevron"],
43
+ :host(:not([chevron-placement="start"])) ::slotted([slot="chevron"]) {
35
44
  margin-inline-start: auto;
36
45
  }
37
46
 
38
- :host(:state(open)) [part="chevron"] {
47
+ :host(:state(open)) [part="chevron"],
48
+ :host(:state(open)) ::slotted([slot="chevron"]) {
39
49
  transform: scaleY(-1);
40
50
  }
41
51
 
42
- [part="trigger"]:hover:not(:disabled) [part="chevron"] {
43
- color: var(--helix-color-foreground);
52
+ [part="trigger"]:hover:not(:disabled) {
53
+ background-color: var(--accordion-item-hover-background-color, transparent);
54
+ }
55
+
56
+ [part="trigger"]:hover:not(:disabled) [part="chevron"],
57
+ [part="trigger"]:hover:not(:disabled) ::slotted([slot="chevron"]) {
58
+ color: var(--accordion-item-chevron-hover-color, var(--helix-color-foreground));
44
59
  }
45
60
 
46
61
  [part="trigger"]:disabled {
47
62
  cursor: not-allowed;
48
- opacity: 0.5;
63
+ opacity: var(--accordion-item-disabled-opacity, 0.5);
49
64
  }
50
65
 
51
66
  [part="trigger"]:focus-visible {
@@ -70,7 +85,7 @@ overflow: hidden;
70
85
  }
71
86
 
72
87
  [part="content-inner"] > slot {
73
- color: var(--helix-color-foreground-muted);
88
+ color: var(--accordion-item-content-color, var(--helix-color-foreground-muted));
74
89
  display: block;
75
90
  line-height: var(--helix-leading-normal);
76
91
  padding: var(--accordion-item-padding, var(--helix-space-sm) var(--helix-space-md));
@@ -16,6 +16,8 @@ import type { CSSResultGroup } from 'lit';
16
16
  * @documentation https://fusion.dev/helix/components/accordion
17
17
  *
18
18
  * @slot - The content shown when the section is open.
19
+ * @slot chevron - Replaces the open and close indicator. A slotted icon is sized, colored and
20
+ * turned like the drawn one.
19
21
  * @slot summary - The text of the button that opens and closes the section. Always shown.
20
22
  *
21
23
  * @csspart chevron - The open and close indicator.
@@ -25,6 +27,14 @@ import type { CSSResultGroup } from 'lit';
25
27
  * @csspart heading - The heading that holds the trigger.
26
28
  * @csspart trigger - The button that opens and closes the section, holding the summary.
27
29
  *
30
+ * @cssproperty --accordion-item-chevron-color - The color of the open and close indicator.
31
+ * @cssproperty --accordion-item-chevron-hover-color - The indicator's color while the pointer is on the trigger.
32
+ * @cssproperty --accordion-item-color - The color of the trigger's text.
33
+ * @cssproperty --accordion-item-content-color - The color of the content's text.
34
+ * @cssproperty --accordion-item-disabled-opacity - How faint a disabled trigger is drawn.
35
+ * @cssproperty --accordion-item-font-size - The size of the trigger's text.
36
+ * @cssproperty --accordion-item-font-weight - The weight of the trigger's text.
37
+ * @cssproperty --accordion-item-hover-background-color - The trigger's fill while the pointer is on it. Unset, there is none.
28
38
  * @cssproperty --accordion-item-padding - The padding of the trigger and the content.
29
39
  */
30
40
  export declare class HelixAccordionItem extends HelixHeadlessAccordionItem {
@@ -0,0 +1,38 @@
1
+ import type { ColorBytes, TokenColor } from '@fusion.dev/helix-core';
2
+ /** The four ramp stops a shape is drawn through, as channels in `0..1`. */
3
+ export interface AuraPalette {
4
+ glow: TokenColor;
5
+ ground: TokenColor;
6
+ haze: TokenColor;
7
+ peak: TokenColor;
8
+ }
9
+ /** The computed colors the canvas carries, one per stop and two for the peak. */
10
+ export type PaletteStyle = Pick<CSSStyleDeclaration, 'caretColor' | 'color' | 'columnRuleColor' | 'outlineColor' | 'textDecorationColor'>;
11
+ /** The default theme's dark page, ink on black, which is drawn until a read. */
12
+ export declare const RESTING_PALETTE: AuraPalette;
13
+ /**
14
+ * The ramp, from the colors the canvas's stylesheet resolved for it.
15
+ *
16
+ * **Carried on color properties of the canvas, which a canvas never paints**,
17
+ * because a custom property is handed back as the text that was written and a
18
+ * color property is resolved by the engine first. Read there, each stop takes
19
+ * the `--aura-*` hook a page wrote anywhere above the aura, and the token
20
+ * otherwise, the same way the fallback gradient on the host does:
21
+ *
22
+ * - `color`: `--aura-glow`, or `primary`.
23
+ * - `text-decoration-color`: `--aura-haze`, or `primary-subtle`.
24
+ * - `outline-color`: `--aura-ground`, or the background.
25
+ * - `column-rule-color` and `caret-color`: `--aura-peak`, or the light and the
26
+ * dark page's own peak.
27
+ *
28
+ * **Light pages peak on the pressed `primary-active` and dark ones on the
29
+ * foreground**, and the ground is what says which: mixing a saturated hue
30
+ * toward near-black takes the chroma out of exactly the pixels carrying the
31
+ * most of it. Read from the ground's own luminance rather than asked of the
32
+ * color mode, which is a private token this tier may not reach for. A page
33
+ * that sets `--aura-peak` sets both, so its peak is used either way.
34
+ *
35
+ * @param style The canvas's computed style.
36
+ * @param read Turns a resolved color into bytes, `undefined` where it cannot.
37
+ */
38
+ export declare const paletteOf: (style: PaletteStyle, read: (value: string) => ColorBytes | undefined) => AuraPalette;
@@ -0,0 +1,22 @@
1
+ export const RESTING_PALETTE = {
2
+ glow: [0.88, 0.88, 0.88],
3
+ ground: [0, 0, 0],
4
+ haze: [0.16, 0.16, 0.16],
5
+ peak: [1, 1, 1],
6
+ };
7
+ export const paletteOf = (style, read) => {
8
+ const channels = (value, fallback) => {
9
+ const bytes = read(value);
10
+ return bytes ? [bytes[0] / 255, bytes[1] / 255, bytes[2] / 255] : [...fallback];
11
+ };
12
+ const ground = channels(style.outlineColor, RESTING_PALETTE.ground);
13
+ const light = ground[0] * 0.2126 + ground[1] * 0.7152 + ground[2] * 0.0722 > 0.5;
14
+ return {
15
+ glow: channels(style.color, RESTING_PALETTE.glow),
16
+ ground,
17
+ haze: channels(style.textDecorationColor, RESTING_PALETTE.haze),
18
+ peak: light
19
+ ? channels(style.columnRuleColor, [0.02, 0.02, 0.02])
20
+ : channels(style.caretColor, RESTING_PALETTE.peak),
21
+ };
22
+ };
@@ -1,5 +1,10 @@
1
1
  import { createSheet } from '@fusion.dev/helix-core';
2
2
  export const styles = createSheet(`:host {
3
+
4
+ --_aura-glow: var(--aura-glow, var(--helix-color-primary));
5
+ --_aura-ground: var(--aura-ground, var(--helix-color-background));
6
+ --_aura-haze: var(--aura-haze, var(--helix-color-primary-subtle));
7
+
3
8
  position: relative;
4
9
  display: block;
5
10
  inline-size: 100%;
@@ -7,9 +12,9 @@ block-size: 100%;
7
12
 
8
13
  background-image: radial-gradient(
9
14
  120% 90% at 62% 12%,
10
- color-mix(in oklab, var(--helix-color-primary) 38%, var(--helix-color-background)) 0%,
11
- var(--helix-color-primary-subtle) 38%,
12
- var(--helix-color-background) 78%
15
+ color-mix(in oklab, var(--_aura-glow) 38%, var(--_aura-ground)) 0%,
16
+ var(--_aura-haze) 38%,
17
+ var(--_aura-ground) 78%
13
18
  );
14
19
  }
15
20
 
@@ -25,6 +30,25 @@ pointer-events: auto;
25
30
  display: block;
26
31
  inline-size: 100%;
27
32
  block-size: 100%;
33
+
34
+ caret-color: var(--aura-peak, var(--helix-color-foreground));
35
+ color: var(--_aura-glow);
36
+ column-rule-color: var(--aura-peak, var(--helix-color-primary-active));
37
+ outline-color: var(--_aura-ground);
38
+ text-decoration-color: var(--_aura-haze);
39
+
40
+ transition-duration: var(--helix-duration-fast);
41
+ transition-property: caret-color, color, column-rule-color, outline-color, text-decoration-color;
42
+ }
43
+
44
+ [part="content"] {
45
+ inset: 0;
46
+ pointer-events: none;
47
+ position: absolute;
48
+ }
49
+
50
+ ::slotted(*) {
51
+ pointer-events: auto;
28
52
  }
29
53
 
30
54
  [part="canvas"][hidden] {
@@ -33,13 +33,39 @@ import type { CSSResultGroup, PropertyValues, TemplateResult } from 'lit';
33
33
  * allow. Its behavior is drawing to a canvas, so there is no structure for you
34
34
  * to restyle and nothing for a headless version to hold.
35
35
  *
36
+ * **Content placed inside sits over the field**, in a `content` part that
37
+ * covers the aura, so an interactive aura answers the pointer over the
38
+ * content too. Clicks still reach the content. Style `::part(content)` to
39
+ * place it, such as centering a hero's heading.
40
+ *
41
+ * **The colors are the theme's, and each has a hook**: `--aura-ground`,
42
+ * `--aura-haze`, `--aura-glow` and `--aura-peak`, read by the shader and by the
43
+ * fallback gradient alike, from wherever on the page you set them. A hook
44
+ * changed while the aura draws, by a class, a style or the color mode,
45
+ * reaches the shader on the next frame.
46
+ *
47
+ * Where the gradient shows in place of the shader, the aura matches
48
+ * `:state(fallback)` and sends `helix-aura-fallback` with the reason.
49
+ *
36
50
  * @since 0.1.0
37
51
  * @status stable
38
52
  * @category media
39
53
  * @aliases Backdrop, Background, Atmosphere, Gradient
40
54
  * @documentation https://fusion.dev/helix/components/aura
41
55
  *
56
+ * @slot - Content drawn over the field, such as a heading.
57
+ *
58
+ * @event {HelixAuraFallbackEvent} helix-aura-fallback - Fired each time the aura stops drawing its shader and shows its gradient, with the reason.
59
+ *
42
60
  * @csspart canvas - The surface the shader draws into.
61
+ * @csspart content - The box over the field that holds the content. It covers the aura.
62
+ *
63
+ * @cssproperty --aura-glow - The bright stop of the ramp. Defaults to `--helix-color-primary`.
64
+ * @cssproperty --aura-ground - The page the field fades into. Defaults to `--helix-color-background`.
65
+ * @cssproperty --aura-haze - The faint stop between the ground and the glow. Defaults to `--helix-color-primary-subtle`.
66
+ * @cssproperty --aura-peak - The hottest stop, at the center of a shape. Defaults to `--helix-color-primary-active` on a light page and `--helix-color-foreground` on a dark one.
67
+ *
68
+ * @cssstate fallback - The aura shows its gradient: the renderer draws on the CPU, there is no WebGL, or the browser took the context back. Cleared once the shader draws again.
43
69
  */
44
70
  export declare class HelixAura extends HelixElement {
45
71
  static styles: CSSResultGroup;
@@ -173,19 +199,29 @@ export declare class HelixAura extends HelixElement {
173
199
  * states.
174
200
  */
175
201
  variant?: 'aurora' | 'braid' | 'caustics' | 'flow' | 'fracture' | 'lattice' | 'particles' | 'rays' | 'streaks' | 'threads' | 'waves';
176
- /** Repaints when the palette moves, which no re-render would do on its own. */
177
- private readonly tokens;
178
202
  /**
179
203
  * The four ramp stops, resolved.
180
204
  *
181
- * **Cached, because reading them is not free and they do not move.** Each
182
- * read sets a style, calls `getComputedStyle`, fills a pixel and reads it
183
- * back: a style recalc and a canvas readback apiece. Doing that inside
184
- * `paint` meant four of each on every frame, forever, to fetch values that
185
- * change only when the color mode does. The controller already says when
186
- * that happens, so this is filled from there and from the first paint.
205
+ * **Read on every frame drawn, and converted only when they moved.** A hook
206
+ * can change anywhere above the aura at any time: a class on a section, a
207
+ * style written by script, a scoped theme, the color mode. Each frame reads
208
+ * the five colors the canvas carries off its live computed style, which is
209
+ * a string compare while nothing changed, and `paletteOf` converts them only
210
+ * when the text differs. Converting on every frame was the cost: a style
211
+ * recalc and a canvas readback per stop, forever, for values that rarely
212
+ * move.
213
+ *
214
+ * A stopped loop draws no frame to read in, so a change of any of the five
215
+ * also runs a short transition on the canvas, and its end paints one frame.
216
+ * That covers a reader who asked for no motion.
187
217
  */
188
218
  private palette;
219
+ /** The canvas's live computed style, and the five colors it last carried. */
220
+ private carried;
221
+ /** Why the gradient shows, while it does, or `undefined` while the shader draws. */
222
+ private fallback;
223
+ /** Why the last `build` drew nothing, for the fallback it leads to. */
224
+ private refused;
189
225
  private canvas;
190
226
  private gl;
191
227
  private program;
@@ -413,6 +449,15 @@ export declare class HelixAura extends HelixElement {
413
449
  * loss was canceled.
414
450
  */
415
451
  private readonly handleLost;
452
+ /**
453
+ * Shows the gradient, and says so once for each time it starts to.
454
+ *
455
+ * The state is what a stylesheet reads, and the event is what a script
456
+ * logs, such as to count how many readers see the gradient. Sent again only
457
+ * after the shader has drawn in between, so a context that is lost while
458
+ * the gradient already shows is not said twice.
459
+ */
460
+ private fallBack;
416
461
  /**
417
462
  * Draws again on the context the browser gave back.
418
463
  *
@@ -422,15 +467,17 @@ export declare class HelixAura extends HelixElement {
422
467
  */
423
468
  private readonly handleRestored;
424
469
  /**
425
- * Resolves the ramp. Called when the palette moves, not when a frame draws.
470
+ * Resolves the ramp, converting only when the colors the canvas carries
471
+ * moved since the last read.
426
472
  *
427
- * Light pages peak on the pressed `primary-active` and dark ones on the
428
- * foreground, and the ground is what says which: mixing a saturated hue
429
- * toward near-black takes the chroma out of exactly the pixels carrying the
430
- * most of it. Read from the ground's own luminance rather than asked of the
431
- * color mode, which is a private token this tier may not reach for.
473
+ * From the canvas's own computed style, which carries each stop on a color
474
+ * property: the `--aura-*` hook where a page set one, and the token
475
+ * otherwise. `paletteOf` says which property is which, and how the peak is
476
+ * chosen for a light page or a dark one.
432
477
  */
433
478
  private readPalette;
479
+ /** One frame for a stopped loop, once a color the canvas carries has moved. */
480
+ private readonly handleRestyle;
434
481
  private link;
435
482
  /**
436
483
  * Re-reads the box and resizes the drawing buffer to match.
@@ -5,10 +5,11 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
5
5
  return c > 3 && r && Object.defineProperty(target, key, r), r;
6
6
  };
7
7
  var HelixAura_1;
8
- import { customElement, HelixElement, MAX_FRAME_STEP, PaintGateController, TokenColorController, } from '@fusion.dev/helix-core';
8
+ import { customElement, HelixAuraFallbackEvent, HelixElement, MAX_FRAME_STEP, PaintGateController, readColor, } from '@fusion.dev/helix-core';
9
9
  import { html } from 'lit';
10
10
  import { property } from 'lit/decorators.js';
11
11
  import { tiltFor } from '../axis.js';
12
+ import { paletteOf, RESTING_PALETTE } from '../palette.js';
12
13
  import { FRAGMENT, INTERACTIONS, SHAPES, VERTEX } from '../shader.js';
13
14
  import { styles } from './aura-root.css.js';
14
15
  const MAX_CANVAS_EDGE = 4096;
@@ -27,16 +28,9 @@ let HelixAura = class HelixAura extends HelixElement {
27
28
  this.speed = 1;
28
29
  this.tilt = 14;
29
30
  this.variance = 1;
30
- this.tokens = new TokenColorController(this, () => {
31
- this.readPalette();
32
- this.paintOnce();
33
- });
34
- this.palette = {
35
- glow: [0.88, 0.88, 0.88],
36
- ground: [0, 0, 0],
37
- haze: [0.16, 0.16, 0.16],
38
- peak: [1, 1, 1],
39
- };
31
+ this.palette = { ...RESTING_PALETTE };
32
+ this.carried = null;
33
+ this.refused = 'unavailable';
40
34
  this.canvas = null;
41
35
  this.gl = null;
42
36
  this.program = null;
@@ -87,6 +81,7 @@ let HelixAura = class HelixAura extends HelixElement {
87
81
  }
88
82
  this.program = null;
89
83
  this.canvas?.toggleAttribute('hidden', true);
84
+ this.fallBack('lost');
90
85
  };
91
86
  this.handleRestored = () => {
92
87
  if (!this.prepare()) {
@@ -103,6 +98,9 @@ let HelixAura = class HelixAura extends HelixElement {
103
98
  this.open();
104
99
  }
105
100
  };
101
+ this.handleRestyle = () => {
102
+ this.paintOnce();
103
+ };
106
104
  this.held = 0;
107
105
  this.handlePointer = (event) => {
108
106
  if (this.gate.reduced) {
@@ -132,7 +130,8 @@ let HelixAura = class HelixAura extends HelixElement {
132
130
  stencil: false,
133
131
  }; }
134
132
  render() {
135
- return html `<canvas part="canvas" aria-hidden="true"></canvas>`;
133
+ return html `<canvas part="canvas" aria-hidden="true"></canvas>
134
+ <div part="content"><slot></slot></div>`;
136
135
  }
137
136
  firstUpdated(changed) {
138
137
  super.firstUpdated(changed);
@@ -150,15 +149,16 @@ let HelixAura = class HelixAura extends HelixElement {
150
149
  this.gl = null;
151
150
  }
152
151
  if (!this.program && !this.gl?.isContextLost() && !this.build()) {
152
+ this.fallBack(this.refused);
153
153
  return;
154
154
  }
155
155
  this.canvas?.addEventListener('webglcontextlost', this.handleLost);
156
156
  this.canvas?.addEventListener('webglcontextrestored', this.handleRestored);
157
+ this.canvas?.addEventListener('transitionend', this.handleRestyle);
157
158
  this.addEventListener('pointermove', this.handlePointer);
158
159
  this.addEventListener('pointerleave', this.handleLeave);
159
160
  this.gate.watch();
160
161
  window.addEventListener('scroll', this.handleScroll, { capture: true, passive: true });
161
- this.readPalette();
162
162
  if (this.measure() || (this.width && !this.opened)) {
163
163
  this.open();
164
164
  }
@@ -182,8 +182,10 @@ let HelixAura = class HelixAura extends HelixElement {
182
182
  return false;
183
183
  }
184
184
  if (HelixAura_1.drawsInSoftware()) {
185
+ this.refused = 'software';
185
186
  return false;
186
187
  }
188
+ this.refused = 'unavailable';
187
189
  let gl = canvas.getContext('webgl2', HelixAura_1.CONTEXT);
188
190
  if (gl?.isContextLost()) {
189
191
  const fresh = document.createElement('canvas');
@@ -212,6 +214,8 @@ let HelixAura = class HelixAura extends HelixElement {
212
214
  return false;
213
215
  }
214
216
  this.canvas?.toggleAttribute('hidden', false);
217
+ this.fallback = undefined;
218
+ this.toggleState('fallback', false);
215
219
  gl.useProgram(this.program);
216
220
  for (const name of [
217
221
  'uBloom',
@@ -258,22 +262,34 @@ let HelixAura = class HelixAura extends HelixElement {
258
262
  }
259
263
  this.canvas?.removeEventListener('webglcontextlost', this.handleLost);
260
264
  this.canvas?.removeEventListener('webglcontextrestored', this.handleRestored);
265
+ this.canvas?.removeEventListener('transitionend', this.handleRestyle);
261
266
  this.removeEventListener('pointermove', this.handlePointer);
262
267
  this.removeEventListener('pointerleave', this.handleLeave);
263
268
  window.removeEventListener('scroll', this.handleScroll, { capture: true });
264
269
  this.release = window.setTimeout(this.drop, 0);
265
270
  }
271
+ fallBack(reason) {
272
+ if (this.fallback !== undefined) {
273
+ return;
274
+ }
275
+ this.fallback = reason;
276
+ this.toggleState('fallback', true);
277
+ this.dispatchEvent(new HelixAuraFallbackEvent({ reason }));
278
+ }
266
279
  readPalette() {
267
- const ground = this.tokens.read('--helix-color-background', [0, 0, 0]);
268
- const light = ground[0] * 0.2126 + ground[1] * 0.7152 + ground[2] * 0.0722 > 0.5;
269
- this.palette = {
270
- glow: this.tokens.read('--helix-color-primary', [0.88, 0.88, 0.88]),
271
- ground,
272
- haze: this.tokens.read('--helix-color-primary-subtle', [0.16, 0.16, 0.16]),
273
- peak: light
274
- ? this.tokens.read('--helix-color-primary-active', [0.02, 0.02, 0.02])
275
- : this.tokens.read('--helix-color-foreground', [1, 1, 1]),
276
- };
280
+ const canvas = this.canvas;
281
+ if (!canvas) {
282
+ return;
283
+ }
284
+ if (this.carried?.canvas !== canvas) {
285
+ this.carried = { canvas, style: getComputedStyle(canvas), text: '' };
286
+ }
287
+ const { style } = this.carried;
288
+ const text = `${style.color} ${style.textDecorationColor} ${style.outlineColor} ${style.columnRuleColor} ${style.caretColor}`;
289
+ if (text !== this.carried.text) {
290
+ this.carried.text = text;
291
+ this.palette = paletteOf(style, readColor);
292
+ }
277
293
  }
278
294
  link(vertex, fragment) {
279
295
  const gl = this.gl;
@@ -350,6 +366,7 @@ let HelixAura = class HelixAura extends HelixElement {
350
366
  if (!gl || !this.program || !this.width) {
351
367
  return;
352
368
  }
369
+ this.readPalette();
353
370
  const { glow, ground, haze, peak } = this.palette;
354
371
  gl.uniform3fv(uniforms.uGround ?? null, ground);
355
372
  gl.uniform3fv(uniforms.uHaze ?? null, haze);
@@ -7,6 +7,8 @@ export const styles = createSheet(`:host {
7
7
  --_badge-dot: var(--helix-space-xs);
8
8
 
9
9
  --_badge-on: var(--helix-color-foreground-on-primary);
10
+
11
+ --_badge-fill: var(--helix-color-primary-surface);
10
12
  display: inline-flex;
11
13
  position: relative;
12
14
  vertical-align: middle;
@@ -15,7 +17,7 @@ vertical-align: middle;
15
17
  [part="indicator"] {
16
18
  align-items: center;
17
19
 
18
- background-color: var(--badge-background-color, var(--helix-color-primary-surface));
20
+ background-color: var(--badge-background-color, var(--_badge-fill));
19
21
  block-size: var(--_badge-size);
20
22
  border-radius: var(--helix-radii-full);
21
23
 
@@ -65,12 +67,12 @@ opacity var(--helix-duration-fast) var(--helix-easing-standard),
65
67
  scale var(--helix-duration-fast) var(--helix-easing-accelerate);
66
68
  }
67
69
 
68
- :host([tone="primary"]) [part="indicator"] {
69
- background-color: var(--badge-background-color, var(--helix-color-primary-surface));
70
+ :host([tone="primary"]) {
71
+ --_badge-fill: var(--helix-color-primary-surface);
70
72
  }
71
73
 
72
- :host([tone="accent"]) [part="indicator"] {
73
- background-color: var(--badge-background-color, var(--helix-color-accent-surface));
74
+ :host([tone="accent"]) {
75
+ --_badge-fill: var(--helix-color-accent-surface);
74
76
  }
75
77
 
76
78
  :host([tone="accent"]) {
@@ -84,25 +86,25 @@ background-color: var(--badge-background-color, var(--helix-color-accent-surface
84
86
  --_badge-on: var(--helix-color-foreground-on-status);
85
87
  }
86
88
 
87
- :host([tone="info"]) [part="indicator"] {
88
- background-color: var(--badge-background-color, var(--helix-color-info-surface));
89
+ :host([tone="info"]) {
90
+ --_badge-fill: var(--helix-color-info-surface);
89
91
  }
90
92
 
91
- :host([tone="success"]) [part="indicator"] {
92
- background-color: var(--badge-background-color, var(--helix-color-success-surface));
93
+ :host([tone="success"]) {
94
+ --_badge-fill: var(--helix-color-success-surface);
93
95
  }
94
96
 
95
- :host([tone="warning"]) [part="indicator"] {
96
- background-color: var(--badge-background-color, var(--helix-color-warning-surface));
97
+ :host([tone="warning"]) {
98
+ --_badge-fill: var(--helix-color-warning-surface);
97
99
  }
98
100
 
99
- :host([tone="danger"]) [part="indicator"] {
100
- background-color: var(--badge-background-color, var(--helix-color-danger-surface));
101
+ :host([tone="danger"]) {
102
+ --_badge-fill: var(--helix-color-danger-surface);
101
103
  }
102
104
 
103
- :host([tone="neutral"]) [part="indicator"] {
104
- background-color: var(--badge-background-color, var(--helix-color-surface-raised));
105
- color: var(--badge-color, var(--helix-color-foreground));
105
+ :host([tone="neutral"]) {
106
+ --_badge-fill: var(--helix-color-surface-raised);
107
+ --_badge-on: var(--helix-color-foreground);
106
108
  }
107
109
 
108
110
  :host([placement="top-start"]) [part="indicator"],
@@ -140,6 +142,74 @@ position: static;
140
142
  translate: none;
141
143
  }
142
144
 
145
+ :host([emphasis="outlined"]) [part="indicator"] {
146
+ background-color: var(--badge-background-color, var(--helix-color-background));
147
+ box-shadow:
148
+ inset 0 0 0 var(--helix-border-thick) var(--_badge-fill),
149
+ 0 0 0 var(--helix-border-thick) var(--badge-ring-color, var(--helix-color-background));
150
+ color: var(--badge-color, var(--helix-color-foreground));
151
+ }
152
+
153
+ :host([emphasis="subtle"]) [part="indicator"] {
154
+ background-color: var(
155
+ --badge-background-color,
156
+ color-mix(in oklab, var(--_badge-fill) 22%, var(--helix-color-background))
157
+ );
158
+ color: var(--badge-color, var(--helix-color-foreground));
159
+ }
160
+
161
+ :host([emphasis="outlined"][placement="inline"]) [part="indicator"] {
162
+ box-shadow: inset 0 0 0 var(--helix-border-thick) var(--_badge-fill);
163
+ }
164
+
165
+ :host([attention="pulse"]) [part="indicator"]:not([data-blank]) {
166
+ animation: badge-pulse var(--badge-attention-duration, 1.5s) var(--helix-easing-standard) infinite;
167
+ }
168
+
169
+ :host([attention="bounce"]) [part="indicator"]:not([data-blank]) {
170
+ animation: badge-bounce var(--badge-attention-duration, 1s) var(--helix-easing-standard) infinite;
171
+ }
172
+
173
+ @keyframes badge-pulse {
174
+ 0%,
175
+ 100% {
176
+ transform: scale(1);
177
+ }
178
+
179
+ 50% {
180
+ transform: scale(1.18);
181
+ }
182
+ }
183
+
184
+ @keyframes badge-bounce {
185
+ 0%,
186
+ 20%,
187
+ 53%,
188
+ 100% {
189
+ transform: translateY(0);
190
+ }
191
+
192
+ 40%,
193
+ 43% {
194
+ transform: translateY(-30%);
195
+ }
196
+
197
+ 70% {
198
+ transform: translateY(-15%);
199
+ }
200
+
201
+ 90% {
202
+ transform: translateY(-4%);
203
+ }
204
+ }
205
+
206
+ @media (prefers-reduced-motion: reduce) {
207
+ :host([attention="pulse"]) [part="indicator"]:not([data-blank]),
208
+ :host([attention="bounce"]) [part="indicator"]:not([data-blank]) {
209
+ animation: none;
210
+ }
211
+ }
212
+
143
213
  @media print {
144
214
  :host {
145
215
  box-shadow: none;
@@ -20,6 +20,7 @@ import type { CSSResultGroup } from 'lit';
20
20
  * @aliases Count, Indicator, Pip
21
21
  * @documentation https://fusion.dev/helix/components/badge
22
22
  *
23
+ * @cssproperty --badge-attention-duration - How long one beat of `attention` takes.
23
24
  * @cssproperty --badge-background-color - The fill of the indicator.
24
25
  * @cssproperty --badge-color - The color of the count, to pair with a fill set through `--badge-background-color`.
25
26
  * @cssproperty --badge-gap - The space between the content and the count under `placement="inline"`.
@@ -27,6 +28,23 @@ import type { CSSResultGroup } from 'lit';
27
28
  * @cssproperty --badge-ring-color - The color of the ring between the indicator and what is behind it.
28
29
  */
29
30
  export declare class HelixBadge extends HelixHeadlessBadge {
31
+ /**
32
+ * An effect that draws the eye to the mark: `pulse` swells it in a loop and
33
+ * `bounce` hops it, as Web Awesome's badge does.
34
+ *
35
+ * Absent is still, which is right for almost every count: a mark that moves
36
+ * is a mark that interrupts. Still for a reader who asked the system for
37
+ * less motion, and while the mark is going away.
38
+ */
39
+ attention?: 'bounce' | 'pulse';
40
+ /**
41
+ * How strongly the mark is drawn: `solid`, `outlined` or `subtle`, `Tag`'s
42
+ * axis.
43
+ *
44
+ * Absent is `solid`, the tone's fill. `outlined` draws the fill as a ring on
45
+ * the page's ground and `subtle` as a tint, both with the page's ink.
46
+ */
47
+ emphasis?: 'outlined' | 'solid' | 'subtle';
30
48
  /**
31
49
  * Where the mark sits: a corner, `top-start`, `bottom-end` or `bottom-start`,
32
50
  * or `inline`, in the line after the content.
@@ -11,6 +11,12 @@ import { styles } from './badge-root.css.js';
11
11
  let HelixBadge = class HelixBadge extends HelixHeadlessBadge {
12
12
  static { this.styles = styles; }
13
13
  };
14
+ __decorate([
15
+ property({ reflect: true })
16
+ ], HelixBadge.prototype, "attention", void 0);
17
+ __decorate([
18
+ property({ reflect: true })
19
+ ], HelixBadge.prototype, "emphasis", void 0);
14
20
  __decorate([
15
21
  property({ reflect: true })
16
22
  ], HelixBadge.prototype, "placement", void 0);