@fusion.dev/helix-docs 0.206.0 → 0.207.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.
- package/README.md +22 -2
- package/content/angular.mdx +37 -4
- package/content/browser-support.mdx +3 -3
- package/content/building-on-the-headless-tier.mdx +84 -21
- package/content/charting.mdx +50 -0
- package/content/coding-agents.mdx +2 -1
- package/content/color-modes.mdx +2 -2
- package/content/color.mdx +35 -5
- package/content/diagnostics.mdx +255 -0
- package/content/graphs.mdx +6 -0
- package/content/iconography.mdx +31 -13
- package/content/installation.mdx +34 -10
- package/content/intent-reference.mdx +27 -3
- package/content/introduction.mdx +3 -2
- package/content/localization.mdx +86 -4
- package/content/maps.mdx +65 -13
- package/content/metrics.mdx +252 -249
- package/content/other-frameworks.mdx +52 -12
- package/content/overriding-a-component.mdx +11 -10
- package/content/react.mdx +15 -1
- package/content/recipes.mdx +18 -2
- package/content/sandbox.mdx +75 -25
- package/content/server-rendering.mdx +2 -2
- package/content/state-and-control.mdx +7 -1
- package/content/surfaces-and-separation.mdx +2 -1
- package/content/svelte.mdx +54 -8
- package/content/theme-builder.mdx +15 -3
- package/content/theming.mdx +5 -1
- package/content/tiers.mdx +15 -14
- package/content/using-helix-with-an-agent.mdx +12 -5
- package/content/using-the-elements.mdx +24 -23
- package/content/versioning.mdx +15 -8
- package/content/visual-axes.mdx +11 -4
- package/content/vue.mdx +28 -1
- package/content/without-a-bundler.mdx +9 -7
- package/dist/index.d.ts +1 -3
- package/dist/index.js +0 -1
- package/dist/pages.generated.js +7 -1
- package/dist/reference.d.ts +70 -6
- package/dist/reference.js +13 -2
- package/dist/sources.generated.js +1343 -458
- package/package.json +13 -12
|
@@ -199,7 +199,8 @@ typos, in your other components.
|
|
|
199
199
|
|
|
200
200
|
\`\`\`html
|
|
201
201
|
<helix-input
|
|
202
|
-
|
|
202
|
+
#field
|
|
203
|
+
(change)="name = field.value"
|
|
203
204
|
[value]="name"
|
|
204
205
|
/>
|
|
205
206
|
\`\`\`
|
|
@@ -207,6 +208,10 @@ typos, in your other components.
|
|
|
207
208
|
\`[prop]\` sets a property, which a value that is not a string needs. \`(event)\`
|
|
208
209
|
binds with \`addEventListener\`, so use the event's own name.
|
|
209
210
|
|
|
211
|
+
\`#field\` gives the template a reference to the element, typed as its class once
|
|
212
|
+
you import its family. Under \`strictTemplates\`, \`$event.target\` is an
|
|
213
|
+
\`EventTarget | null\`, which has no \`value\`.
|
|
214
|
+
|
|
210
215
|
**Do not use \`attr.\` for a stateful property.** \`[attr.value]="name"\` writes the
|
|
211
216
|
attribute. The attribute seeds the control and is out of date as soon as someone
|
|
212
217
|
types in it. The property holds the current value.
|
|
@@ -218,9 +223,17 @@ checking that typings would feed, so Angular would not enforce a generated file.
|
|
|
218
223
|
We publish the manifest as \`@fusion.dev/helix/custom-elements.json\`, so you can
|
|
219
224
|
generate your own files from it.
|
|
220
225
|
|
|
221
|
-
As a result, a misspelled property fails without an error in Angular.
|
|
222
|
-
|
|
223
|
-
|
|
226
|
+
As a result, a misspelled property fails without an error in Angular. Check
|
|
227
|
+
property names in code review.
|
|
228
|
+
|
|
229
|
+
The other frameworks check a misspelled name to different degrees:
|
|
230
|
+
|
|
231
|
+
- In React, TypeScript reports a misspelled prop unless its name has a hyphen.
|
|
232
|
+
See [React](./react).
|
|
233
|
+
- In Vue, \`vue-tsc\` reports one only when \`checkUnknownProps\` is on. See
|
|
234
|
+
[Vue](./vue).
|
|
235
|
+
- In Svelte, \`svelte-check\` reports an attribute that the Helix tag does not
|
|
236
|
+
declare.
|
|
224
237
|
|
|
225
238
|
## Refuse a change
|
|
226
239
|
|
|
@@ -228,6 +241,26 @@ code review.
|
|
|
228
241
|
value you set and then applies its own changes. To refuse a change, listen for
|
|
229
242
|
the \`helix-before-*\` event and call \`preventDefault()\`. See
|
|
230
243
|
[State and control](./state-and-control).
|
|
244
|
+
|
|
245
|
+
To write your own value into a field you type into, set your signal and then
|
|
246
|
+
call \`detectChanges()\` on the component's \`ChangeDetectorRef\`:
|
|
247
|
+
|
|
248
|
+
\`\`\`ts
|
|
249
|
+
private readonly view = inject(ChangeDetectorRef)
|
|
250
|
+
|
|
251
|
+
protected takeHandle(event: Event): void {
|
|
252
|
+
event.preventDefault()
|
|
253
|
+
const { value } = (event as CustomEvent<{ value: string }>).detail
|
|
254
|
+
this.handle.set(clean(value))
|
|
255
|
+
this.view.detectChanges()
|
|
256
|
+
}
|
|
257
|
+
\`\`\`
|
|
258
|
+
|
|
259
|
+
Without zone.js, Angular renders a signal change in a later task. A key that
|
|
260
|
+
arrives before that task lands in the text from before your value, and the keys
|
|
261
|
+
typed in between are lost. A barcode scanner, a password manager, and a test
|
|
262
|
+
send keys that fast. \`detectChanges()\` renders the change before your handler
|
|
263
|
+
returns.
|
|
231
264
|
`,
|
|
232
265
|
'browser-support': `---
|
|
233
266
|
description: The browser features Helix uses, and the browser we test in.
|
|
@@ -244,7 +277,7 @@ support in older browsers. Your browser needs every feature Helix uses.
|
|
|
244
277
|
|
|
245
278
|
## What we test
|
|
246
279
|
|
|
247
|
-
We test in Chromium, through Playwright. The DOM specs run in it, and
|
|
280
|
+
We test in Chromium, through Playwright. The DOM specs run in it, and the pixel
|
|
248
281
|
baselines are captured in it, on Linux, with the network blocked.
|
|
249
282
|
|
|
250
283
|
We do not run the tests in Firefox or Safari. The rest of this page lists the
|
|
@@ -258,9 +291,9 @@ something broken, please report it. Our tests would not have caught it.
|
|
|
258
291
|
| Custom elements and shadow DOM | Everything |
|
|
259
292
|
| \`ElementInternals\` | Form participation, and ARIA that has to cross a shadow boundary |
|
|
260
293
|
| Popover API | Every overlay: menu, tooltip, select, combobox, dialog |
|
|
261
|
-
| \`light-dark()\` |
|
|
294
|
+
| \`light-dark()\` | Every color token, and each component color that differs between the modes |
|
|
262
295
|
| \`oklch()\` | Every color in the system |
|
|
263
|
-
| \`:has()\` |
|
|
296
|
+
| \`:has()\` | Relational styling, mostly of slotted content |
|
|
264
297
|
| \`@starting-style\` | Entry transitions on overlays |
|
|
265
298
|
| \`:state()\` | Every boolean state the library publishes: \`open\`, \`checked\`, \`selected\`, \`invalid\`, \`read\`, \`has-label\`, \`settled\`, \`empty\`, \`controls\` |
|
|
266
299
|
| \`CSS.highlights\` | Filter-match highlighting, in \`Command\` and \`Combobox\` |
|
|
@@ -371,9 +404,10 @@ headless manifest carries the static, so you can find it there.
|
|
|
371
404
|
**When a component renders another component, register both.**
|
|
372
405
|
|
|
373
406
|
If you register \`acme-combobox\` without \`acme-listbox\`, the panel renders a tag
|
|
374
|
-
that is not defined.
|
|
375
|
-
|
|
376
|
-
|
|
407
|
+
that is not defined. An unregistered custom element is an inert, unstyled inline
|
|
408
|
+
box, so the panel shows every option with no list around them, and typing
|
|
409
|
+
filters nothing. In development, the combobox says so once in the console and
|
|
410
|
+
names the tag to define.
|
|
377
411
|
|
|
378
412
|
[//]: # (renders)
|
|
379
413
|
|
|
@@ -413,7 +447,8 @@ everything those families render in turn. For example, \`command\` reaches
|
|
|
413
447
|
**The elements you write inside a component are yours to register as well.**
|
|
414
448
|
A select renders none of the options you write in it, so \`acme-select\` needs
|
|
415
449
|
your \`acme-option\` registered beside it. In development, a component holding a
|
|
416
|
-
child of its tier that nobody registered says so once
|
|
450
|
+
child of its tier that nobody registered says so once and names the tag to
|
|
451
|
+
define.
|
|
417
452
|
A listbox given \`items\` and a filter input given a \`source\` draw options of
|
|
418
453
|
your tier from data, and those need \`acme-option\` registered too.
|
|
419
454
|
|
|
@@ -426,7 +461,7 @@ generated table is always current.
|
|
|
426
461
|
|
|
427
462
|
### A \`:not(:defined)\` rule
|
|
428
463
|
|
|
429
|
-
\`@fusion.dev/helix\` ships an \`elements.css\` that hides each of its tags until
|
|
464
|
+
\`@fusion.dev/helix-styles\` ships an \`elements.css\` that hides each of its tags until
|
|
430
465
|
the tag's definition loads. Your page then does not show unstyled text that
|
|
431
466
|
changes into components. The headless package ships no such file, and no tool
|
|
432
467
|
can generate one for tags you have not named yet. Write the rule for your own
|
|
@@ -439,26 +474,85 @@ acme-listbox:not(:defined) {
|
|
|
439
474
|
}
|
|
440
475
|
\`\`\`
|
|
441
476
|
|
|
442
|
-
|
|
477
|
+
The rule hides each element until its definition loads, and the layout
|
|
478
|
+
settles when the elements upgrade.
|
|
479
|
+
|
|
480
|
+
### A rule that hides the \`live\` parts
|
|
481
|
+
|
|
482
|
+
A \`live\` part is a live region. It holds words only a screen reader should
|
|
483
|
+
read, such as how many options a filter left, and the headless tier draws it
|
|
484
|
+
as ordinary text. Hide each one visually with a clip, which keeps it in the
|
|
485
|
+
accessibility tree:
|
|
486
|
+
|
|
487
|
+
\`\`\`css
|
|
488
|
+
acme-combobox::part(live),
|
|
489
|
+
acme-combobox::part(listbox-live) {
|
|
490
|
+
block-size: 1px;
|
|
491
|
+
clip-path: inset(50%);
|
|
492
|
+
inline-size: 1px;
|
|
493
|
+
overflow: hidden;
|
|
494
|
+
position: absolute;
|
|
495
|
+
white-space: nowrap;
|
|
496
|
+
}
|
|
497
|
+
\`\`\`
|
|
498
|
+
|
|
499
|
+
\`display: none\` and \`visibility: hidden\` take a region out of the
|
|
500
|
+
accessibility tree as well, and a screen reader then hears nothing. A
|
|
501
|
+
component forwards the \`live\` part of a component it renders under a name of
|
|
502
|
+
its own, such as \`listbox-live\` on Combobox. The styled tier hides every one
|
|
503
|
+
of them.
|
|
443
504
|
|
|
444
505
|
### Typings for your tags
|
|
445
506
|
|
|
446
|
-
Your typings come from a manifest of your package
|
|
447
|
-
\`@
|
|
448
|
-
inherit from ours. Name each Helix package you extend and where its manifest is:
|
|
507
|
+
Your typings come from a custom elements manifest of your package, written by
|
|
508
|
+
\`@custom-elements-manifest/analyzer\`:
|
|
449
509
|
|
|
450
510
|
\`\`\`sh
|
|
451
|
-
|
|
452
|
-
--
|
|
453
|
-
--dependency @fusion.dev/helix-headless=node_modules/@fusion.dev/helix-headless/custom-elements.json \\
|
|
454
|
-
--dependency @fusion.dev/helix-core=node_modules/@fusion.dev/helix-core/custom-elements.json \\
|
|
455
|
-
> custom-elements.json
|
|
511
|
+
npx @custom-elements-manifest/analyzer analyze --litelement --dependencies \\
|
|
512
|
+
--globs "src/**/*.ts"
|
|
456
513
|
\`\`\`
|
|
457
514
|
|
|
458
|
-
|
|
515
|
+
Each flag does one job, and the manifest is incomplete without either:
|
|
516
|
+
|
|
517
|
+
- \`--dependencies\` reads the \`custom-elements.json\` each Helix package ships
|
|
518
|
+
and names in its \`package.json\`, so \`acme-combobox\` gets the members,
|
|
519
|
+
attributes, events, slots and parts it inherits from ours.
|
|
520
|
+
- \`--litelement\` reads Lit's decorators, so the properties you declare with
|
|
521
|
+
\`@property\` are attributes too.
|
|
522
|
+
|
|
523
|
+
Then run a typings generator over the manifest. Our own typings start from
|
|
459
524
|
\`@wc-toolkit/jsx-types\` and \`@wc-toolkit/vuejs-types\`. The typings for
|
|
460
|
-
\`acme-combobox\` then include the
|
|
461
|
-
|
|
525
|
+
\`acme-combobox\` then include the members it inherits, as well as the ones you
|
|
526
|
+
wrote.
|
|
527
|
+
|
|
528
|
+
The generated file names Helix's event classes, such as
|
|
529
|
+
\`HelixBeforeChangeEvent\`, without importing them. Add one line at its top that
|
|
530
|
+
imports the names TypeScript reports as missing:
|
|
531
|
+
|
|
532
|
+
\`\`\`ts
|
|
533
|
+
import type { HelixBeforeChangeEvent, HelixFilterEvent } from '@fusion.dev/helix-core'
|
|
534
|
+
\`\`\`
|
|
535
|
+
|
|
536
|
+
Without that line, a project that sets \`skipLibCheck\` gets no error, and each
|
|
537
|
+
of those event handlers is typed \`any\`.
|
|
538
|
+
|
|
539
|
+
For a few tags in React, you can skip the generator. Each headless family
|
|
540
|
+
publishes its props in \`@fusion.dev/helix-headless/jsx\`, and your tag takes
|
|
541
|
+
them:
|
|
542
|
+
|
|
543
|
+
\`\`\`ts
|
|
544
|
+
import type { HelixHeadlessComboboxProps } from '@fusion.dev/helix-headless/jsx'
|
|
545
|
+
|
|
546
|
+
declare module 'react' {
|
|
547
|
+
namespace JSX {
|
|
548
|
+
interface IntrinsicElements {
|
|
549
|
+
'acme-combobox': HelixHeadlessComboboxProps
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
\`\`\`
|
|
554
|
+
|
|
555
|
+
A subclass that adds properties joins its own to them with \`&\`.
|
|
462
556
|
|
|
463
557
|
\`@fusion.dev/helix-cem\`, the step that runs those generators for our packages
|
|
464
558
|
and corrects what they write, is not published. It is build tooling for this
|
|
@@ -526,10 +620,12 @@ findings as data. The rules check what your components document and write:
|
|
|
526
620
|
|
|
527
621
|
### Tree-shaking
|
|
528
622
|
|
|
529
|
-
\`@fusion.dev/helix-headless\` declares \`sideEffects:
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
623
|
+
\`@fusion.dev/helix-headless\` declares \`"sideEffects": true\`, because its
|
|
624
|
+
modules register elements when you import them. A bundler that treated them as
|
|
625
|
+
free of side effects would drop a registration, and a tag would never upgrade.
|
|
626
|
+
Import each family from its own entry, such as
|
|
627
|
+
\`@fusion.dev/helix-headless/combobox\`, so your bundle carries only the families
|
|
628
|
+
you use.
|
|
533
629
|
|
|
534
630
|
### Documented parts
|
|
535
631
|
|
|
@@ -886,6 +982,56 @@ bar there, and a heatmap draws the cell with no tone. The readings after the gap
|
|
|
886
982
|
stay under their own labels, and the tooltip for that row still shows the
|
|
887
983
|
row's cells.
|
|
888
984
|
|
|
985
|
+
A cell holds a number when its whole text reads as one, such as \`1200\` or
|
|
986
|
+
\`-3.5\`. A formatted number, such as \`1,200\` or \`$1.2k\`, is a gap. To chart a
|
|
987
|
+
formatted cell, write its number in \`data-value\`:
|
|
988
|
+
|
|
989
|
+
\`\`\`html
|
|
990
|
+
<tr><th>Q1</th><td data-value="1200">$1.2k</td><td data-value="800">$800</td></tr>
|
|
991
|
+
\`\`\`
|
|
992
|
+
|
|
993
|
+
The chart reads \`data-value\` when a cell has one, and the cell's text otherwise.
|
|
994
|
+
The table and the tooltip still show \`$1.2k\`.
|
|
995
|
+
|
|
996
|
+
## Charts with many readings
|
|
997
|
+
|
|
998
|
+
A chart builds what it can show, and that is bounded by its size. The readings
|
|
999
|
+
themselves are read again each time the table or the data changes.
|
|
1000
|
+
|
|
1001
|
+
- **Names on the axis.** \`Line Chart\`, \`Area Chart\`, and \`Bar Chart\` draw every
|
|
1002
|
+
reading's name where every name fits. Where they do not, the chart draws a
|
|
1003
|
+
regular share of them, and always the first and the last. Where every row
|
|
1004
|
+
header holds a \`<time>\`, the names drawn start a period, such as the first
|
|
1005
|
+
reading of each month or each year. Where the names are numbers in order,
|
|
1006
|
+
such as years, they are round numbers. Other names are drawn every so many
|
|
1007
|
+
readings, counted from the first. The chart works out the share again when it
|
|
1008
|
+
is resized. The tooltip and the keys still say each reading's own name.
|
|
1009
|
+
- **Gridlines.** \`gridlines="category"\` rules the readings whose names are
|
|
1010
|
+
drawn.
|
|
1011
|
+
- **Lines.** Where a line has more readings than the plot has pixels across, it
|
|
1012
|
+
is drawn through the points those pixels can show: in each column of pixels,
|
|
1013
|
+
the first reading, the lowest, the highest, and the last.
|
|
1014
|
+
- **Markers.** \`markers\` marks every reading, with one path for each series.
|
|
1015
|
+
- **Columns.** \`Bar Chart\` draws each column as an element while every column
|
|
1016
|
+
and the space between two categories are at least a pixel wide. Past that, it
|
|
1017
|
+
draws each series' columns as one path, each column of pixels as tall as the
|
|
1018
|
+
tallest column in it, and draws the category the tooltip describes again over
|
|
1019
|
+
them, at least two pixels thick.
|
|
1020
|
+
- **Points.** \`Scatter Chart\` draws each series' points as paths. Points that
|
|
1021
|
+
overlap are drawn in separate groups, up to four deep, so each keeps its edge
|
|
1022
|
+
and a cluster reads darker. The point the keys rest on is drawn again over
|
|
1023
|
+
them.
|
|
1024
|
+
- **The table drawn from \`data\`.** Past a few hundred readings, the hidden table
|
|
1025
|
+
holds one page of them at a time. Its markup gives a screen reader the number
|
|
1026
|
+
of rows in the whole table and the place of each row, and the words after it
|
|
1027
|
+
say which readings the page holds. Two buttons after it show the previous and
|
|
1028
|
+
the next page. You press them with a screen reader as you read. They are left
|
|
1029
|
+
out of the tab order, so the plot stays the chart's one tab stop. The keys on
|
|
1030
|
+
the plot move through every reading.
|
|
1031
|
+
- **A table on the page.** A chart reads the whole table that \`for\` names, and
|
|
1032
|
+
the table is part of your page, however many rows it has. Where the readings
|
|
1033
|
+
are too many to put in the document, draw the chart from \`data\`.
|
|
1034
|
+
|
|
889
1035
|
## Series colors
|
|
890
1036
|
|
|
891
1037
|
Helix has twenty-four series color tokens, in order, from
|
|
@@ -1129,7 +1275,8 @@ agent uses most:
|
|
|
1129
1275
|
\`helix scan\`, \`helix diagnostics --once\`, and \`helix theme audit\` check what an
|
|
1130
1276
|
agent wrote, and the MCP server runs the same checks.
|
|
1131
1277
|
[Using Helix with an agent](./using-helix-with-an-agent#check-the-code-you-wrote)
|
|
1132
|
-
lists what each finds
|
|
1278
|
+
lists what each finds, and [Diagnostics](./diagnostics) sets the checks up in
|
|
1279
|
+
your app and in CI.
|
|
1133
1280
|
|
|
1134
1281
|
## The index
|
|
1135
1282
|
|
|
@@ -1260,11 +1407,20 @@ Choose a surface by where the element sits relative to the page. This is the
|
|
|
1260
1407
|
choice people most often get wrong. See [Surfaces and
|
|
1261
1408
|
separation](./surfaces-and-separation).
|
|
1262
1409
|
|
|
1410
|
+
A field is filled with \`well\`: 5% of \`foreground\` laid over whatever the field
|
|
1411
|
+
sits on, so its color depends on the surface under the field.
|
|
1412
|
+
|
|
1263
1413
|
### Inks
|
|
1264
1414
|
|
|
1265
1415
|
Inks are the colors that go on grounds. The base ink is \`foreground\`.
|
|
1266
1416
|
|
|
1267
|
-
- \`foreground-muted\`
|
|
1417
|
+
- \`foreground-muted\` is for text that recedes and is still read, such as a
|
|
1418
|
+
placeholder, a hint, or a caption. The engine solves it to the contrast
|
|
1419
|
+
target.
|
|
1420
|
+
- \`foreground-subtle\` recedes further. It is for marks that are not text, such
|
|
1421
|
+
as a separator, a chevron, or a grip, and for the text of a disabled control.
|
|
1422
|
+
It is a fixed step of the gray ramp, and the engine does not solve it, so its
|
|
1423
|
+
contrast is not guaranteed. Use \`foreground-muted\` for text that is read.
|
|
1268
1424
|
- Text on a filled color takes the ink named for that fill:
|
|
1269
1425
|
- \`foreground-on-primary\` on \`primary-surface\`
|
|
1270
1426
|
- \`foreground-on-accent\` on \`accent-surface\`
|
|
@@ -1278,8 +1434,16 @@ Intent colors show what something means.
|
|
|
1278
1434
|
\`-subtle\` variants. With no brand color set, both are ink: near-black in
|
|
1279
1435
|
light mode and near-white in dark mode.
|
|
1280
1436
|
- \`info\`, \`success\`, \`warning\`, and \`danger\` are for status. Each has a
|
|
1281
|
-
\`-surface\`
|
|
1282
|
-
|
|
1437
|
+
\`-surface\` variant, the filled ground that carries \`foreground-on-status\`,
|
|
1438
|
+
such as a danger button or a badge in a status tone, and a \`-surface-hover\`
|
|
1439
|
+
variant for that fill under the pointer.
|
|
1440
|
+
|
|
1441
|
+
### Rise and fall
|
|
1442
|
+
|
|
1443
|
+
\`rise\` and \`fall\` color a value by which way it moved, such as a price. They
|
|
1444
|
+
are \`success\` and \`danger\` unless you set them. Mainland China, Taiwan, and
|
|
1445
|
+
Japan draw a rise in red, so a page for one of those markets swaps the two. See
|
|
1446
|
+
[Rise and fall colors](./localization#rise-and-fall-colors).
|
|
1283
1447
|
|
|
1284
1448
|
### Series
|
|
1285
1449
|
|
|
@@ -1324,8 +1488,21 @@ OKLCH lets the theme engine *solve* for a palette from your brand color. See
|
|
|
1324
1488
|
|
|
1325
1489
|
The engine measures each ink against the ground it will be painted on. A brand
|
|
1326
1490
|
color that already meets the target there is used as it is. For any other color,
|
|
1327
|
-
the engine searches for the lightness that meets the target.
|
|
1328
|
-
|
|
1491
|
+
the engine searches for the lightness that meets the target. A pair that cannot
|
|
1492
|
+
meet its target, such as one with a color you pinned, is reported in
|
|
1493
|
+
\`violations\`.
|
|
1494
|
+
|
|
1495
|
+
\`foreground\` and \`foreground-muted\` are also written in fields, so the engine
|
|
1496
|
+
solves them against the well over the page, a card, a raised surface, a dialog,
|
|
1497
|
+
and a sunken surface. The report names each of these grounds as the well over
|
|
1498
|
+
its surface, such as \`color-well over color-surface\`.
|
|
1499
|
+
|
|
1500
|
+
\`foreground-subtle\` is the one ink the engine does not solve. The range of
|
|
1501
|
+
readable grays has room for one quiet ink, and \`foreground-muted\` is that ink, so
|
|
1502
|
+
\`foreground-subtle\` stays a fixed step of the stylesheet's gray ramp and its
|
|
1503
|
+
contrast is not guaranteed. A component paints its quiet text in
|
|
1504
|
+
\`foreground-muted\`, and keeps \`foreground-subtle\` for marks that are not text and
|
|
1505
|
+
for the text of a disabled control.
|
|
1329
1506
|
|
|
1330
1507
|
The engine can solve only the pairs it knows about, so we check two things:
|
|
1331
1508
|
|
|
@@ -1376,8 +1553,8 @@ class, or a second copy of any value.
|
|
|
1376
1553
|
Each token is written once, with its light and dark values in \`light-dark()\`.
|
|
1377
1554
|
The \`color-scheme\` property alone decides which value applies. \`color-scheme\`
|
|
1378
1555
|
inherits, so one property sets the mode for an element and everything inside
|
|
1379
|
-
it.
|
|
1380
|
-
|
|
1556
|
+
it. A component that sets a color of its own for each mode uses \`light-dark()\`
|
|
1557
|
+
the same way.
|
|
1381
1558
|
|
|
1382
1559
|
## Set the mode
|
|
1383
1560
|
|
|
@@ -1630,6 +1807,262 @@ For the same reason, we wrote \`DrilldownController\`. Since it was first writte
|
|
|
1630
1807
|
was specific to menus. \`Menu\` and \`Drilldown\` now share it.
|
|
1631
1808
|
|
|
1632
1809
|
If you are about to write a second copy of some behavior, make it shared.
|
|
1810
|
+
`,
|
|
1811
|
+
diagnostics: `---
|
|
1812
|
+
description: Find the Helix mistakes the browser accepts without an error, on your running pages and in your code, with a setup for Vite, React, Next.js, and CI.
|
|
1813
|
+
section: Tooling
|
|
1814
|
+
title: Diagnostics
|
|
1815
|
+
---
|
|
1816
|
+
|
|
1817
|
+
# Diagnostics
|
|
1818
|
+
|
|
1819
|
+
Helix diagnostics finds the mistakes in your markup and styles that the browser
|
|
1820
|
+
accepts without an error. A \`::part()\` that names a part nothing publishes
|
|
1821
|
+
matches nothing, an attribute value outside the set a component takes falls
|
|
1822
|
+
back to the default, and \`disabled="false"\` turns \`disabled\` on. Each one is
|
|
1823
|
+
valid HTML or CSS, so nothing else reports it.
|
|
1824
|
+
|
|
1825
|
+
Two tools check your project, in two places:
|
|
1826
|
+
|
|
1827
|
+
- **\`initDiagnostics()\`**, from \`@fusion.dev/helix-diagnostics\`, checks each
|
|
1828
|
+
page as it runs in development. It prints each finding in DevTools, and a
|
|
1829
|
+
badge in the corner of the page lists them.
|
|
1830
|
+
- **\`helix scan\`**, from \`@fusion.dev/helix-cli\`, reads every stylesheet,
|
|
1831
|
+
template, and module in your project, and prints each finding with its file
|
|
1832
|
+
and line.
|
|
1833
|
+
|
|
1834
|
+
Both judge your code against the lint data of the Helix version you installed.
|
|
1835
|
+
A fault that both find has the same kind and the same fingerprint in each, so a
|
|
1836
|
+
report can join a page's findings to the scan's.
|
|
1837
|
+
|
|
1838
|
+
## Start diagnostics in your app
|
|
1839
|
+
|
|
1840
|
+
\`\`\`sh
|
|
1841
|
+
npm install --save-dev @fusion.dev/helix-diagnostics
|
|
1842
|
+
\`\`\`
|
|
1843
|
+
|
|
1844
|
+
Call \`initDiagnostics()\` once, before your app mounts. Put the call in a module
|
|
1845
|
+
of its own, so a production build can leave it out:
|
|
1846
|
+
|
|
1847
|
+
\`\`\`ts
|
|
1848
|
+
// src/diagnostics.ts
|
|
1849
|
+
import { initDiagnostics } from '@fusion.dev/helix-diagnostics'
|
|
1850
|
+
import lint from '@fusion.dev/helix/lint.json'
|
|
1851
|
+
|
|
1852
|
+
initDiagnostics({ environment: 'development', tiers: [{ lint }] })
|
|
1853
|
+
\`\`\`
|
|
1854
|
+
|
|
1855
|
+
\`tiers\` passes the lint data from your own \`node_modules\`, so diagnostics loads
|
|
1856
|
+
nothing from the network. Without it, diagnostics loads the lint data for the
|
|
1857
|
+
version on the page from a public npm CDN.
|
|
1858
|
+
|
|
1859
|
+
An add-on package publishes the lint data for its own elements, and you pass it
|
|
1860
|
+
in \`tiers\` the same way. \`@fusion.dev/helix-sandbox\` publishes it for
|
|
1861
|
+
\`helix-sandbox-editor\`:
|
|
1862
|
+
|
|
1863
|
+
\`\`\`ts
|
|
1864
|
+
import lint from '@fusion.dev/helix/lint.json'
|
|
1865
|
+
import sandbox from '@fusion.dev/helix-sandbox/lint.json'
|
|
1866
|
+
|
|
1867
|
+
initDiagnostics({ environment: 'development', tiers: [{ lint }, { lint: sandbox }] })
|
|
1868
|
+
\`\`\`
|
|
1869
|
+
|
|
1870
|
+
The CDN serves the two tiers' lint data alone, so an add-on's elements are
|
|
1871
|
+
checked only where its lint data is in \`tiers\`. Without it, each of them is an
|
|
1872
|
+
unknown tag.
|
|
1873
|
+
|
|
1874
|
+
Load the module in development only. In Vite, with React or any other
|
|
1875
|
+
framework, import it from your entry:
|
|
1876
|
+
|
|
1877
|
+
\`\`\`tsx
|
|
1878
|
+
// src/main.tsx
|
|
1879
|
+
if (import.meta.env.DEV) {
|
|
1880
|
+
void import('./diagnostics.js')
|
|
1881
|
+
}
|
|
1882
|
+
\`\`\`
|
|
1883
|
+
|
|
1884
|
+
\`vite build\` replaces \`import.meta.env.DEV\` with \`false\` and drops the import
|
|
1885
|
+
with it. In Next.js, import it from \`instrumentation-client.ts\`, which runs in
|
|
1886
|
+
the browser before your app hydrates:
|
|
1887
|
+
|
|
1888
|
+
\`\`\`ts
|
|
1889
|
+
// instrumentation-client.ts
|
|
1890
|
+
if (process.env.NODE_ENV === 'development') {
|
|
1891
|
+
void import('./app/diagnostics')
|
|
1892
|
+
}
|
|
1893
|
+
\`\`\`
|
|
1894
|
+
|
|
1895
|
+
Every Helix template starts diagnostics this way. Angular, Svelte, Vue, Solid,
|
|
1896
|
+
and a page with no bundler call it the same way, and the package's README says
|
|
1897
|
+
where each one's entry is.
|
|
1898
|
+
|
|
1899
|
+
From then on, diagnostics runs after the page loads, after a navigation, and
|
|
1900
|
+
after the page changes. It prints each finding once per page, and the badge
|
|
1901
|
+
shows only while the page has findings. Hover over a finding in the badge's
|
|
1902
|
+
list to outline the element it is about.
|
|
1903
|
+
|
|
1904
|
+
## Print the findings in your terminal
|
|
1905
|
+
|
|
1906
|
+
A page cannot write to your terminal, so the Helix command line receives each
|
|
1907
|
+
run beside your dev server. Install it, and turn the relay on where you call
|
|
1908
|
+
\`initDiagnostics()\`:
|
|
1909
|
+
|
|
1910
|
+
\`\`\`sh
|
|
1911
|
+
npm install --save-dev @fusion.dev/helix-cli
|
|
1912
|
+
\`\`\`
|
|
1913
|
+
|
|
1914
|
+
\`\`\`ts
|
|
1915
|
+
initDiagnostics({
|
|
1916
|
+
environment: 'development',
|
|
1917
|
+
sinks: { relay: true },
|
|
1918
|
+
tiers: [{ lint }],
|
|
1919
|
+
})
|
|
1920
|
+
\`\`\`
|
|
1921
|
+
|
|
1922
|
+
Then run the relay in your project's directory, beside the dev server:
|
|
1923
|
+
|
|
1924
|
+
\`\`\`sh
|
|
1925
|
+
npx helix diagnostics
|
|
1926
|
+
\`\`\`
|
|
1927
|
+
|
|
1928
|
+
Each Helix template has the script for it, so \`npm run diagnostics\` starts it
|
|
1929
|
+
there. The command prints each finding the first time a page has it. It listens
|
|
1930
|
+
on \`127.0.0.1:7417\`, takes runs only from pages on a local host, and serves the
|
|
1931
|
+
\`helix-diagnostics.json\` in the directory it runs in to the page.
|
|
1932
|
+
|
|
1933
|
+
## Scan your code
|
|
1934
|
+
|
|
1935
|
+
\`\`\`sh
|
|
1936
|
+
npx helix scan
|
|
1937
|
+
\`\`\`
|
|
1938
|
+
|
|
1939
|
+
\`helix scan\` judges your code against the lint data of every Helix package you
|
|
1940
|
+
install that publishes \`lint.json\`, the two tiers and an add-on such as
|
|
1941
|
+
\`@fusion.dev/helix-sandbox\`. It reads every file \`.gitignore\` leaves in, apart
|
|
1942
|
+
from tests and the folders a build writes. It reads CSS, Sass, and Less; HTML, Vue, Svelte,
|
|
1943
|
+
and Astro templates; MDX; and JSX, Lit's \`html\` and \`css\` templates, and
|
|
1944
|
+
Angular's inline templates in JavaScript and TypeScript. Each finding has its
|
|
1945
|
+
file and line:
|
|
1946
|
+
|
|
1947
|
+
\`\`\`
|
|
1948
|
+
helix scan: 1 error, 0 warnings, 0 info, and 0 unsettled, in 42 files
|
|
1949
|
+
error nested-interactive <helix-button> <helix-icon-button>
|
|
1950
|
+
\`helix-icon-button\` is inside \`helix-button\`, which draws its content inside a native button. …
|
|
1951
|
+
in src/toolbar.tsx:12
|
|
1952
|
+
\`\`\`
|
|
1953
|
+
|
|
1954
|
+
- **It reads every route.** A page sees only the routes someone opened.
|
|
1955
|
+
- **What only a page can settle is listed as \`unsettled\`**, such as a hook set
|
|
1956
|
+
on a tag that does not have it, which works where an element inside the tag
|
|
1957
|
+
has it. An unsettled finding never fails the scan.
|
|
1958
|
+
- **It checks an attribute where your code writes it as one.**
|
|
1959
|
+
\`disabled="false"\` is checked. \`?disabled=\${false}\` in Lit, \`:disabled="false"\`
|
|
1960
|
+
in Vue, and \`disabled={false}\` in JSX set the value at run time, and the page
|
|
1961
|
+
judges what they write.
|
|
1962
|
+
- **\`--file <path>\` reports one file's findings**, and still reads the whole
|
|
1963
|
+
project, so an import in another file counts. \`--stdin <language>\` checks a
|
|
1964
|
+
snippet.
|
|
1965
|
+
|
|
1966
|
+
It exits 1 on a finding at \`error\`, and 3 where it cannot run, such as where no
|
|
1967
|
+
installed Helix package has lint data.
|
|
1968
|
+
|
|
1969
|
+
## Run the checks in CI
|
|
1970
|
+
|
|
1971
|
+
\`--out\` writes a run to a CI file, and each command keeps the runs the others
|
|
1972
|
+
wrote there, so one file holds your code's run and your pages' runs:
|
|
1973
|
+
|
|
1974
|
+
\`\`\`sh
|
|
1975
|
+
npx helix scan --out reports/helix-diagnostics.json
|
|
1976
|
+
npx helix diagnostics --once http://localhost:4173/ --out reports/helix-diagnostics.json
|
|
1977
|
+
\`\`\`
|
|
1978
|
+
|
|
1979
|
+
\`helix diagnostics --once <url>\` opens a page headless, runs diagnostics on it
|
|
1980
|
+
once the page holds still, prints each finding, and exits. Repeat \`--once\` for
|
|
1981
|
+
each page. It needs Playwright installed beside \`@fusion.dev/helix-cli\`, and
|
|
1982
|
+
adds nothing to your app.
|
|
1983
|
+
|
|
1984
|
+
Where your own end-to-end tests open the pages, gather their runs with
|
|
1985
|
+
\`@fusion.dev/helix-diagnostics/ci\`:
|
|
1986
|
+
|
|
1987
|
+
\`\`\`ts
|
|
1988
|
+
import { ciSink } from '@fusion.dev/helix-diagnostics/ci'
|
|
1989
|
+
|
|
1990
|
+
// \`runs\` are the reports diagnostics returned in each page your job opened.
|
|
1991
|
+
const { code, summary } = ciSink(runs, { file: 'reports/helix-diagnostics.json' })
|
|
1992
|
+
console.log(summary)
|
|
1993
|
+
process.exitCode = code
|
|
1994
|
+
\`\`\`
|
|
1995
|
+
|
|
1996
|
+
A fault found on many pages is one line, with the pages it was on. A finding at
|
|
1997
|
+
\`error\` fails the job. \`failOn: 'warn'\` fails it on warnings too, and
|
|
1998
|
+
\`failOn: 'never'\` reports without failing. \`ciSink\` writes the whole file, so
|
|
1999
|
+
run \`helix scan --out\` after it to add your code's run.
|
|
2000
|
+
|
|
2001
|
+
## What it finds
|
|
2002
|
+
|
|
2003
|
+
| Kind | Severity | Found by | Means |
|
|
2004
|
+
| --- | --- | --- | --- |
|
|
2005
|
+
| \`dead-hook\` | error | both | A hook is set where nothing answers it, or a name close to a hook is set that nothing reads |
|
|
2006
|
+
| \`dead-part\` | error | both | A \`::part()\` names a part the element does not publish, or follows another \`::part()\` |
|
|
2007
|
+
| \`disabled-attribute\` | error | scan | A rule styles a form control's \`[disabled]\`, which a control a \`<fieldset disabled>\` disables does not carry. Write \`:disabled\` |
|
|
2008
|
+
| \`duplicate-registration\` | error | page | Two copies of Helix are on the page |
|
|
2009
|
+
| \`false-boolean\` | error | both | A boolean attribute is written \`="false"\`, which turns it on, as \`disabled="false"\` does. An attribute that \`"false"\` turns off, such as Drawer's \`modal\`, is not one |
|
|
2010
|
+
| \`invalid-value\` | error | both | An attribute with a closed set of values carries one outside it, so the element falls back to its default |
|
|
2011
|
+
| \`manifest-skew\` | error | page | The lint data is from another version than the page runs, so no other finding is reported |
|
|
2012
|
+
| \`native-element\` | warn | scan | A native element is written where an installed Helix family stands in for it, such as \`<button>\` for \`helix-button\` |
|
|
2013
|
+
| \`nested-interactive\` | error | both | An interactive element is inside a button or a link, which HTML forbids, such as \`helix-icon-button\` inside \`helix-button\` or \`helix-button\` inside an \`<a>\` |
|
|
2014
|
+
| \`primitive-token\` | error | scan | A rule reads a \`--_helix-\` primitive, which is internal to \`@fusion.dev/helix-styles\` |
|
|
2015
|
+
| \`unforwarded-part\` | warn | both | The headless tag publishes a part the styled tag does not forward, which is a gap in Helix |
|
|
2016
|
+
| \`unimported-tag\` | warn | scan | An element is written, and nothing in the project imports the entry that registers it |
|
|
2017
|
+
| \`unknown-attribute\` | error | both | A Helix element carries an attribute it does not take |
|
|
2018
|
+
| \`unknown-event\` | error | scan | A React \`on\` prop names an event the element does not dispatch |
|
|
2019
|
+
| \`unknown-slot\` | error | scan | A child names a slot its Helix element does not have, so it is not drawn |
|
|
2020
|
+
| \`unknown-tag\` | error | both | A rule or an element names a tag no installed package lists |
|
|
2021
|
+
| \`unknown-token\` | error | scan | A rule reads or sets a \`--helix-\` name the token set does not have |
|
|
2022
|
+
| \`unreflected-attribute\` | warn | both | A rule selects a Helix element by an attribute it never writes back, so it matches only markup a server wrote |
|
|
2023
|
+
|
|
2024
|
+
**A button inside a button is judged where the markup settles it.** Button,
|
|
2025
|
+
Icon Button, Copy Button, Toggle, Motion Toggle, Radio, and Tab draw their
|
|
2026
|
+
content inside a native button, and Skip Link draws its content inside a link.
|
|
2027
|
+
A slot for actions, such as a card's \`footer-actions\`, is not inside anything
|
|
2028
|
+
pressable. A framework's component, such as a router's \`<Link>\`, places its
|
|
2029
|
+
children itself, so \`helix scan\` stops at it, and the page sees the \`<a>\` it
|
|
2030
|
+
renders.
|
|
2031
|
+
|
|
2032
|
+
## Configure the checks
|
|
2033
|
+
|
|
2034
|
+
Both tools read \`helix-diagnostics.json\` from your project. The page cannot read
|
|
2035
|
+
files, so import it and pass it as \`file\`, or let the relay serve it:
|
|
2036
|
+
|
|
2037
|
+
\`\`\`json
|
|
2038
|
+
{
|
|
2039
|
+
"rules": {
|
|
2040
|
+
"native-element": "off",
|
|
2041
|
+
"unknown-tag": { "severity": "off", "tags": ["helix-mine"] }
|
|
2042
|
+
}
|
|
2043
|
+
}
|
|
2044
|
+
\`\`\`
|
|
2045
|
+
|
|
2046
|
+
A kind takes \`error\`, \`warn\`, \`info\`, or \`off\`, for every tag or for the tags
|
|
2047
|
+
you name. A setting that names nothing is refused, with the closest one that
|
|
2048
|
+
exists. Every setting is in the README of \`@fusion.dev/helix-diagnostics\`.
|
|
2049
|
+
|
|
2050
|
+
## Scope
|
|
2051
|
+
|
|
2052
|
+
The checks cover what the lint data can settle. They do not check:
|
|
2053
|
+
|
|
2054
|
+
- the value a token or a hook is set to, such as \`--helix-radii-md: red\`.
|
|
2055
|
+
\`helix scan\` checks each \`--helix-\` name you write;
|
|
2056
|
+
- a selector that descends into a component, such as \`helix-card .title\` or
|
|
2057
|
+
\`helix-button span\`. It matches only the elements you put inside the
|
|
2058
|
+
component;
|
|
2059
|
+
- a \`::part()\` on a selector with no Helix tag in it, such as
|
|
2060
|
+
\`.cta::part(label)\`, since the class names no element whose parts can be
|
|
2061
|
+
read;
|
|
2062
|
+
- an attribute value an expression sets, such as \`tone={tone}\` in JSX, in
|
|
2063
|
+
\`helix scan\`. The page judges the attribute it writes;
|
|
2064
|
+
- the contrast of a color you write yourself. \`helix theme audit\` checks the
|
|
2065
|
+
contrast of your theme's tokens.
|
|
1633
2066
|
`,
|
|
1634
2067
|
'elevation-and-material': `---
|
|
1635
2068
|
description: The layer, shadow, and material tokens for stacking, lifting, and translucent surfaces.
|
|
@@ -2567,6 +3000,12 @@ helix-graph:state(ready) > div {
|
|
|
2567
3000
|
}
|
|
2568
3001
|
\`\`\`
|
|
2569
3002
|
|
|
3003
|
+
Use the fade where the layout settles in a moment, such as a graph whose notes
|
|
3004
|
+
all have places. Leave a cosmos.gl graph shown. Its layout runs for several
|
|
3005
|
+
seconds by design, and the view follows the notes as they spread, so the reader
|
|
3006
|
+
watches the graph settle. Faded in on the state, its drawing is hidden for that
|
|
3007
|
+
whole time.
|
|
3008
|
+
|
|
2570
3009
|
## Draw the graph before it loads
|
|
2571
3010
|
|
|
2572
3011
|
\`graphSnapshot(nodes, links, options)\` draws the graph on your server as an
|
|
@@ -2871,24 +3310,42 @@ bundler can remove that record.
|
|
|
2871
3310
|
an order of precedence, the glyph on the page would come from whichever
|
|
2872
3311
|
directory ranked higher, and nothing would look wrong.
|
|
2873
3312
|
|
|
2874
|
-
##
|
|
3313
|
+
## Draw your own glyphs by name
|
|
2875
3314
|
|
|
2876
|
-
|
|
2877
|
-
|
|
2878
|
-
one line. You do not copy a path into a TypeScript object by hand.
|
|
3315
|
+
\`registerIcons\` makes your glyphs resolve by \`name\`, so plain HTML can draw
|
|
3316
|
+
them:
|
|
2879
3317
|
|
|
2880
|
-
|
|
2881
|
-
|
|
2882
|
-
|
|
2883
|
-
|
|
3318
|
+
\`\`\`ts
|
|
3319
|
+
import { registerIcons } from '@fusion.dev/helix/icon'
|
|
3320
|
+
import { peak } from './icons'
|
|
3321
|
+
|
|
3322
|
+
registerIcons([peak])
|
|
3323
|
+
\`\`\`
|
|
3324
|
+
|
|
3325
|
+
\`\`\`html
|
|
3326
|
+
<helix-icon name="peak"></helix-icon>
|
|
3327
|
+
\`\`\`
|
|
3328
|
+
|
|
3329
|
+
Each glyph registers under its own \`name\`. To register a whole set, build it
|
|
3330
|
+
with \`--index\` and pass the \`icons\` record the command writes to
|
|
3331
|
+
\`registerIcons\`.
|
|
3332
|
+
|
|
3333
|
+
\`registerIconLibrary\` resolves names under a prefix through a function of your
|
|
3334
|
+
own, such as one that points into a sprite sheet or fetches from a server. After
|
|
3335
|
+
\`registerIconLibrary('brand', resolve)\`, \`name="brand:peak"\` draws the glyph that
|
|
3336
|
+
\`resolve('peak')\` returns.
|
|
3337
|
+
|
|
3338
|
+
To sit beside the Helix set, draw a glyph with the same geometry, a 256 box at
|
|
3339
|
+
the bold weight. A glyph drawn at a different weight stands out next to the
|
|
3340
|
+
others.
|
|
2884
3341
|
|
|
2885
3342
|
## Product marks
|
|
2886
3343
|
|
|
2887
|
-
Helix and Cortex
|
|
2888
|
-
|
|
2889
|
-
|
|
2890
|
-
|
|
2891
|
-
|
|
3344
|
+
The set includes the Fusion, Helix, and Cortex marks, and the React and HTML5
|
|
3345
|
+
logos, so \`<helix-icon name="helix">\` draws the Helix mark. A mark identifies a
|
|
3346
|
+
product. An icon stands for an action. **The Helix and Cortex marks are each one
|
|
3347
|
+
rounded body with material removed.** Neither is a pictogram of a metaphor,
|
|
3348
|
+
because each stands beside the Fusion mark, a geometric monogram.
|
|
2892
3349
|
`,
|
|
2893
3350
|
installation: `---
|
|
2894
3351
|
description: What to install, the order of the stylesheets, and how to check that the elements registered.
|
|
@@ -2908,6 +3365,24 @@ use its semantic tokens and draw nothing without them. If the elements are
|
|
|
2908
3365
|
installed and the tokens are missing, your page shows unstyled boxes. There is
|
|
2909
3366
|
no fallback theme.
|
|
2910
3367
|
|
|
3368
|
+
## Start a new project
|
|
3369
|
+
|
|
3370
|
+
If you do not have a project yet, the scaffolder writes one with Helix
|
|
3371
|
+
installed, the stylesheets imported, and every element registered:
|
|
3372
|
+
|
|
3373
|
+
\`\`\`sh
|
|
3374
|
+
npm create @fusion.dev/helix-app@latest my-app -- --template react-vite
|
|
3375
|
+
\`\`\`
|
|
3376
|
+
|
|
3377
|
+
Leave out \`--template\`, and the command lists the templates and writes nothing.
|
|
3378
|
+
Each template starts with the same three pages:
|
|
3379
|
+
|
|
3380
|
+
- a landing page
|
|
3381
|
+
- a sorted, paged data table
|
|
3382
|
+
- a form that a \`<form>\` reads with \`FormData\`
|
|
3383
|
+
|
|
3384
|
+
The rest of this page adds Helix to a project you already have.
|
|
3385
|
+
|
|
2911
3386
|
## Import the stylesheets
|
|
2912
3387
|
|
|
2913
3388
|
\`\`\`ts
|
|
@@ -2941,18 +3416,19 @@ import the two files you want and skip \`helix.css\`.
|
|
|
2941
3416
|
\`tokens.css\` refers to the primitives throughout, so without
|
|
2942
3417
|
\`primitives.css\` every semantic token resolves to nothing. We measured a page without it:
|
|
2943
3418
|
\`--helix-color-background\` computes to \`rgba(0, 0, 0, 0)\`, and so does every
|
|
2944
|
-
other color. [Theming](theming
|
|
3419
|
+
other color. [Theming](./theming) also depends on this file, because without
|
|
2945
3420
|
it, setting \`--helix-seed-brand\` changes nothing.
|
|
2946
3421
|
- **\`tokens.css\`** declares the semantic layer, the custom properties grouped
|
|
2947
3422
|
by category, plus \`color-scheme: light dark\` on \`:root\`. The files below read
|
|
2948
3423
|
from it, and it reads from \`primitives.css\`.
|
|
2949
3424
|
- **\`base.css\`** holds the element reset, the type scale on bare \`h1\` through
|
|
2950
3425
|
\`h6\`, and the recipes for markup that Helix does not render. It reads tokens.
|
|
2951
|
-
- **\`elements.css\`**
|
|
2952
|
-
often left out, because a page works without it until the page
|
|
2953
|
-
server-rendered. An element that has not upgraded has no shadow root and no
|
|
2954
|
-
styles of its own, so the markup flashes unstyled
|
|
2955
|
-
|
|
3426
|
+
- **\`elements.css\`** hides each Helix element until its definition loads. It is
|
|
3427
|
+
the file most often left out, because a page works without it until the page
|
|
3428
|
+
is server-rendered. An element that has not upgraded has no shadow root and no
|
|
3429
|
+
styles of its own, so without the file the markup flashes unstyled. With it,
|
|
3430
|
+
the element stays hidden until it upgrades, and the layout settles when it
|
|
3431
|
+
does.
|
|
2956
3432
|
|
|
2957
3433
|
Import the files from a stylesheet you control, and keep them out of individual
|
|
2958
3434
|
components. The order then stays visible in one place.
|
|
@@ -2983,13 +3459,14 @@ Render one element and check whether it upgraded:
|
|
|
2983
3459
|
|
|
2984
3460
|
\`\`\`ts
|
|
2985
3461
|
await customElements.whenDefined('helix-button')
|
|
2986
|
-
console.log(document.querySelector('helix-button')?.shadowRoot
|
|
3462
|
+
console.log(document.querySelector('helix-button')?.shadowRoot instanceof ShadowRoot)
|
|
2987
3463
|
\`\`\`
|
|
2988
3464
|
|
|
2989
3465
|
\`true\` means the definition loaded and the element built its shadow root.
|
|
2990
|
-
\`false
|
|
2991
|
-
|
|
2992
|
-
the subpath of a component you did
|
|
3466
|
+
\`false\` means the page has no \`helix-button\` to check. A promise that never
|
|
3467
|
+
settles means the registration import did not run. Usually the import is outside
|
|
3468
|
+
a \`'use client'\` boundary, or you imported the subpath of a component you did
|
|
3469
|
+
not end up using.
|
|
2993
3470
|
|
|
2994
3471
|
If the element upgraded and the page still looks wrong, check the stylesheets
|
|
2995
3472
|
in the order above. The symptom tells you which layer is missing:
|
|
@@ -2999,6 +3476,10 @@ in the order above. The symptom tells you which layer is missing:
|
|
|
2999
3476
|
- Components that look right once JavaScript runs, but flash unstyled before
|
|
3000
3477
|
it, mean \`elements.css\` is missing.
|
|
3001
3478
|
|
|
3479
|
+
To check the rest of your markup and styles against the Helix you installed,
|
|
3480
|
+
start [Diagnostics](./diagnostics) in development, and run \`helix scan\` on your
|
|
3481
|
+
code. Each template starts diagnostics for you.
|
|
3482
|
+
|
|
3002
3483
|
## Framework setup
|
|
3003
3484
|
|
|
3004
3485
|
The elements are custom elements, and every framework can render them.
|
|
@@ -3054,7 +3535,7 @@ empty intent, \`{}\`, is a valid theme.
|
|
|
3054
3535
|
| \`accent\` | The second emphasis color. Defaults to the brand color, or to ink with no brand. \`{ from: 'brand', rotate: 180 }\` gives the complementary color, and needs a brand |
|
|
3055
3536
|
| \`background\` | The page's color, \`{ light, dark }\`, for a page that is not pure white or pure black. The surfaces keep their contrast with it, and the grays take its tint. A page no text color can reach the contrast target on is refused |
|
|
3056
3537
|
| \`neutral\` | The gray ramp, which can be tinted toward the brand. With \`background\`, it takes the page's tint |
|
|
3057
|
-
| \`status\` | \`info\`, \`success\`, \`warning\`, and \`danger\`. A color pins one.
|
|
3538
|
+
| \`status\` | \`info\`, \`success\`, \`warning\`, and \`danger\`. A color pins one. \`{ hue }\` moves its hue within the band that keeps its meaning |
|
|
3058
3539
|
| \`chroma\` | How saturated the palette is, independent of hue |
|
|
3059
3540
|
| \`scheme\` | \`'light'\` or \`'dark'\`, for a theme that has only one mode |
|
|
3060
3541
|
| \`tokens\` | Literal overrides by token name, for values no other field sets |
|
|
@@ -3069,6 +3550,17 @@ lightness moves only as far as the target needs.
|
|
|
3069
3550
|
\`danger\` rotated with the brand, your users would have to learn a new warning
|
|
3070
3551
|
color for each product.
|
|
3071
3552
|
|
|
3553
|
+
**A status hue stays in its band.** Each band, in OKLCH degrees, covers the
|
|
3554
|
+
colors that still read as that status. A hue outside its band is refused, and
|
|
3555
|
+
the error names the band.
|
|
3556
|
+
|
|
3557
|
+
| Status | Hue band | Colors |
|
|
3558
|
+
|---|---|---|
|
|
3559
|
+
| \`danger\` | 15 to 40 | The reds, from crimson to red-orange |
|
|
3560
|
+
| \`warning\` | 45 to 105 | Orange and amber to yellow |
|
|
3561
|
+
| \`success\` | 125 to 180 | The greens, from yellow-green to teal-green |
|
|
3562
|
+
| \`info\` | 200 to 280 | Cyan and blue to indigo |
|
|
3563
|
+
|
|
3072
3564
|
\`chroma\` sets how saturated the theme feels. Monotone, muted, restrained, and
|
|
3073
3565
|
vivid all describe chroma, and none of them describes a hue. \`chroma\` has two
|
|
3074
3566
|
settings:
|
|
@@ -3082,12 +3574,20 @@ the gray nearest the page that meets the contrast target, which is a mid gray.
|
|
|
3082
3574
|
For black and white, leave out \`brand\`. Neither applies to a color you pinned. A
|
|
3083
3575
|
pinned color is used exactly as you set it.
|
|
3084
3576
|
|
|
3577
|
+
\`tokens\` sets each value after the engine has solved the rest, so no other color
|
|
3578
|
+
is solved against it. Each pair that names the token measures it, and a value
|
|
3579
|
+
that misses its target is reported in \`violations\`. A name the engine does not
|
|
3580
|
+
emit is added as a token of your own. No default pair names it, so only a pair
|
|
3581
|
+
you list in \`pairings\` measures it. A value the engine cannot read is skipped: a
|
|
3582
|
+
token the engine emits keeps its derived value, and a name it does not emit is
|
|
3583
|
+
left out.
|
|
3584
|
+
|
|
3085
3585
|
## Contrast
|
|
3086
3586
|
|
|
3087
3587
|
| Field | What it controls |
|
|
3088
3588
|
|---|---|
|
|
3089
3589
|
| \`contrastTarget\` | Target contrast for body text. 4.5 is WCAG AA, and 7 is AAA |
|
|
3090
|
-
| \`pairings\` |
|
|
3590
|
+
| \`pairings\` | The foreground and background pairs to measure, by token name. A background of \`color-well over color-surface\` is a field's well over that surface. The list replaces the pairs from the engine's surface model. The engine measures each pair and solves none of them, and a pair that names a token the engine does not emit is reported as \`unverifiable\` |
|
|
3091
3591
|
|
|
3092
3592
|
The default is 4.5, the WCAG 2.1 AA figure. The European Accessibility Act
|
|
3093
3593
|
refers to WCAG 2.1 AA, so 4.5 is the figure with legal weight.
|
|
@@ -3141,11 +3641,16 @@ you the trade-off that a theme with heavy material makes.
|
|
|
3141
3641
|
|
|
3142
3642
|
| Field | What it controls |
|
|
3143
3643
|
|---|---|
|
|
3144
|
-
| \`namespace\` | The token prefix. Defaults to \`helix\` |
|
|
3644
|
+
| \`namespace\` | The token prefix. Defaults to \`helix\`. \`acme\` emits \`--acme-color-primary\` |
|
|
3145
3645
|
|
|
3146
3646
|
Use \`namespace\` when your product ships its own token layer built on the
|
|
3147
3647
|
engine. The rest of the library still reads \`--helix-*\`, so a new namespace
|
|
3148
3648
|
changes only your own tokens.
|
|
3649
|
+
|
|
3650
|
+
**A namespace is ASCII letters, digits, hyphens, and underscores, with no
|
|
3651
|
+
hyphen at either end.** The engine writes the hyphens around it. Any other
|
|
3652
|
+
namespace is refused with the value it received, such as an empty one, \`--acme\`,
|
|
3653
|
+
or \`acme tokens\`.
|
|
3149
3654
|
`,
|
|
3150
3655
|
introduction: `---
|
|
3151
3656
|
description: What Helix is, and the packages it includes.
|
|
@@ -3167,8 +3672,9 @@ The components are custom elements, so you can use them in any framework or
|
|
|
3167
3672
|
without one.
|
|
3168
3673
|
|
|
3169
3674
|
**Helix computes your theme.** You enter a few inputs, such as your brand color,
|
|
3170
|
-
and Helix computes every token from them.
|
|
3171
|
-
|
|
3675
|
+
and Helix computes every token from them. Helix measures the contrast of each
|
|
3676
|
+
text and background pair it solves, and reports any pair that misses its target.
|
|
3677
|
+
See [Theming](./theming).
|
|
3172
3678
|
|
|
3173
3679
|
**AI agents can read it.** A custom elements manifest describes every component,
|
|
3174
3680
|
with typed options, documented parts, and generated React prop names, so an
|
|
@@ -3223,7 +3729,7 @@ custom events. [React](./react) covers both.
|
|
|
3223
3729
|
See [Tiers](./tiers) for how the packages relate and which one to use.
|
|
3224
3730
|
`,
|
|
3225
3731
|
localization: `---
|
|
3226
|
-
description: Set the locale of date, time, and number controls, translate the built-in words,
|
|
3732
|
+
description: Set the locale of date, time, and number controls, translate the built-in words, lay out pages right to left, and color a rise and a fall for your market.
|
|
3227
3733
|
section: Essentials
|
|
3228
3734
|
title: Localization and RTL
|
|
3229
3735
|
---
|
|
@@ -3387,7 +3893,7 @@ Register the words for a language once, and every element in that language
|
|
|
3387
3893
|
speaks them:
|
|
3388
3894
|
|
|
3389
3895
|
\`\`\`js
|
|
3390
|
-
import { registerWords } from '@fusion.dev/helix'
|
|
3896
|
+
import { registerWords } from '@fusion.dev/helix/words'
|
|
3391
3897
|
|
|
3392
3898
|
registerWords('pl', {
|
|
3393
3899
|
'helix-combobox': {
|
|
@@ -3403,8 +3909,12 @@ registerWords('pl', {
|
|
|
3403
3909
|
\`\`\`
|
|
3404
3910
|
|
|
3405
3911
|
Each family's words are keyed by its tag, such as \`helix-combobox\`, and then
|
|
3406
|
-
by the property name. The headless and styled tiers share one key.
|
|
3407
|
-
|
|
3912
|
+
by the property name. The headless and styled tiers share one key.
|
|
3913
|
+
\`@fusion.dev/helix/words\` holds \`registerWords\` without the elements, so a page
|
|
3914
|
+
can register its words before it loads any, and it types each key and property.
|
|
3915
|
+
A page that uses only the headless tier imports
|
|
3916
|
+
\`@fusion.dev/helix-headless/words\`. With \`registerWords\` imported from another
|
|
3917
|
+
entry, import the types once:
|
|
3408
3918
|
|
|
3409
3919
|
\`\`\`ts
|
|
3410
3920
|
import type {} from '@fusion.dev/helix/words'
|
|
@@ -3554,6 +4064,81 @@ Aria's \`@react-stately/color\` translates, so you can reuse that table.
|
|
|
3554
4064
|
Helix ships English words and no translations. Pass the words from your own
|
|
3555
4065
|
translation system, either to \`registerWords\` or to the properties.
|
|
3556
4066
|
|
|
4067
|
+
## Rise and fall colors
|
|
4068
|
+
|
|
4069
|
+
Markets draw a price movement in different colors:
|
|
4070
|
+
|
|
4071
|
+
- Mainland China, Taiwan, and Japan draw a rise in red and a fall in green.
|
|
4072
|
+
- Hong Kong, like most other markets, draws a rise in green and a fall in red.
|
|
4073
|
+
|
|
4074
|
+
Colors that show a status, such as an error or a warning, are the same in all
|
|
4075
|
+
of these markets. Only a color that shows which way a value moved changes.
|
|
4076
|
+
|
|
4077
|
+
Two tokens hold these colors:
|
|
4078
|
+
|
|
4079
|
+
| Token | Default |
|
|
4080
|
+
|---|---|
|
|
4081
|
+
| \`--helix-color-rise\` | \`--helix-color-success\`, which is green |
|
|
4082
|
+
| \`--helix-color-fall\` | \`--helix-color-danger\`, which is red |
|
|
4083
|
+
|
|
4084
|
+
\`Trend\` uses them when you set \`color-by="movement"\`. Without it, the color of a
|
|
4085
|
+
trend follows \`good\`, which says whether the change is an improvement.
|
|
4086
|
+
|
|
4087
|
+
For a page that shows one of the markets that draw a rise in red, swap the two
|
|
4088
|
+
tokens at the root of the page, and set \`color-by\` on each trend:
|
|
4089
|
+
|
|
4090
|
+
\`\`\`css
|
|
4091
|
+
:root,
|
|
4092
|
+
[data-helix-theme] {
|
|
4093
|
+
--helix-color-fall: var(--helix-color-success);
|
|
4094
|
+
--helix-color-rise: var(--helix-color-danger);
|
|
4095
|
+
}
|
|
4096
|
+
\`\`\`
|
|
4097
|
+
|
|
4098
|
+
\`\`\`html
|
|
4099
|
+
<helix-trend
|
|
4100
|
+
color-by="movement"
|
|
4101
|
+
format-options='{"style":"percent","maximumFractionDigits":2}'
|
|
4102
|
+
value="0.0124"
|
|
4103
|
+
>
|
|
4104
|
+
<span slot="label">Shanghai Composite</span>
|
|
4105
|
+
</helix-trend>
|
|
4106
|
+
\`\`\`
|
|
4107
|
+
|
|
4108
|
+
An element with \`data-helix-theme\` declares every token again for the part of
|
|
4109
|
+
the page it themes, so the rule names \`[data-helix-theme]\` beside \`:root\`. To
|
|
4110
|
+
color one part of a page, such as a panel of Shanghai prices on a page about
|
|
4111
|
+
several markets, set the two tokens on that panel.
|
|
4112
|
+
|
|
4113
|
+
The two tokens point at the status colors, so after the swap each one still
|
|
4114
|
+
measures at least 4.5:1 on the page and on every surface, in both modes. A
|
|
4115
|
+
theme that changes \`success\` and \`danger\` changes them too.
|
|
4116
|
+
|
|
4117
|
+
**Helix does not set these from \`lang\`.** The language of a page does not name
|
|
4118
|
+
its market. A page in Chinese for Hong Kong draws a rise in green, a \`zh-Hant\`
|
|
4119
|
+
page with no region could be for Taiwan or for Hong Kong, and a page in English
|
|
4120
|
+
can show Shanghai prices. If the language of your page does decide its market,
|
|
4121
|
+
select on the language yourself:
|
|
4122
|
+
|
|
4123
|
+
\`\`\`css
|
|
4124
|
+
:is(:root, [data-helix-theme]):is(:lang(zh-CN), :lang(zh-TW), :lang(ja)) {
|
|
4125
|
+
--helix-color-fall: var(--helix-color-success);
|
|
4126
|
+
--helix-color-rise: var(--helix-color-danger);
|
|
4127
|
+
}
|
|
4128
|
+
\`\`\`
|
|
4129
|
+
|
|
4130
|
+
A page in \`zh-HK\` matches none of these, so it keeps green for a rise.
|
|
4131
|
+
|
|
4132
|
+
Other components color a series by its place in the list, or by a tone you
|
|
4133
|
+
name. To draw one in the color of a rise or a fall, set its own color to the
|
|
4134
|
+
token, such as \`--sparkline-color: var(--helix-color-rise)\` on the sparkline of
|
|
4135
|
+
a price that went up.
|
|
4136
|
+
|
|
4137
|
+
Sources:
|
|
4138
|
+
|
|
4139
|
+
- [Benzinga, on the colors of stock prices in China, Japan, and Taiwan](https://www.benzinga.com/general/education/21/10/23258001/did-you-know-you-dont-want-to-be-in-the-green-if-youre-trading-in-china-japan-or-taiwan)
|
|
4140
|
+
- [Feng Jiang, Rui Zhang, and Su Lu, on red and green in the Chinese and Hong Kong markets](https://www.behavioraleconomics.com/when-red-means-go)
|
|
4141
|
+
|
|
3557
4142
|
## What to check when you add a language
|
|
3558
4143
|
|
|
3559
4144
|
- **Long strings.** Labels in German and Finnish are often long. A control sized
|
|
@@ -3562,6 +4147,9 @@ translation system, either to \`registerWords\` or to the properties.
|
|
|
3562
4147
|
buttons may now fit four.
|
|
3563
4148
|
- **The \`lang\` attribute.** Set it as well as translating the strings.
|
|
3564
4149
|
Hyphenation, quotation marks, and font fallback all depend on it.
|
|
4150
|
+
- **A market's colors.** If the new language comes with a market that draws a
|
|
4151
|
+
rise in red, swap the rise and fall tokens. See
|
|
4152
|
+
[Rise and fall colors](#rise-and-fall-colors).
|
|
3565
4153
|
- **Numerals.** \`Intl\` formats the digits. A \`%\` or a currency symbol hardcoded
|
|
3566
4154
|
in your own markup does not move to the correct side.
|
|
3567
4155
|
- **Icons that point.** A caret that a component draws turns by itself. An icon
|
|
@@ -3717,6 +4305,11 @@ theme. Map labels meet a contrast target of 4.5 against the land. Pass
|
|
|
3717
4305
|
\`contrast\` to change it, \`language\` to choose which name each place is labeled
|
|
3718
4306
|
with, and \`tiles\` and \`glyphs\` to use your own tile server.
|
|
3719
4307
|
|
|
4308
|
+
The tiles come from OpenFreeMap unless you pass \`tiles\`, and so do the glyphs
|
|
4309
|
+
unless you pass \`glyphs\` or \`font\`. OpenFreeMap needs no key. Your reader's
|
|
4310
|
+
browser requests the map's tiles and glyphs from OpenFreeMap's servers, and
|
|
4311
|
+
\`mapSnapshot\` requests its tiles from them on your server.
|
|
4312
|
+
|
|
3720
4313
|
Pass \`font\` to draw the labels in your theme's typeface. MapLibre draws the
|
|
3721
4314
|
letters in the page from the font your page has loaded, so there are no glyph
|
|
3722
4315
|
files to fetch. \`mapFontFrom(frame)\` reads the typeface from your theme, and
|
|
@@ -3724,6 +4317,8 @@ files to fetch. \`mapFontFrom(frame)\` reads the typeface from your theme, and
|
|
|
3724
4317
|
create the map, because MapLibre keeps each letter as first drawn:
|
|
3725
4318
|
|
|
3726
4319
|
\`\`\`js
|
|
4320
|
+
import { fontReady, mapFontFrom } from '@fusion.dev/helix-map'
|
|
4321
|
+
|
|
3727
4322
|
const font = mapFontFrom(frame)
|
|
3728
4323
|
await fontReady(font)
|
|
3729
4324
|
const map = new maplibregl.Map({ container: 'map', style: mapLibreStyle(colors, { font }) })
|
|
@@ -3803,7 +4398,7 @@ const view = {
|
|
|
3803
4398
|
zoom: 14,
|
|
3804
4399
|
}
|
|
3805
4400
|
const snapshot = await mapSnapshot(view)
|
|
3806
|
-
// Inside helix-map, after <div id="map"></div>, write
|
|
4401
|
+
// Inside helix-map, after <div id="map"></div>, write snapshot.markup.
|
|
3807
4402
|
\`\`\`
|
|
3808
4403
|
|
|
3809
4404
|
Give MapLibre the same \`center\`, \`zoom\`, \`pitch\`, and \`bearing\`, and the snapshot
|
|
@@ -3811,15 +4406,55 @@ lines up with the map that replaces it. \`width\` and \`height\` are the box it
|
|
|
3811
4406
|
drawn for, and a smaller box shows its middle. A snapshot draws the land, water,
|
|
3812
4407
|
parks, buildings, and roads, and leaves the labels to MapLibre. Its colors are
|
|
3813
4408
|
your theme's tokens, so it is light or dark with the page and follows a theme
|
|
3814
|
-
change.
|
|
4409
|
+
change. Its \`markup\` is an empty string when any tile it needs cannot be read
|
|
3815
4410
|
in time, so a snapshot is whole or absent and the map draws in its place. Each
|
|
3816
4411
|
request is given \`timeout\` milliseconds, 3000 by default. Cache only a snapshot
|
|
3817
|
-
|
|
3818
|
-
|
|
3819
|
-
|
|
4412
|
+
whose \`markup\` is not empty.
|
|
4413
|
+
|
|
4414
|
+
A snapshot reads OpenFreeMap's tiles from your server by default. Pass the
|
|
4415
|
+
\`tiles\` you give \`mapLibreStyle\` as \`mapSnapshot(view, { tiles })\` to read
|
|
4416
|
+
your own.
|
|
4417
|
+
|
|
4418
|
+
**The snapshot carries the map credit.** Most tile licenses require the credit
|
|
4419
|
+
wherever the map is shown, including before MapLibre loads and on a page that
|
|
4420
|
+
never loads it. \`markup\` is the picture followed by the credit your tiles
|
|
4421
|
+
name, in a \`<span slot="attribution">\` marked \`data-helix-map-credit\`, so the
|
|
4422
|
+
frame shows the credit from the first paint. \`connectMap\` hides that credit
|
|
4423
|
+
once it writes MapLibre's, which names the same tiles.
|
|
4424
|
+
|
|
4425
|
+
A snapshot also holds the two apart: \`picture\`, the map alone, and \`credit\`,
|
|
4426
|
+
the credit as runs of words and links, each \`{ text }\` or \`{ href, text }\`.
|
|
4427
|
+
In a framework that renders the markup for you, such as React, put \`picture\`
|
|
3820
4428
|
in an element that is a direct child of the frame and has the
|
|
3821
|
-
\`data-helix-map-snapshot\` attribute. \`
|
|
3822
|
-
|
|
4429
|
+
\`data-helix-map-snapshot\` attribute. Write \`credit\` in a
|
|
4430
|
+
\`<span slot="attribution" data-helix-map-credit>\`, also a direct child of the
|
|
4431
|
+
frame:
|
|
4432
|
+
|
|
4433
|
+
\`\`\`jsx
|
|
4434
|
+
<helix-map label="Downtown Austin">
|
|
4435
|
+
<div id="map" />
|
|
4436
|
+
<div dangerouslySetInnerHTML={{ __html: snapshot.picture }} data-helix-map-snapshot="" />
|
|
4437
|
+
{snapshot.credit.length > 0 ? (
|
|
4438
|
+
<span data-helix-map-credit="" slot="attribution">
|
|
4439
|
+
{snapshot.credit.map((part, index) =>
|
|
4440
|
+
part.href ? (
|
|
4441
|
+
<a data-link="quiet" href={part.href} key={index} rel="noopener noreferrer" target="_blank">
|
|
4442
|
+
{part.text}
|
|
4443
|
+
</a>
|
|
4444
|
+
) : (
|
|
4445
|
+
part.text
|
|
4446
|
+
),
|
|
4447
|
+
)}
|
|
4448
|
+
</span>
|
|
4449
|
+
) : null}
|
|
4450
|
+
</helix-map>
|
|
4451
|
+
\`\`\`
|
|
4452
|
+
|
|
4453
|
+
\`connectMap\` hides both elements, and leaves removing them to your framework.
|
|
4454
|
+
|
|
4455
|
+
A credit you slot yourself, with no \`data-helix-map-credit\`, wins. Write
|
|
4456
|
+
\`picture\` alone beside it. A snapshot credit already in the frame is hidden
|
|
4457
|
+
when \`connectMap\` connects.
|
|
3823
4458
|
|
|
3824
4459
|
A snapshot is a flat map. A map zoomed out past about 6 is a globe in MapLibre,
|
|
3825
4460
|
so draw snapshots of city and street views.
|
|
@@ -3897,7 +4532,9 @@ finished.
|
|
|
3897
4532
|
| \`helix-map-contours\` | Contour lines, each at one height |
|
|
3898
4533
|
| \`helix-map-landcover\` | What covers the ground: forest, grass, farmland, sand, rock, and ice |
|
|
3899
4534
|
|
|
3900
|
-
|
|
4535
|
+
Markers, routes, areas, positions, points, and buildings take \`tone\`, such as
|
|
4536
|
+
\`primary\` or \`danger\`, for their color. Raster tiles keep their own colors, and
|
|
4537
|
+
terrain, contours, and landcover take theirs from your theme.
|
|
3901
4538
|
|
|
3902
4539
|
### Markers
|
|
3903
4540
|
|
|
@@ -3912,11 +4549,14 @@ Each element takes \`tone\`, such as \`primary\` or \`danger\`, for its color.
|
|
|
3912
4549
|
</helix-map-marker>
|
|
3913
4550
|
\`\`\`
|
|
3914
4551
|
|
|
3915
|
-
A marker is a button placed at its latitude and longitude. Its
|
|
3916
|
-
|
|
3917
|
-
|
|
3918
|
-
|
|
3919
|
-
|
|
4552
|
+
A marker is a button placed at its latitude and longitude. Its point is as
|
|
4553
|
+
large as the map's landmarks, in your primary color, with a dot in the middle
|
|
4554
|
+
and a halo of the same color around it, so your places stand out from the
|
|
4555
|
+
landmarks. Its \`label\` appears when you hover over or focus the marker. The
|
|
4556
|
+
\`icon\` slot holds a letter, a number, or an icon in place of the dot, and
|
|
4557
|
+
\`shape\` sets the marker's form: \`ring\`, \`rounded\`, \`square\`, \`diamond\`, or
|
|
4558
|
+
\`pin\`. Pressing a marker sends \`helix-activate\` from the frame with the
|
|
4559
|
+
marker's \`name\`, or its \`label\` if it has no name.
|
|
3920
4560
|
|
|
3921
4561
|
To show that a place has been visited, add \`passed\`. The marker is drawn in a
|
|
3922
4562
|
muted color, and screen readers hear its name followed by "passed". Use
|
|
@@ -4429,11 +5069,13 @@ We publish the method so you can repeat each measurement.
|
|
|
4429
5069
|
Each component has two columns. Components share a runtime, so no single
|
|
4430
5070
|
number gives the cost of one component:
|
|
4431
5071
|
|
|
4432
|
-
- **Alone** is a page that
|
|
5072
|
+
- **Alone** is a page that imports the component's own entry and nothing else. It includes the runtime the component needs, so a sum of Alone numbers counts the runtime once for every component.
|
|
4433
5073
|
- **Added** is what the component adds to a page that already has the runtime. It leaves out the shared runtime, so it understates the cost of your first component.
|
|
4434
5074
|
|
|
4435
5075
|
The cost on your page falls between the two numbers.
|
|
4436
5076
|
|
|
5077
|
+
A container that holds another family's elements does not register them, so neither column includes them. A page with a Select also imports \`option\`, and a page with a Data Table also imports \`table\`. Each component's page lists the entries to import beside it.
|
|
5078
|
+
|
|
4437
5079
|
## The shared runtime
|
|
4438
5080
|
|
|
4439
5081
|
The shared runtime is Lit and \`@fusion.dev/helix-core\`. Every element needs it,
|
|
@@ -4447,7 +5089,7 @@ on its own.
|
|
|
4447
5089
|
|
|
4448
5090
|
| | Raw | Gzip | Brotli |
|
|
4449
5091
|
|---|---|---|---|
|
|
4450
|
-
| Lit and \`@fusion.dev/helix-core\` |
|
|
5092
|
+
| Lit and \`@fusion.dev/helix-core\` | 264,965 | 85,796 | 73,181 |
|
|
4451
5093
|
|
|
4452
5094
|
## Whole packages
|
|
4453
5095
|
|
|
@@ -4458,12 +5100,12 @@ either barrel. The per-component tables show what your page pays.
|
|
|
4458
5100
|
|
|
4459
5101
|
| Package | Raw | Gzip | Brotli | |
|
|
4460
5102
|
|---|---|---|---|---|
|
|
4461
|
-
| \`@fusion.dev/helix-core\` |
|
|
4462
|
-
| \`@fusion.dev/helix-headless\` | 1,
|
|
5103
|
+
| \`@fusion.dev/helix-core\` | 264,174 | 85,657 | 72,818 | The element base class, mixins, decorators, and floating positioning. |
|
|
5104
|
+
| \`@fusion.dev/helix-headless\` | 1,457,984 | 408,952 | 306,808 | Every headless family. The barrel entry includes all of them. |
|
|
4463
5105
|
| \`@fusion.dev/helix-icons\` | 56,111 | 17,847 | 15,166 | The whole glyph set. |
|
|
4464
|
-
| \`@fusion.dev/helix-theme\` |
|
|
4465
|
-
| \`fusion_helix_theme_bg.wasm\` |
|
|
4466
|
-
| \`@fusion.dev/helix\` | 2,
|
|
5106
|
+
| \`@fusion.dev/helix-theme\` | 4,030 | 1,791 | 1,560 | The JavaScript that runs the derivation engine. Most sites run it at build time, and a theme switcher runs it in the browser. |
|
|
5107
|
+
| \`fusion_helix_theme_bg.wasm\` | 408,830 | 152,342 | 122,874 | The derivation engine itself, in WebAssembly. The entry above loads it when the engine runs. |
|
|
5108
|
+
| \`@fusion.dev/helix\` | 2,179,209 | 564,636 | 402,625 | Every styled family. |
|
|
4467
5109
|
| \`lit\` | 15,394 | 6,118 | 5,460 | Lit itself, listed so you can subtract it from every row above. |
|
|
4468
5110
|
|
|
4469
5111
|
## Stylesheets
|
|
@@ -4476,21 +5118,21 @@ shows the whole cost of using the base layer on its own.
|
|
|
4476
5118
|
|
|
4477
5119
|
| File | Raw | Gzip | Brotli | |
|
|
4478
5120
|
|---|---|---|---|---|
|
|
4479
|
-
| \`base.css\` |
|
|
5121
|
+
| \`base.css\` | 33,901 | 4,783 | 4,149 | The base layer. It styles markup Helix does not render, and it works with no JavaScript. |
|
|
4480
5122
|
| \`elements.css\` | 19,633 | 2,937 | 2,632 | Hides each element until its definition loads. It is generated from the tags in \`@fusion.dev/helix\`. |
|
|
4481
5123
|
| \`helix.css\` | 207 | 148 | 118 | The other four stylesheets in one import, in the order they must load. |
|
|
4482
5124
|
| \`primitives.css\` | 7,511 | 1,401 | 1,221 | The primitive layer. It is private, and it ships because \`tokens.css\` refers to it. |
|
|
4483
|
-
| \`tokens.css\` | 22,
|
|
5125
|
+
| \`tokens.css\` | 22,846 | 2,778 | 2,332 | The 199 semantic tokens. Every styled component needs them. |
|
|
4484
5126
|
|
|
4485
5127
|
## What the numbers say
|
|
4486
5128
|
|
|
4487
|
-
**Most of your first component is the shared runtime.** The median styled family is
|
|
5129
|
+
**Most of your first component is the shared runtime.** The median styled family is 19,331 bytes gzipped alone, and it adds 4,376 to a page that already has Helix. The shared runtime is 77 percent of the first component, and the component itself is 23 percent. Ten typical components cost the first number once, plus nine of the added number. The two columns in the tables below let you work that out for your own components.
|
|
4488
5130
|
|
|
4489
|
-
Across the 113 styled components, the smallest is \`code\` at 8,
|
|
5131
|
+
Across the 113 styled components, the smallest is \`code\` at 8,560 bytes gzipped alone, and the largest is \`data-table\` at 67,583. Every family together is 564,636.
|
|
4490
5132
|
|
|
4491
|
-
No family imports a barrel. The \`barrels\` rule in \`fusion x workspace\` enforces that. \`@fusion.dev/helix-headless\` marks every module in \`dist\` as having side effects, which is true of a file that calls \`customElements.define\`. A bundler can remove none of it. One \`import { HelixHeadlessButton } from '@fusion.dev/helix-headless'\` costs
|
|
5133
|
+
No family imports a barrel. The \`barrels\` rule in \`fusion x workspace\` enforces that. \`@fusion.dev/helix-headless\` marks every module in \`dist\` as having side effects, which is true of a file that calls \`customElements.define\`. A bundler can remove none of it. One \`import { HelixHeadlessButton } from '@fusion.dev/helix-headless'\` costs 408,952 bytes gzipped, whatever else the page imports. That is 21 times the median family above. All 113 styled families import the headless family they extend directly. The rule checks the shape of the import, so it applies to any package that sets \`sideEffects\`, whatever its name.
|
|
4492
5134
|
|
|
4493
|
-
The headless tier never had the problem, so it serves as the control. \`@fusion.dev/helix-core\` is \`sideEffects: false\`, and the median headless family is
|
|
5135
|
+
The headless tier never had the problem, so it serves as the control. \`@fusion.dev/helix-core\` is \`sideEffects: false\`, and the median headless family is 18,197 bytes gzipped alone.
|
|
4494
5136
|
|
|
4495
5137
|
## Compared with other systems
|
|
4496
5138
|
|
|
@@ -4502,43 +5144,43 @@ The table has no whole-library row. That row would compare how many components e
|
|
|
4502
5144
|
|
|
4503
5145
|
| Component | System | Raw | Gzip | Brotli |
|
|
4504
5146
|
|---|---|---|---|---|
|
|
4505
|
-
| \`button\` | **Helix** |
|
|
5147
|
+
| \`button\` | **Helix** | 42,638 | 14,385 | 12,821 |
|
|
4506
5148
|
| | Carbon | 133,058 | 20,959 | 17,685 |
|
|
4507
5149
|
| | Material Web | 47,060 | 14,173 | 12,591 |
|
|
4508
5150
|
| | Spectrum | 98,792 | 22,045 | 18,674 |
|
|
4509
5151
|
| | Web Awesome | 136,044 | 33,730 | 28,760 |
|
|
4510
|
-
| \`checkbox\` | **Helix** |
|
|
5152
|
+
| \`checkbox\` | **Helix** | 47,728 | 15,518 | 13,836 |
|
|
4511
5153
|
| | Carbon | 60,255 | 15,609 | 13,550 |
|
|
4512
5154
|
| | Material Web | 43,785 | 13,556 | 12,015 |
|
|
4513
5155
|
| | Spectrum | 62,492 | 15,437 | 13,481 |
|
|
4514
5156
|
| | Web Awesome | 115,251 | 28,728 | 24,420 |
|
|
4515
|
-
| \`dialog\` | **Helix** |
|
|
5157
|
+
| \`dialog\` | **Helix** | 44,311 | 15,226 | 13,612 |
|
|
4516
5158
|
| | Carbon | 510,866 | 67,390 | 39,189 |
|
|
4517
5159
|
| | Material Web | 34,238 | 11,510 | 10,229 |
|
|
4518
5160
|
| | Spectrum | 155,708 | 31,899 | 26,262 |
|
|
4519
5161
|
| | Web Awesome | 148,657 | 36,692 | 31,388 |
|
|
4520
|
-
| \`radio\` | **Helix** |
|
|
5162
|
+
| \`radio\` | **Helix** | 51,120 | 16,282 | 14,511 |
|
|
4521
5163
|
| | Carbon | 63,055 | 16,887 | 14,605 |
|
|
4522
5164
|
| | Material Web | 41,044 | 13,234 | 11,757 |
|
|
4523
5165
|
| | Spectrum | 38,685 | 10,660 | 9,370 |
|
|
4524
5166
|
| | Web Awesome | 112,026 | 27,416 | 23,128 |
|
|
4525
|
-
| \`switch\` | **Helix** |
|
|
5167
|
+
| \`switch\` | **Helix** | 44,449 | 14,978 | 13,325 |
|
|
4526
5168
|
| | Carbon | 69,423 | 17,127 | 14,764 |
|
|
4527
5169
|
| | Material Web | 50,155 | 14,577 | 12,935 |
|
|
4528
5170
|
| | Spectrum | 62,322 | 15,059 | 13,321 |
|
|
4529
5171
|
| | Web Awesome | 45,876 | 15,003 | 13,475 |
|
|
4530
|
-
| \`tabs\` | **Helix** |
|
|
5172
|
+
| \`tabs\` | **Helix** | 56,031 | 18,047 | 16,065 |
|
|
4531
5173
|
| | Carbon | 578,766 | 73,537 | 43,235 |
|
|
4532
5174
|
| | Material Web | 38,883 | 12,820 | 11,368 |
|
|
4533
5175
|
| | Spectrum | 64,875 | 18,298 | 16,283 |
|
|
4534
5176
|
| | Web Awesome | 153,439 | 37,030 | 31,737 |
|
|
4535
|
-
| \`tooltip\` | **Helix** |
|
|
5177
|
+
| \`tooltip\` | **Helix** | 62,502 | 22,262 | 19,733 |
|
|
4536
5178
|
| | Carbon | 189,382 | 30,354 | 22,273 |
|
|
4537
5179
|
| | Material Web | | not shipped | |
|
|
4538
5180
|
| | Spectrum | 136,599 | 40,552 | 35,286 |
|
|
4539
5181
|
| | Web Awesome | 70,568 | 24,005 | 21,107 |
|
|
4540
5182
|
|
|
4541
|
-
At least one system is smaller than Helix on \`checkbox\`, \`dialog\`, \`radio\`, \`switch\`, \`tabs\`. The largest gap is radio against Spectrum, at 5,
|
|
5183
|
+
At least one system is smaller than Helix on \`button\`, \`checkbox\`, \`dialog\`, \`radio\`, \`switch\`, \`tabs\`. The largest gap is radio against Spectrum, at 5,622 bytes gzipped. 8 of 27 peer measurements are smaller than the Helix row.
|
|
4542
5184
|
|
|
4543
5185
|
Helix's \`radio\` family includes Radio Group, so its row carries an element that some peer rows measure without.
|
|
4544
5186
|
|
|
@@ -4569,8 +5211,8 @@ A system covers a job when its manifest defines an element with one of a fixed l
|
|
|
4569
5211
|
| Chat | \`helix-chat\` | | | | |
|
|
4570
5212
|
| Command palette | \`helix-command\` | | | | |
|
|
4571
5213
|
| Product tour | \`helix-tour\` | | | \`sp-coachmark\` | |
|
|
4572
|
-
| Node diagrams | \`helix-canvas\`
|
|
4573
|
-
| Maps | \`helix-map\`
|
|
5214
|
+
| Node diagrams | \`helix-canvas\` | | | | |
|
|
5215
|
+
| Maps | \`helix-map\` | | | | |
|
|
4574
5216
|
| Video player | \`helix-video\` | | | | |
|
|
4575
5217
|
| Carousel | \`helix-carousel\` | | | | \`wa-carousel\` (experimental) |
|
|
4576
5218
|
| **Jobs covered** | 16 of 16 | 6 of 16 | 0 of 16 | 5 of 16 | 3 of 16 |
|
|
@@ -4612,112 +5254,112 @@ Structure, accessibility, state, and form participation. It has no presentation
|
|
|
4612
5254
|
|
|
4613
5255
|
| Family | Alone raw | Alone gzip | Alone brotli | Added raw | Added gzip | Added brotli |
|
|
4614
5256
|
|---|---|---|---|---|---|---|
|
|
4615
|
-
| \`accordion\` |
|
|
4616
|
-
| \`announcement\` |
|
|
4617
|
-
| \`app-header\` |
|
|
4618
|
-
| \`area-chart\` |
|
|
4619
|
-
| \`avatar\` |
|
|
4620
|
-
| \`badge\` |
|
|
4621
|
-
| \`bar-chart\` |
|
|
4622
|
-
| \`blockquote\` | 24,
|
|
4623
|
-
| \`breadcrumbs\` |
|
|
4624
|
-
| \`button\` |
|
|
4625
|
-
| \`button-group\` |
|
|
4626
|
-
| \`callout\` |
|
|
4627
|
-
| \`canvas\` |
|
|
4628
|
-
| \`card\` |
|
|
4629
|
-
| \`carousel\` |
|
|
4630
|
-
| \`chat\` |
|
|
4631
|
-
| \`checkbox\` |
|
|
4632
|
-
| \`checkbox-group\` |
|
|
4633
|
-
| \`code\` | 20,
|
|
4634
|
-
| \`code-block\` |
|
|
4635
|
-
| \`color-input\` |
|
|
4636
|
-
| \`color-picker\` |
|
|
4637
|
-
| \`combobox\` |
|
|
4638
|
-
| \`command\` |
|
|
4639
|
-
| \`copy-button\` |
|
|
4640
|
-
| \`data-table\` |
|
|
4641
|
-
| \`date-input\` |
|
|
4642
|
-
| \`date-picker\` |
|
|
4643
|
-
| \`description-list\` |
|
|
4644
|
-
| \`dialog\` |
|
|
4645
|
-
| \`divider\` |
|
|
4646
|
-
| \`donut-chart\` |
|
|
4647
|
-
| \`drawer\` |
|
|
4648
|
-
| \`drilldown\` |
|
|
4649
|
-
| \`empty\` |
|
|
4650
|
-
| \`file-input\` |
|
|
4651
|
-
| \`filter-input\` |
|
|
4652
|
-
| \`format-date\` |
|
|
4653
|
-
| \`graph\` |
|
|
4654
|
-
| \`heatmap\` |
|
|
4655
|
-
| \`icon\` |
|
|
4656
|
-
| \`icon-button\` |
|
|
4657
|
-
| \`image\` |
|
|
4658
|
-
| \`input\` |
|
|
4659
|
-
| \`kbd\` |
|
|
4660
|
-
| \`legend\` |
|
|
4661
|
-
| \`lightbox\` |
|
|
4662
|
-
| \`line-chart\` |
|
|
4663
|
-
| \`list\` |
|
|
4664
|
-
| \`listbox\` |
|
|
4665
|
-
| \`loader\` |
|
|
4666
|
-
| \`log\` |
|
|
4667
|
-
| \`map\` |
|
|
4668
|
-
| \`megamenu\` |
|
|
4669
|
-
| \`menu\` |
|
|
4670
|
-
| \`meter\` |
|
|
4671
|
-
| \`motion-toggle\` |
|
|
4672
|
-
| \`navigation\` | 33,
|
|
4673
|
-
| \`notifications\` |
|
|
4674
|
-
| \`number-input\` |
|
|
4675
|
-
| \`object-inspector\` |
|
|
4676
|
-
| \`option\` |
|
|
4677
|
-
| \`outline\` |
|
|
4678
|
-
| \`pagination\` |
|
|
4679
|
-
| \`pin-input\` |
|
|
4680
|
-
| \`popover\` |
|
|
4681
|
-
| \`progress\` |
|
|
4682
|
-
| \`qr-code\` |
|
|
4683
|
-
| \`quote\` |
|
|
4684
|
-
| \`radio\` |
|
|
4685
|
-
| \`rating\` |
|
|
4686
|
-
| \`scatter-chart\` |
|
|
4687
|
-
| \`scroll-area\` |
|
|
4688
|
-
| \`select\` |
|
|
4689
|
-
| \`skeleton\` | 22,
|
|
4690
|
-
| \`skip-link\` |
|
|
4691
|
-
| \`slider\` |
|
|
4692
|
-
| \`sparkline\` |
|
|
4693
|
-
| \`spatial-bar-chart\` |
|
|
4694
|
-
| \`spatial-heatmap\` |
|
|
4695
|
-
| \`spatial-ribbon-chart\` |
|
|
4696
|
-
| \`spatial-scatter-chart\` |
|
|
4697
|
-
| \`spatial-surface-chart\` |
|
|
4698
|
-
| \`spatial-treemap\` |
|
|
4699
|
-
| \`speed-dial\` |
|
|
4700
|
-
| \`splitter\` |
|
|
4701
|
-
| \`stat\` |
|
|
4702
|
-
| \`steps\` |
|
|
4703
|
-
| \`switch\` |
|
|
4704
|
-
| \`table\` |
|
|
4705
|
-
| \`tabs\` |
|
|
4706
|
-
| \`tag\` |
|
|
4707
|
-
| \`textarea\` |
|
|
4708
|
-
| \`time-input\` |
|
|
4709
|
-
| \`time-picker\` |
|
|
4710
|
-
| \`timeline\` |
|
|
4711
|
-
| \`toast\` |
|
|
4712
|
-
| \`toggle\` |
|
|
4713
|
-
| \`toolbar\` | 31,
|
|
4714
|
-
| \`tooltip\` |
|
|
4715
|
-
| \`tour\` |
|
|
4716
|
-
| \`tree\` |
|
|
4717
|
-
| \`trend\` |
|
|
4718
|
-
| \`video\` |
|
|
4719
|
-
| \`video-playlist\` |
|
|
4720
|
-
| \`virtual-list\` |
|
|
5257
|
+
| \`accordion\` | 32,671 | 12,095 | 10,738 | 5,377 | 1,558 | 1,115 |
|
|
5258
|
+
| \`announcement\` | 37,661 | 13,707 | 12,236 | 4,700 | 1,380 | 963 |
|
|
5259
|
+
| \`app-header\` | 33,168 | 12,242 | 10,882 | 5,900 | 1,760 | 1,235 |
|
|
5260
|
+
| \`area-chart\` | 99,355 | 34,966 | 30,900 | 14,417 | 4,436 | 3,601 |
|
|
5261
|
+
| \`avatar\` | 31,887 | 12,153 | 10,896 | 3,999 | 1,246 | 921 |
|
|
5262
|
+
| \`badge\` | 28,118 | 10,943 | 9,747 | 2,960 | 887 | 624 |
|
|
5263
|
+
| \`bar-chart\` | 107,369 | 37,644 | 33,280 | 21,901 | 6,878 | 5,646 |
|
|
5264
|
+
| \`blockquote\` | 24,679 | 9,638 | 8,549 | 2,400 | 883 | 717 |
|
|
5265
|
+
| \`breadcrumbs\` | 37,419 | 14,329 | 12,824 | 7,381 | 2,413 | 1,888 |
|
|
5266
|
+
| \`button\` | 36,221 | 12,969 | 11,566 | 5,127 | 1,522 | 1,037 |
|
|
5267
|
+
| \`button-group\` | 28,214 | 10,616 | 9,418 | 4,923 | 1,508 | 1,105 |
|
|
5268
|
+
| \`callout\` | 36,284 | 13,988 | 12,430 | 2,884 | 960 | 693 |
|
|
5269
|
+
| \`canvas\` | 109,962 | 35,313 | 30,855 | 68,989 | 21,024 | 17,645 |
|
|
5270
|
+
| \`card\` | 24,397 | 9,311 | 8,254 | 2,090 | 634 | 427 |
|
|
5271
|
+
| \`carousel\` | 45,040 | 16,012 | 14,309 | 15,317 | 4,235 | 3,274 |
|
|
5272
|
+
| \`chat\` | 80,951 | 26,986 | 23,897 | 27,678 | 8,076 | 6,375 |
|
|
5273
|
+
| \`checkbox\` | 41,242 | 14,016 | 12,507 | 3,107 | 959 | 567 |
|
|
5274
|
+
| \`checkbox-group\` | 48,465 | 15,944 | 14,217 | 8,321 | 2,373 | 1,588 |
|
|
5275
|
+
| \`code\` | 20,869 | 8,248 | 7,311 | 138 | 67 | 25 |
|
|
5276
|
+
| \`code-block\` | 104,774 | 35,631 | 31,442 | 37,349 | 10,789 | 8,834 |
|
|
5277
|
+
| \`color-input\` | 143,744 | 45,826 | 39,959 | 55,780 | 14,227 | 11,731 |
|
|
5278
|
+
| \`color-picker\` | 105,363 | 33,633 | 29,440 | 47,900 | 12,700 | 10,319 |
|
|
5279
|
+
| \`combobox\` | 133,766 | 42,655 | 37,506 | 35,968 | 9,135 | 7,215 |
|
|
5280
|
+
| \`command\` | 101,170 | 32,143 | 28,267 | 30,351 | 8,182 | 6,545 |
|
|
5281
|
+
| \`copy-button\` | 82,225 | 28,294 | 25,019 | 18,756 | 5,070 | 4,024 |
|
|
5282
|
+
| \`data-table\` | 201,919 | 62,149 | 53,145 | 89,974 | 24,051 | 19,267 |
|
|
5283
|
+
| \`date-input\` | 143,049 | 45,174 | 39,697 | 38,666 | 9,515 | 7,551 |
|
|
5284
|
+
| \`date-picker\` | 63,093 | 21,512 | 19,282 | 20,720 | 5,656 | 4,439 |
|
|
5285
|
+
| \`description-list\` | 23,472 | 8,993 | 7,999 | 1,565 | 498 | 254 |
|
|
5286
|
+
| \`dialog\` | 39,175 | 13,972 | 12,446 | 5,948 | 1,764 | 1,243 |
|
|
5287
|
+
| \`divider\` | 23,564 | 9,144 | 8,085 | 1,414 | 495 | 355 |
|
|
5288
|
+
| \`donut-chart\` | 92,673 | 32,268 | 28,539 | 14,717 | 4,498 | 3,641 |
|
|
5289
|
+
| \`drawer\` | 43,812 | 15,483 | 13,793 | 7,798 | 2,316 | 1,866 |
|
|
5290
|
+
| \`drilldown\` | 43,527 | 15,249 | 13,622 | 9,709 | 2,684 | 2,052 |
|
|
5291
|
+
| \`empty\` | 25,007 | 9,579 | 8,498 | 1,976 | 646 | 323 |
|
|
5292
|
+
| \`file-input\` | 59,118 | 20,550 | 18,268 | 13,152 | 4,118 | 3,191 |
|
|
5293
|
+
| \`filter-input\` | 99,578 | 33,493 | 29,738 | 21,569 | 6,178 | 4,972 |
|
|
5294
|
+
| \`format-date\` | 31,209 | 11,674 | 10,418 | 4,474 | 1,290 | 951 |
|
|
5295
|
+
| \`graph\` | 76,986 | 25,236 | 22,347 | 23,180 | 6,769 | 5,429 |
|
|
5296
|
+
| \`heatmap\` | 101,735 | 34,694 | 30,635 | 19,071 | 5,600 | 4,551 |
|
|
5297
|
+
| \`icon\` | 83,516 | 28,589 | 24,445 | 58,618 | 20,085 | 16,045 |
|
|
5298
|
+
| \`icon-button\` | 36,332 | 13,001 | 11,592 | 5,236 | 1,561 | 1,120 |
|
|
5299
|
+
| \`image\` | 26,373 | 9,843 | 8,745 | 3,845 | 1,148 | 815 |
|
|
5300
|
+
| \`input\` | 47,049 | 16,178 | 14,347 | 7,005 | 2,242 | 1,478 |
|
|
5301
|
+
| \`kbd\` | 28,084 | 10,789 | 9,553 | 3,518 | 1,104 | 774 |
|
|
5302
|
+
| \`legend\` | 36,396 | 13,369 | 11,949 | 2,955 | 984 | 682 |
|
|
5303
|
+
| \`lightbox\` | 57,138 | 19,382 | 17,237 | 16,760 | 4,614 | 3,463 |
|
|
5304
|
+
| \`line-chart\` | 102,350 | 35,823 | 31,666 | 16,812 | 5,062 | 4,206 |
|
|
5305
|
+
| \`list\` | 62,949 | 21,723 | 19,337 | 15,258 | 4,394 | 3,364 |
|
|
5306
|
+
| \`listbox\` | 78,070 | 25,947 | 22,965 | 15,605 | 4,579 | 3,395 |
|
|
5307
|
+
| \`loader\` | 27,341 | 10,451 | 9,310 | 2,785 | 940 | 677 |
|
|
5308
|
+
| \`log\` | 80,937 | 27,698 | 24,593 | 37,582 | 11,597 | 9,825 |
|
|
5309
|
+
| \`map\` | 81,046 | 25,212 | 22,053 | 37,496 | 10,232 | 8,090 |
|
|
5310
|
+
| \`megamenu\` | 61,165 | 22,169 | 19,586 | 8,723 | 2,442 | 1,704 |
|
|
5311
|
+
| \`menu\` | 98,510 | 32,959 | 29,021 | 30,262 | 7,971 | 6,316 |
|
|
5312
|
+
| \`meter\` | 32,921 | 12,484 | 11,131 | 4,980 | 1,464 | 1,175 |
|
|
5313
|
+
| \`motion-toggle\` | 26,353 | 10,250 | 9,108 | 1,426 | 514 | 316 |
|
|
5314
|
+
| \`navigation\` | 33,861 | 12,731 | 11,315 | 3,364 | 1,161 | 888 |
|
|
5315
|
+
| \`notifications\` | 59,539 | 20,332 | 18,148 | 18,280 | 5,038 | 4,017 |
|
|
5316
|
+
| \`number-input\` | 57,629 | 19,358 | 17,261 | 13,579 | 3,609 | 2,827 |
|
|
5317
|
+
| \`object-inspector\` | 115,897 | 39,008 | 34,310 | 36,018 | 10,397 | 8,445 |
|
|
5318
|
+
| \`option\` | 27,049 | 9,970 | 8,881 | 4,522 | 1,202 | 896 |
|
|
5319
|
+
| \`outline\` | 34,490 | 12,937 | 11,488 | 7,878 | 2,548 | 1,797 |
|
|
5320
|
+
| \`pagination\` | 138,030 | 44,305 | 38,517 | 45,681 | 11,904 | 9,410 |
|
|
5321
|
+
| \`pin-input\` | 48,848 | 17,044 | 15,220 | 7,207 | 2,364 | 1,697 |
|
|
5322
|
+
| \`popover\` | 64,882 | 23,350 | 20,625 | 6,460 | 1,852 | 1,283 |
|
|
5323
|
+
| \`progress\` | 29,235 | 11,153 | 9,959 | 3,006 | 943 | 696 |
|
|
5324
|
+
| \`qr-code\` | 43,669 | 17,003 | 15,113 | 17,002 | 6,288 | 5,291 |
|
|
5325
|
+
| \`quote\` | 23,088 | 9,112 | 8,040 | 1,209 | 499 | 432 |
|
|
5326
|
+
| \`radio\` | 43,333 | 14,777 | 13,152 | 3,815 | 1,205 | 816 |
|
|
5327
|
+
| \`rating\` | 51,593 | 17,779 | 15,883 | 8,057 | 2,440 | 1,706 |
|
|
5328
|
+
| \`scatter-chart\` | 98,654 | 34,303 | 30,247 | 20,338 | 6,374 | 5,276 |
|
|
5329
|
+
| \`scroll-area\` | 31,294 | 11,433 | 10,150 | 8,518 | 2,583 | 1,963 |
|
|
5330
|
+
| \`select\` | 116,719 | 38,717 | 33,960 | 26,155 | 7,165 | 5,494 |
|
|
5331
|
+
| \`skeleton\` | 22,900 | 8,871 | 7,891 | 991 | 377 | 277 |
|
|
5332
|
+
| \`skip-link\` | 24,547 | 9,607 | 8,536 | 1,223 | 456 | 336 |
|
|
5333
|
+
| \`slider\` | 54,642 | 18,411 | 16,505 | 13,623 | 3,851 | 2,958 |
|
|
5334
|
+
| \`sparkline\` | 49,258 | 17,982 | 16,072 | 5,982 | 2,086 | 1,613 |
|
|
5335
|
+
| \`spatial-bar-chart\` | 105,167 | 37,122 | 32,643 | 19,854 | 6,518 | 5,233 |
|
|
5336
|
+
| \`spatial-heatmap\` | 117,089 | 39,972 | 35,153 | 31,736 | 9,641 | 7,867 |
|
|
5337
|
+
| \`spatial-ribbon-chart\` | 104,288 | 36,728 | 32,391 | 20,791 | 6,921 | 5,582 |
|
|
5338
|
+
| \`spatial-scatter-chart\` | 102,521 | 35,980 | 31,785 | 19,085 | 6,281 | 5,062 |
|
|
5339
|
+
| \`spatial-surface-chart\` | 105,443 | 37,265 | 32,895 | 21,949 | 7,465 | 6,084 |
|
|
5340
|
+
| \`spatial-treemap\` | 106,561 | 37,910 | 33,352 | 21,370 | 7,346 | 5,941 |
|
|
5341
|
+
| \`speed-dial\` | 69,618 | 25,068 | 22,217 | 10,934 | 2,964 | 2,314 |
|
|
5342
|
+
| \`splitter\` | 45,071 | 15,638 | 13,995 | 14,506 | 3,884 | 3,124 |
|
|
5343
|
+
| \`stat\` | 26,285 | 10,056 | 8,958 | 2,322 | 765 | 519 |
|
|
5344
|
+
| \`steps\` | 54,926 | 19,123 | 17,061 | 13,804 | 3,887 | 3,073 |
|
|
5345
|
+
| \`switch\` | 40,096 | 13,832 | 12,335 | 1,679 | 618 | 506 |
|
|
5346
|
+
| \`table\` | 119,408 | 37,410 | 32,491 | 45,149 | 12,317 | 9,944 |
|
|
5347
|
+
| \`tabs\` | 47,080 | 16,201 | 14,421 | 10,491 | 2,847 | 2,118 |
|
|
5348
|
+
| \`tag\` | 43,623 | 15,994 | 14,277 | 8,450 | 2,507 | 1,811 |
|
|
5349
|
+
| \`textarea\` | 44,973 | 15,361 | 13,745 | 5,489 | 1,613 | 1,183 |
|
|
5350
|
+
| \`time-input\` | 170,882 | 53,314 | 46,321 | 48,574 | 12,340 | 9,937 |
|
|
5351
|
+
| \`time-picker\` | 101,126 | 32,099 | 28,322 | 33,987 | 9,197 | 7,261 |
|
|
5352
|
+
| \`timeline\` | 32,429 | 12,281 | 11,016 | 4,086 | 1,199 | 863 |
|
|
5353
|
+
| \`toast\` | 52,298 | 18,497 | 16,512 | 12,164 | 3,516 | 2,684 |
|
|
5354
|
+
| \`toggle\` | 46,246 | 15,540 | 13,826 | 6,005 | 1,800 | 1,291 |
|
|
5355
|
+
| \`toolbar\` | 31,817 | 11,859 | 10,565 | 5,156 | 1,626 | 1,170 |
|
|
5356
|
+
| \`tooltip\` | 59,698 | 21,530 | 19,098 | 8,504 | 2,352 | 1,775 |
|
|
5357
|
+
| \`tour\` | 85,264 | 29,314 | 25,917 | 20,091 | 5,517 | 4,414 |
|
|
5358
|
+
| \`tree\` | 68,641 | 23,150 | 20,567 | 17,198 | 4,915 | 3,955 |
|
|
5359
|
+
| \`trend\` | 30,937 | 12,024 | 10,726 | 3,590 | 1,102 | 800 |
|
|
5360
|
+
| \`video\` | 143,863 | 45,087 | 39,321 | 66,787 | 16,894 | 13,884 |
|
|
5361
|
+
| \`video-playlist\` | 128,994 | 41,833 | 36,771 | 42,860 | 11,206 | 9,239 |
|
|
5362
|
+
| \`virtual-list\` | 64,382 | 21,913 | 19,494 | 23,393 | 7,169 | 5,818 |
|
|
4721
5363
|
|
|
4722
5364
|
### Styled tier
|
|
4723
5365
|
|
|
@@ -4725,119 +5367,119 @@ The headless tier plus presentation. \`bun add @fusion.dev/helix\` installs this
|
|
|
4725
5367
|
|
|
4726
5368
|
| Family | Alone raw | Alone gzip | Alone brotli | Added raw | Added gzip | Added brotli |
|
|
4727
5369
|
|---|---|---|---|---|---|---|
|
|
4728
|
-
| \`accordion\` |
|
|
4729
|
-
| \`announcement\` |
|
|
4730
|
-
| \`app-header\` |
|
|
4731
|
-
| \`area-chart\` |
|
|
4732
|
-
| \`aura\` |
|
|
4733
|
-
| \`avatar\` |
|
|
4734
|
-
| \`badge\` |
|
|
4735
|
-
| \`bar-chart\` |
|
|
4736
|
-
| \`blockquote\` | 26,
|
|
4737
|
-
| \`breadcrumbs\` |
|
|
4738
|
-
| \`button\` |
|
|
4739
|
-
| \`button-group\` | 31,
|
|
4740
|
-
| \`callout\` |
|
|
4741
|
-
| \`canvas\` |
|
|
4742
|
-
| \`card\` |
|
|
4743
|
-
| \`carousel\` |
|
|
4744
|
-
| \`chat\` |
|
|
4745
|
-
| \`checkbox\` |
|
|
4746
|
-
| \`checkbox-group\` |
|
|
4747
|
-
| \`code\` | 21,
|
|
4748
|
-
| \`code-block\` |
|
|
4749
|
-
| \`color-input\` |
|
|
4750
|
-
| \`color-picker\` |
|
|
4751
|
-
| \`combobox\` |
|
|
4752
|
-
| \`command\` |
|
|
4753
|
-
| \`copy-button\` |
|
|
4754
|
-
| \`data-table\` |
|
|
4755
|
-
| \`date-input\` |
|
|
4756
|
-
| \`date-picker\` |
|
|
4757
|
-
| \`description-list\` |
|
|
4758
|
-
| \`dialog\` |
|
|
4759
|
-
| \`divider\` | 25,
|
|
4760
|
-
| \`donut-chart\` |
|
|
4761
|
-
| \`drawer\` |
|
|
4762
|
-
| \`drilldown\` |
|
|
4763
|
-
| \`empty\` |
|
|
4764
|
-
| \`file-input\` |
|
|
4765
|
-
| \`filter-input\` |
|
|
4766
|
-
| \`flex\` |
|
|
4767
|
-
| \`format-date\` |
|
|
4768
|
-
| \`graph\` |
|
|
4769
|
-
| \`grid\` | 27,
|
|
4770
|
-
| \`heatmap\` |
|
|
4771
|
-
| \`icon\` |
|
|
4772
|
-
| \`icon-button\` |
|
|
4773
|
-
| \`image\` |
|
|
4774
|
-
| \`input\` |
|
|
4775
|
-
| \`kbd\` |
|
|
4776
|
-
| \`legend\` |
|
|
4777
|
-
| \`lightbox\` |
|
|
4778
|
-
| \`line-chart\` |
|
|
4779
|
-
| \`list\` |
|
|
4780
|
-
| \`listbox\` |
|
|
4781
|
-
| \`loader\` |
|
|
4782
|
-
| \`log\` |
|
|
4783
|
-
| \`map\` |
|
|
4784
|
-
| \`megamenu\` |
|
|
4785
|
-
| \`menu\` |
|
|
4786
|
-
| \`meter\` |
|
|
4787
|
-
| \`motion-toggle\` | 27,
|
|
4788
|
-
| \`navigation\` |
|
|
4789
|
-
| \`notifications\` |
|
|
4790
|
-
| \`number-input\` |
|
|
4791
|
-
| \`object-inspector\` |
|
|
4792
|
-
| \`option\` |
|
|
4793
|
-
| \`orb\` | 36,
|
|
4794
|
-
| \`outline\` |
|
|
4795
|
-
| \`pagination\` |
|
|
4796
|
-
| \`pin-input\` |
|
|
4797
|
-
| \`popover\` |
|
|
4798
|
-
| \`progress\` |
|
|
4799
|
-
| \`qr-code\` |
|
|
4800
|
-
| \`quote\` | 23,
|
|
4801
|
-
| \`radio\` |
|
|
4802
|
-
| \`rating\` |
|
|
4803
|
-
| \`sandbox\` |
|
|
4804
|
-
| \`scatter-chart\` |
|
|
4805
|
-
| \`scroll-area\` |
|
|
4806
|
-
| \`select\` |
|
|
4807
|
-
| \`skeleton\` | 26,
|
|
4808
|
-
| \`skip-link\` |
|
|
4809
|
-
| \`slider\` |
|
|
4810
|
-
| \`sparkline\` |
|
|
4811
|
-
| \`spatial-bar-chart\` |
|
|
4812
|
-
| \`spatial-heatmap\` |
|
|
4813
|
-
| \`spatial-ribbon-chart\` |
|
|
4814
|
-
| \`spatial-scatter-chart\` |
|
|
4815
|
-
| \`spatial-surface-chart\` |
|
|
4816
|
-
| \`spatial-treemap\` |
|
|
4817
|
-
| \`speed-dial\` |
|
|
4818
|
-
| \`splitter\` |
|
|
4819
|
-
| \`stat\` |
|
|
4820
|
-
| \`steps\` |
|
|
4821
|
-
| \`strands\` |
|
|
4822
|
-
| \`switch\` |
|
|
4823
|
-
| \`table\` |
|
|
4824
|
-
| \`tabs\` |
|
|
4825
|
-
| \`tag\` |
|
|
4826
|
-
| \`text\` |
|
|
4827
|
-
| \`textarea\` |
|
|
4828
|
-
| \`time-input\` |
|
|
4829
|
-
| \`time-picker\` |
|
|
4830
|
-
| \`timeline\` |
|
|
4831
|
-
| \`toast\` |
|
|
4832
|
-
| \`toggle\` |
|
|
4833
|
-
| \`toolbar\` |
|
|
4834
|
-
| \`tooltip\` |
|
|
4835
|
-
| \`tour\` |
|
|
4836
|
-
| \`tree\` |
|
|
4837
|
-
| \`trend\` |
|
|
4838
|
-
| \`video\` |
|
|
4839
|
-
| \`video-playlist\` |
|
|
4840
|
-
| \`virtual-list\` |
|
|
5370
|
+
| \`accordion\` | 36,104 | 12,942 | 11,549 | 8,794 | 2,432 | 1,899 |
|
|
5371
|
+
| \`announcement\` | 42,276 | 14,775 | 13,228 | 9,296 | 2,461 | 1,848 |
|
|
5372
|
+
| \`app-header\` | 38,326 | 13,450 | 11,973 | 11,045 | 2,971 | 2,442 |
|
|
5373
|
+
| \`area-chart\` | 107,559 | 36,865 | 32,515 | 22,602 | 6,333 | 5,144 |
|
|
5374
|
+
| \`aura\` | 59,567 | 20,254 | 17,941 | 31,767 | 9,666 | 8,316 |
|
|
5375
|
+
| \`avatar\` | 34,890 | 12,911 | 11,530 | 6,987 | 1,978 | 1,656 |
|
|
5376
|
+
| \`badge\` | 33,626 | 12,189 | 10,865 | 8,458 | 2,158 | 1,747 |
|
|
5377
|
+
| \`bar-chart\` | 122,404 | 40,670 | 35,940 | 36,918 | 9,892 | 8,184 |
|
|
5378
|
+
| \`blockquote\` | 26,905 | 10,186 | 9,009 | 4,610 | 1,426 | 1,083 |
|
|
5379
|
+
| \`breadcrumbs\` | 44,356 | 15,535 | 13,899 | 14,306 | 3,646 | 3,017 |
|
|
5380
|
+
| \`button\` | 42,638 | 14,385 | 12,821 | 11,526 | 2,919 | 2,286 |
|
|
5381
|
+
| \`button-group\` | 31,709 | 11,294 | 10,028 | 8,408 | 2,202 | 1,655 |
|
|
5382
|
+
| \`callout\` | 40,077 | 14,932 | 13,285 | 6,659 | 1,926 | 1,486 |
|
|
5383
|
+
| \`canvas\` | 131,001 | 39,286 | 34,369 | 89,671 | 24,923 | 20,892 |
|
|
5384
|
+
| \`card\` | 28,297 | 10,317 | 9,175 | 5,976 | 1,633 | 1,226 |
|
|
5385
|
+
| \`carousel\` | 55,526 | 17,813 | 15,963 | 25,788 | 6,085 | 4,831 |
|
|
5386
|
+
| \`chat\` | 96,367 | 29,721 | 26,295 | 43,087 | 10,858 | 8,853 |
|
|
5387
|
+
| \`checkbox\` | 47,728 | 15,518 | 13,836 | 9,579 | 2,487 | 1,908 |
|
|
5388
|
+
| \`checkbox-group\` | 55,688 | 17,587 | 15,651 | 15,535 | 4,008 | 3,086 |
|
|
5389
|
+
| \`code\` | 21,726 | 8,560 | 7,606 | 976 | 377 | 382 |
|
|
5390
|
+
| \`code-block\` | 129,247 | 39,457 | 34,785 | 61,803 | 14,528 | 12,279 |
|
|
5391
|
+
| \`color-input\` | 186,124 | 52,091 | 45,562 | 98,147 | 20,720 | 17,291 |
|
|
5392
|
+
| \`color-picker\` | 142,298 | 39,184 | 34,386 | 84,819 | 18,371 | 15,252 |
|
|
5393
|
+
| \`combobox\` | 149,243 | 45,632 | 40,195 | 51,428 | 12,037 | 9,738 |
|
|
5394
|
+
| \`command\` | 112,469 | 34,490 | 30,368 | 41,644 | 10,582 | 8,652 |
|
|
5395
|
+
| \`copy-button\` | 94,256 | 30,466 | 26,980 | 30,769 | 7,291 | 5,878 |
|
|
5396
|
+
| \`data-table\` | 232,707 | 67,583 | 57,864 | 120,745 | 29,581 | 23,977 |
|
|
5397
|
+
| \`date-input\` | 164,353 | 48,935 | 43,089 | 59,950 | 13,125 | 10,875 |
|
|
5398
|
+
| \`date-picker\` | 71,139 | 22,979 | 20,542 | 28,748 | 7,139 | 5,654 |
|
|
5399
|
+
| \`description-list\` | 26,427 | 9,731 | 8,674 | 4,508 | 1,275 | 877 |
|
|
5400
|
+
| \`dialog\` | 44,311 | 15,226 | 13,612 | 11,064 | 3,040 | 2,320 |
|
|
5401
|
+
| \`divider\` | 25,863 | 9,771 | 8,637 | 3,697 | 1,132 | 815 |
|
|
5402
|
+
| \`donut-chart\` | 102,652 | 34,418 | 30,426 | 24,673 | 6,687 | 5,572 |
|
|
5403
|
+
| \`drawer\` | 49,642 | 16,871 | 15,090 | 13,612 | 3,747 | 3,025 |
|
|
5404
|
+
| \`drilldown\` | 49,204 | 16,541 | 14,765 | 15,375 | 4,004 | 3,098 |
|
|
5405
|
+
| \`empty\` | 26,316 | 10,055 | 8,906 | 3,267 | 1,090 | 883 |
|
|
5406
|
+
| \`file-input\` | 70,648 | 22,941 | 20,369 | 24,668 | 6,540 | 5,360 |
|
|
5407
|
+
| \`filter-input\` | 110,412 | 35,679 | 31,644 | 32,396 | 8,372 | 6,797 |
|
|
5408
|
+
| \`flex\` | 26,312 | 9,424 | 8,375 | 4,761 | 1,041 | 832 |
|
|
5409
|
+
| \`format-date\` | 31,463 | 11,789 | 10,532 | 4,712 | 1,389 | 950 |
|
|
5410
|
+
| \`graph\` | 92,941 | 27,948 | 24,622 | 39,115 | 9,599 | 7,810 |
|
|
5411
|
+
| \`grid\` | 27,762 | 10,137 | 8,994 | 5,655 | 1,457 | 1,188 |
|
|
5412
|
+
| \`heatmap\` | 110,057 | 36,548 | 32,263 | 27,373 | 7,430 | 6,014 |
|
|
5413
|
+
| \`icon\` | 85,551 | 29,194 | 25,031 | 60,473 | 20,643 | 16,501 |
|
|
5414
|
+
| \`icon-button\` | 44,066 | 14,350 | 12,783 | 12,955 | 2,917 | 2,251 |
|
|
5415
|
+
| \`image\` | 27,760 | 10,306 | 9,129 | 5,216 | 1,623 | 1,202 |
|
|
5416
|
+
| \`input\` | 51,304 | 17,264 | 15,273 | 11,243 | 3,350 | 2,496 |
|
|
5417
|
+
| \`kbd\` | 30,078 | 11,329 | 10,047 | 5,494 | 1,656 | 1,218 |
|
|
5418
|
+
| \`legend\` | 41,634 | 14,661 | 13,051 | 8,065 | 2,233 | 1,665 |
|
|
5419
|
+
| \`lightbox\` | 63,432 | 20,712 | 18,433 | 23,035 | 5,934 | 4,680 |
|
|
5420
|
+
| \`line-chart\` | 113,713 | 38,278 | 33,804 | 28,159 | 7,540 | 6,233 |
|
|
5421
|
+
| \`list\` | 69,295 | 23,266 | 20,712 | 21,585 | 5,916 | 4,764 |
|
|
5422
|
+
| \`listbox\` | 82,511 | 27,061 | 23,895 | 20,034 | 5,683 | 4,322 |
|
|
5423
|
+
| \`loader\` | 30,222 | 11,214 | 10,030 | 5,652 | 1,707 | 1,399 |
|
|
5424
|
+
| \`log\` | 92,605 | 29,988 | 26,578 | 49,238 | 13,837 | 11,768 |
|
|
5425
|
+
| \`map\` | 114,692 | 30,616 | 26,855 | 71,126 | 15,703 | 12,901 |
|
|
5426
|
+
| \`megamenu\` | 66,134 | 23,308 | 20,640 | 13,673 | 3,614 | 2,780 |
|
|
5427
|
+
| \`menu\` | 109,435 | 35,105 | 30,901 | 41,177 | 10,171 | 8,292 |
|
|
5428
|
+
| \`meter\` | 39,093 | 13,830 | 12,394 | 11,136 | 2,821 | 2,333 |
|
|
5429
|
+
| \`motion-toggle\` | 27,988 | 10,726 | 9,535 | 3,044 | 996 | 805 |
|
|
5430
|
+
| \`navigation\` | 37,585 | 13,566 | 12,044 | 7,077 | 2,005 | 1,539 |
|
|
5431
|
+
| \`notifications\` | 73,027 | 22,893 | 20,441 | 31,757 | 7,639 | 6,268 |
|
|
5432
|
+
| \`number-input\` | 62,610 | 20,544 | 18,288 | 18,544 | 4,834 | 3,694 |
|
|
5433
|
+
| \`object-inspector\` | 132,306 | 41,847 | 36,873 | 52,407 | 13,126 | 10,719 |
|
|
5434
|
+
| \`option\` | 30,245 | 10,972 | 9,759 | 7,708 | 2,180 | 1,723 |
|
|
5435
|
+
| \`orb\` | 36,885 | 14,019 | 12,487 | 10,014 | 3,695 | 3,083 |
|
|
5436
|
+
| \`outline\` | 38,722 | 13,792 | 12,269 | 12,095 | 3,423 | 2,644 |
|
|
5437
|
+
| \`pagination\` | 155,030 | 47,597 | 41,410 | 62,664 | 15,116 | 12,369 |
|
|
5438
|
+
| \`pin-input\` | 52,228 | 17,955 | 16,008 | 10,570 | 3,313 | 2,507 |
|
|
5439
|
+
| \`popover\` | 68,034 | 24,117 | 21,308 | 9,594 | 2,638 | 1,983 |
|
|
5440
|
+
| \`progress\` | 36,415 | 12,815 | 11,466 | 10,169 | 2,605 | 2,076 |
|
|
5441
|
+
| \`qr-code\` | 45,270 | 17,490 | 15,576 | 18,586 | 6,787 | 5,767 |
|
|
5442
|
+
| \`quote\` | 23,646 | 9,303 | 8,218 | 1,750 | 694 | 466 |
|
|
5443
|
+
| \`radio\` | 51,120 | 16,282 | 14,511 | 11,591 | 2,749 | 2,105 |
|
|
5444
|
+
| \`rating\` | 55,527 | 19,005 | 16,948 | 11,973 | 3,667 | 2,928 |
|
|
5445
|
+
| \`sandbox\` | 108,218 | 32,974 | 28,890 | 74,725 | 20,351 | 17,034 |
|
|
5446
|
+
| \`scatter-chart\` | 108,028 | 36,473 | 32,145 | 29,693 | 8,586 | 6,916 |
|
|
5447
|
+
| \`scroll-area\` | 36,301 | 12,383 | 11,025 | 13,508 | 3,564 | 2,884 |
|
|
5448
|
+
| \`select\` | 125,846 | 40,631 | 35,659 | 35,267 | 9,129 | 7,098 |
|
|
5449
|
+
| \`skeleton\` | 26,848 | 9,947 | 8,858 | 4,755 | 1,398 | 1,117 |
|
|
5450
|
+
| \`skip-link\` | 26,118 | 10,114 | 8,973 | 2,778 | 966 | 785 |
|
|
5451
|
+
| \`slider\` | 69,605 | 20,728 | 18,556 | 28,572 | 6,253 | 4,988 |
|
|
5452
|
+
| \`sparkline\` | 54,716 | 19,331 | 17,256 | 11,422 | 3,445 | 2,832 |
|
|
5453
|
+
| \`spatial-bar-chart\` | 114,232 | 39,173 | 34,497 | 28,899 | 8,556 | 6,886 |
|
|
5454
|
+
| \`spatial-heatmap\` | 126,828 | 42,153 | 36,998 | 41,457 | 11,702 | 9,766 |
|
|
5455
|
+
| \`spatial-ribbon-chart\` | 113,204 | 38,749 | 34,162 | 29,690 | 8,956 | 7,374 |
|
|
5456
|
+
| \`spatial-scatter-chart\` | 111,501 | 37,966 | 33,461 | 28,039 | 8,231 | 6,662 |
|
|
5457
|
+
| \`spatial-surface-chart\` | 113,878 | 39,172 | 34,539 | 30,366 | 9,349 | 7,842 |
|
|
5458
|
+
| \`spatial-treemap\` | 115,578 | 39,956 | 35,130 | 30,367 | 9,373 | 7,723 |
|
|
5459
|
+
| \`speed-dial\` | 76,667 | 26,451 | 23,434 | 17,966 | 4,376 | 3,533 |
|
|
5460
|
+
| \`splitter\` | 51,121 | 16,689 | 14,971 | 20,542 | 4,916 | 4,082 |
|
|
5461
|
+
| \`stat\` | 29,171 | 10,751 | 9,607 | 5,193 | 1,467 | 1,194 |
|
|
5462
|
+
| \`steps\` | 68,915 | 21,887 | 19,477 | 27,781 | 6,739 | 5,662 |
|
|
5463
|
+
| \`strands\` | 63,680 | 23,697 | 20,891 | 38,177 | 13,705 | 11,852 |
|
|
5464
|
+
| \`switch\` | 44,449 | 14,978 | 13,325 | 6,021 | 1,773 | 1,338 |
|
|
5465
|
+
| \`table\` | 136,948 | 40,477 | 35,271 | 62,692 | 15,338 | 12,668 |
|
|
5466
|
+
| \`tabs\` | 56,031 | 18,047 | 16,065 | 19,434 | 4,733 | 3,847 |
|
|
5467
|
+
| \`tag\` | 48,197 | 17,126 | 15,315 | 13,011 | 3,621 | 2,857 |
|
|
5468
|
+
| \`text\` | 29,286 | 10,212 | 9,014 | 7,182 | 1,494 | 1,095 |
|
|
5469
|
+
| \`textarea\` | 48,905 | 16,377 | 14,632 | 9,403 | 2,654 | 2,098 |
|
|
5470
|
+
| \`time-input\` | 195,260 | 57,479 | 50,074 | 72,937 | 16,508 | 13,588 |
|
|
5471
|
+
| \`time-picker\` | 113,448 | 34,519 | 30,422 | 46,303 | 11,562 | 9,305 |
|
|
5472
|
+
| \`timeline\` | 39,321 | 13,742 | 12,312 | 10,973 | 2,701 | 2,248 |
|
|
5473
|
+
| \`toast\` | 60,192 | 20,339 | 18,151 | 20,046 | 5,402 | 4,278 |
|
|
5474
|
+
| \`toggle\` | 51,106 | 16,791 | 14,924 | 10,851 | 3,065 | 2,304 |
|
|
5475
|
+
| \`toolbar\` | 34,496 | 12,352 | 10,991 | 7,820 | 2,142 | 1,667 |
|
|
5476
|
+
| \`tooltip\` | 62,502 | 22,262 | 19,733 | 11,290 | 3,110 | 2,485 |
|
|
5477
|
+
| \`tour\` | 95,265 | 31,293 | 27,689 | 30,070 | 7,561 | 6,075 |
|
|
5478
|
+
| \`tree\` | 77,329 | 25,058 | 22,304 | 25,871 | 6,863 | 5,731 |
|
|
5479
|
+
| \`trend\` | 32,854 | 12,576 | 11,227 | 5,487 | 1,641 | 1,260 |
|
|
5480
|
+
| \`video\` | 164,366 | 48,811 | 42,551 | 87,272 | 20,643 | 17,163 |
|
|
5481
|
+
| \`video-playlist\` | 144,942 | 44,812 | 39,319 | 58,789 | 14,041 | 11,807 |
|
|
5482
|
+
| \`virtual-list\` | 66,376 | 22,417 | 19,968 | 25,370 | 7,675 | 6,240 |
|
|
4841
5483
|
|
|
4842
5484
|
## What an install adds to \`node_modules\`
|
|
4843
5485
|
|
|
@@ -4845,16 +5487,17 @@ These sizes are what an install adds to your disk, which is what an install
|
|
|
4845
5487
|
budget limits. Each row is the unpacked size of exactly what a package
|
|
4846
5488
|
publishes, as listed in its \`files\` field. The sizes do not include source
|
|
4847
5489
|
maps. We strip them from the tarball when we pack it, and the Source maps
|
|
4848
|
-
stripped column shows the bytes we removed.
|
|
5490
|
+
stripped column shows the bytes we removed. We also pack each JSON file
|
|
5491
|
+
without its whitespace, and the sizes count each one that way.
|
|
4849
5492
|
|
|
4850
5493
|
| Package | Unpacked | Files | Source maps stripped | Runtime dependencies |
|
|
4851
5494
|
|---|---|---|---|---|
|
|
4852
|
-
| \`@fusion.dev/helix-core\` | 2,
|
|
4853
|
-
| \`@fusion.dev/helix-headless\` |
|
|
4854
|
-
| \`@fusion.dev/helix-icons\` |
|
|
4855
|
-
| \`@fusion.dev/helix-styles\` | 121,
|
|
4856
|
-
| \`@fusion.dev/helix-theme\` |
|
|
4857
|
-
| \`@fusion.dev/helix\` |
|
|
5495
|
+
| \`@fusion.dev/helix-core\` | 2,191,244 | 587 | 791,148 | \`@floating-ui/dom\`, \`@fusion.dev/helix-icons\`, \`tslib\` |
|
|
5496
|
+
| \`@fusion.dev/helix-headless\` | 23,979,650 | 772 | 2,534,725 | \`@fusion.dev/helix-core\`, \`@fusion.dev/helix-icons\`, \`tslib\`, \`uqr\` |
|
|
5497
|
+
| \`@fusion.dev/helix-icons\` | 152,695 | 21 | 49,995 | \`svgo\` |
|
|
5498
|
+
| \`@fusion.dev/helix-styles\` | 121,825 | 6 | 0 | none |
|
|
5499
|
+
| \`@fusion.dev/helix-theme\` | 449,779 | 8 | 6,255 | none |
|
|
5500
|
+
| \`@fusion.dev/helix\` | 24,617,831 | 1,023 | 566,915 | \`@fusion.dev/helix-core\`, \`@fusion.dev/helix-headless\`, \`@fusion.dev/helix-styles\`, \`tslib\` |
|
|
4858
5501
|
|
|
4859
5502
|
## Not measured yet
|
|
4860
5503
|
|
|
@@ -5017,9 +5660,9 @@ trailing: true
|
|
|
5017
5660
|
|
|
5018
5661
|
# Other frameworks
|
|
5019
5662
|
|
|
5020
|
-
This page covers Solid, Qwik, Lit, Ember, Alpine, htmx, and any other
|
|
5021
|
-
that puts elements in a document. They all follow the same three
|
|
5022
|
-
nothing else.
|
|
5663
|
+
This page covers Preact, Solid, Qwik, Lit, Ember, Alpine, htmx, and any other
|
|
5664
|
+
framework that puts elements in a document. They all follow the same three
|
|
5665
|
+
rules and need nothing else.
|
|
5023
5666
|
|
|
5024
5667
|
## The three rules
|
|
5025
5668
|
|
|
@@ -5053,11 +5696,12 @@ in full.
|
|
|
5053
5696
|
|
|
5054
5697
|
| Framework | Notes |
|
|
5055
5698
|
|---|---|
|
|
5056
|
-
| Solid | Sets properties on custom elements by default. Use \`prop:\` to force one and \`attr:\` to force the other. \`on:helix-before-change\` binds the literal name |
|
|
5057
|
-
| Qwik | Works as JSX. Bind with \`document:on…\` or a ref, since a plain \`on*\` prop is normalized |
|
|
5058
|
-
| Lit | \`.prop=\`, \`?bool=\`, and \`@helix-before-change=\` work with no setup |
|
|
5059
|
-
| Ember | Angle-bracket invocation passes attributes only, so set properties from a modifier |
|
|
5060
5699
|
| Alpine, htmx | Markup and attributes only, which work with every component. Refusing a change needs script |
|
|
5700
|
+
| Ember | Angle-bracket invocation passes attributes only, so set properties from a modifier |
|
|
5701
|
+
| Lit | \`.prop=\`, \`?bool=\`, and \`@helix-before-change=\` work with no setup |
|
|
5702
|
+
| Preact | Sets a property when the element has one, and an attribute otherwise. \`onhelix-before-change\` binds the literal name. [Typings](#typings) says how to type the tags |
|
|
5703
|
+
| Qwik | Works as JSX. Bind with \`document:on…\` or a ref, since a plain \`on*\` prop is normalized |
|
|
5704
|
+
| Solid | Sets properties on custom elements by default. Use \`prop:\` to force one and \`attr:\` to force the other. \`on:helix-before-change\` binds the literal name. [Typings](#typings) says how to type the tags |
|
|
5061
5705
|
|
|
5062
5706
|
We do not test these frameworks, so they share one page. If your framework needs
|
|
5063
5707
|
something the three rules do not cover, tell us. It is a gap in this page or in
|
|
@@ -5065,11 +5709,50 @@ the library.
|
|
|
5065
5709
|
|
|
5066
5710
|
## Typings
|
|
5067
5711
|
|
|
5068
|
-
\`@fusion.dev/helix/jsx\`
|
|
5069
|
-
the
|
|
5070
|
-
|
|
5071
|
-
|
|
5072
|
-
|
|
5712
|
+
\`@fusion.dev/helix/jsx\` types the tags for React, Preact, and Solid, and it is
|
|
5713
|
+
the base for typing any other framework that uses JSX. The Svelte typings read
|
|
5714
|
+
their props from it. It needs only the types of the framework you use, and it
|
|
5715
|
+
compiles with \`skipLibCheck: false\`.
|
|
5716
|
+
|
|
5717
|
+
\`ref\` and \`style\` take the types of the framework the tag is written in:
|
|
5718
|
+
|
|
5719
|
+
- React: React's \`Ref\` and \`CSSProperties\`.
|
|
5720
|
+
- Preact: Preact's \`Ref\`, and the \`style\` a \`<div>\` takes, which is a string or
|
|
5721
|
+
an object.
|
|
5722
|
+
- Solid: the element or a callback that receives it, and the \`style\` a \`<div>\`
|
|
5723
|
+
takes, which is a string or an object.
|
|
5724
|
+
- Any other framework: the element or a callback that receives it, and an
|
|
5725
|
+
object of style properties.
|
|
5726
|
+
|
|
5727
|
+
In Preact and Solid, import the typings once, anywhere TypeScript sees it:
|
|
5728
|
+
|
|
5729
|
+
\`\`\`ts
|
|
5730
|
+
import '@fusion.dev/helix/jsx'
|
|
5731
|
+
\`\`\`
|
|
5732
|
+
|
|
5733
|
+
Preact reads the tags with \`jsxImportSource: "preact"\` and with \`h\` as the JSX
|
|
5734
|
+
factory. If your \`paths\` alias \`react\` to \`preact/compat\`, the tags take \`ref\`
|
|
5735
|
+
and \`style\` as \`preact/compat\` types React's, so \`style\` takes an object.
|
|
5736
|
+
|
|
5737
|
+
Solid reads the tags with \`jsxImportSource: "solid-js"\`, and each tag also
|
|
5738
|
+
takes \`prop:\`, \`attr:\`, and \`on:\` forms of its props and events.
|
|
5739
|
+
|
|
5740
|
+
With \`jsxImportSource: "solid-js/h"\`, Solid runs without its compiler, and the
|
|
5741
|
+
same import types the tags. Each prop also takes a function that returns its
|
|
5742
|
+
value, and \`h\` calls that function again when a signal it reads changes. A
|
|
5743
|
+
property whose value is a function, such as a slider's \`formatter\`, takes only a
|
|
5744
|
+
function that returns it, because \`h\` calls any function it is given:
|
|
5745
|
+
|
|
5746
|
+
\`\`\`tsx
|
|
5747
|
+
<helix-slider disabled={() => busy()} formatter={() => format} label="Volume" />
|
|
5748
|
+
\`\`\`
|
|
5749
|
+
|
|
5750
|
+
\`ref\`, \`children\`, and the handlers named \`on…\`, such as
|
|
5751
|
+
\`on:helix-before-change\`, take their values as they are.
|
|
5752
|
+
|
|
5753
|
+
If your framework needs a different format, use the manifest at
|
|
5754
|
+
\`@fusion.dev/helix/custom-elements.json\`. It describes every tag, prop, event,
|
|
5755
|
+
and part, and we generate all our typings from it.
|
|
5073
5756
|
|
|
5074
5757
|
## Register the elements
|
|
5075
5758
|
|
|
@@ -5349,7 +6032,7 @@ helix-select::part(trigger) {
|
|
|
5349
6032
|
font-variant-numeric: tabular-nums;
|
|
5350
6033
|
}
|
|
5351
6034
|
|
|
5352
|
-
helix-dialog::part(
|
|
6035
|
+
helix-dialog::part(surface) {
|
|
5353
6036
|
max-inline-size: 40rem;
|
|
5354
6037
|
}
|
|
5355
6038
|
\`\`\`
|
|
@@ -5379,16 +6062,17 @@ helix-video::part(overflow-header) { … }
|
|
|
5379
6062
|
\`\`\`
|
|
5380
6063
|
|
|
5381
6064
|
\`<helix-date-input>\` renders a date picker in the \`picker\` role, so the picker's
|
|
5382
|
-
\`cell\` part is \`picker-cell\`.
|
|
5383
|
-
|
|
5384
|
-
\`tooltip
|
|
6065
|
+
\`cell\` part is \`picker-cell\`. The tooltip inside \`<helix-copy-button>\` is in the
|
|
6066
|
+
\`tooltip\` role, so its \`surface\` is \`tooltip-surface\` and its arrow is
|
|
6067
|
+
\`tooltip-arrow\`. When the child's part is the role itself, the forwarded name is
|
|
6068
|
+
the role alone: each line inside \`<helix-log>\` is \`line\`.
|
|
5385
6069
|
|
|
5386
6070
|
A part that changes with a state has a second name while the state is active,
|
|
5387
6071
|
and that name is forwarded too. A tooltip placed below its button is also
|
|
5388
|
-
\`tooltip-bottom\`, and its arrow is also \`tooltip-arrow-bottom\`. Your
|
|
5389
|
-
can use these names to style each side, which it cannot detect any
|
|
5390
|
-
The arrow already faces the button on every side. Use the names for
|
|
5391
|
-
add on top.
|
|
6072
|
+
\`tooltip-surface-bottom\`, and its arrow is also \`tooltip-arrow-bottom\`. Your
|
|
6073
|
+
stylesheet can use these names to style each side, which it cannot detect any
|
|
6074
|
+
other way. The arrow already faces the button on every side. Use the names for
|
|
6075
|
+
styles you add on top.
|
|
5392
6076
|
|
|
5393
6077
|
**The prefix keeps part names unique.** A component and its child can use the
|
|
5394
6078
|
same word. This happened once: \`Command\` forwarded the listbox's \`search-field\`
|
|
@@ -5418,8 +6102,8 @@ so you learn each name once:
|
|
|
5418
6102
|
| \`container\` | An element's own outer box. |
|
|
5419
6103
|
| \`content\` | A region you fill. |
|
|
5420
6104
|
| \`panel\` | A region switched between, or a sheet anchored to an edge. |
|
|
5421
|
-
| \`surface\` | A floating panel drawn over the page. |
|
|
5422
|
-
| \`viewport\` |
|
|
6105
|
+
| \`surface\` | A floating panel drawn over the page, such as a menu, a popover, a tooltip, or a dialog. |
|
|
6106
|
+
| \`viewport\` | The box that shows part of a larger area: a scrollport, or the frame a map or a graph is panned and zoomed in. |
|
|
5423
6107
|
|
|
5424
6108
|
A part that names something specific to one component, such as \`thumb\`,
|
|
5425
6109
|
\`swatch\`, or \`weekday\`, is listed on that component's page.
|
|
@@ -5591,6 +6275,8 @@ starts with that value and keeps applying its own changes, so the markup below
|
|
|
5591
6275
|
works without a handler.
|
|
5592
6276
|
|
|
5593
6277
|
\`\`\`tsx
|
|
6278
|
+
import { flushSync } from 'react-dom'
|
|
6279
|
+
|
|
5594
6280
|
// Seeded: the element owns it from there.
|
|
5595
6281
|
<helix-input default-value="Ada" label="Name" />
|
|
5596
6282
|
|
|
@@ -5602,7 +6288,7 @@ works without a handler.
|
|
|
5602
6288
|
label="Name"
|
|
5603
6289
|
onhelix-before-change={(event) => {
|
|
5604
6290
|
event.preventDefault()
|
|
5605
|
-
setName(clean(event.detail.value))
|
|
6291
|
+
flushSync(() => setName(clean(event.detail.value)))
|
|
5606
6292
|
}}
|
|
5607
6293
|
value={name}
|
|
5608
6294
|
/>
|
|
@@ -5613,6 +6299,18 @@ refuse it. React restores a native \`<input>\` whose state did not change, and i
|
|
|
5613
6299
|
does not do this for a custom element. A handler that does not call \`setState\`
|
|
5614
6300
|
leaves the element with the new value.
|
|
5615
6301
|
|
|
6302
|
+
**Set the state inside \`flushSync\` when you write your own value.** React sets
|
|
6303
|
+
the priority of a state update from the type of the event being handled.
|
|
6304
|
+
\`helix-before-change\` is not one of the types React lists, so the update gets
|
|
6305
|
+
the default priority and renders in a later task. A key that arrives before that
|
|
6306
|
+
task lands in the text from before the update, and the keys typed in between are
|
|
6307
|
+
lost. A barcode scanner, a password manager, and a test send keys that fast:
|
|
6308
|
+
Playwright typing \`Ada L!\` with no delay into a field that keeps lowercase
|
|
6309
|
+
letters left it empty. \`flushSync\` renders the update before your handler
|
|
6310
|
+
returns, so the element holds your value when the next key arrives. The same
|
|
6311
|
+
applies to every field you type into, such as \`helix-textarea\`,
|
|
6312
|
+
\`helix-number-input\`, and \`helix-pin-input\`.
|
|
6313
|
+
|
|
5616
6314
|
*Until August 26, 2026, this page described the opposite behavior, and the typings
|
|
5617
6315
|
enforced it. Writing \`value\` made you the owner of the state. The element
|
|
5618
6316
|
applied no change until you set the value, and \`<helix-input value="Ada" />\`
|
|
@@ -5789,7 +6487,7 @@ Three Markdown constructs need components, because HTML alone cannot hold what
|
|
|
5789
6487
|
they need:
|
|
5790
6488
|
|
|
5791
6489
|
- **Fenced code** becomes \`helix-code-block\`, with the fence's language as its
|
|
5792
|
-
\`label\`. A \`<pre>\` has no label or copy button.
|
|
6490
|
+
\`label\` and its \`language\`. A \`<pre>\` has no label or copy button.
|
|
5793
6491
|
- **Alerts** such as \`> [!WARNING]\` become \`helix-callout\`. A \`<blockquote>\` has no
|
|
5794
6492
|
tone. \`NOTE\` becomes \`tone="info"\`, \`TIP\` becomes \`success\`, \`WARNING\` becomes
|
|
5795
6493
|
\`warning\`, and \`CAUTION\` becomes \`danger\`. \`IMPORTANT\` has no tone. Each
|
|
@@ -5804,13 +6502,29 @@ The \`rehype\` plugin from \`@fusion.dev/helix/rehype\` makes all three changes
|
|
|
5804
6502
|
tree your Markdown library has already built. It parses nothing, so your
|
|
5805
6503
|
library, its dialect, and your sanitizing stay yours. Add it to any pipeline
|
|
5806
6504
|
that reaches rehype, such as \`remark\` with \`remark-rehype\`, \`react-markdown\`, or
|
|
5807
|
-
MDX
|
|
6505
|
+
MDX.
|
|
6506
|
+
|
|
6507
|
+
Tables are GitHub Flavored Markdown, so your pipeline needs \`remark-gfm\` to
|
|
6508
|
+
parse them. Without it, a table stays a paragraph of pipes. For a \`remark\`
|
|
6509
|
+
pipeline, install:
|
|
6510
|
+
|
|
6511
|
+
\`\`\`sh
|
|
6512
|
+
bun add unified remark-parse remark-gfm remark-rehype rehype-stringify
|
|
6513
|
+
\`\`\`
|
|
6514
|
+
|
|
6515
|
+
Then add the plugin after \`remark-rehype\`:
|
|
5808
6516
|
|
|
5809
6517
|
\`\`\`js
|
|
5810
6518
|
import rehypeHelix from '@fusion.dev/helix/rehype'
|
|
6519
|
+
import rehypeStringify from 'rehype-stringify'
|
|
6520
|
+
import remarkGfm from 'remark-gfm'
|
|
6521
|
+
import remarkParse from 'remark-parse'
|
|
6522
|
+
import remarkRehype from 'remark-rehype'
|
|
6523
|
+
import { unified } from 'unified'
|
|
5811
6524
|
|
|
5812
6525
|
const html = await unified()
|
|
5813
6526
|
.use(remarkParse)
|
|
6527
|
+
.use(remarkGfm)
|
|
5814
6528
|
.use(remarkRehype)
|
|
5815
6529
|
.use(rehypeHelix)
|
|
5816
6530
|
.use(rehypeStringify)
|
|
@@ -6095,8 +6809,10 @@ You can edit every example on a component's page. When you change the markup,
|
|
|
6095
6809
|
the preview redraws. When you change the stylesheet beside it, the preview
|
|
6096
6810
|
restyles.
|
|
6097
6811
|
|
|
6098
|
-
To build a playground of your own,
|
|
6099
|
-
|
|
6812
|
+
To build a playground of your own, use the same elements: the sandbox from
|
|
6813
|
+
\`@fusion.dev/helix/sandbox\`, and the editor from \`@fusion.dev/helix-sandbox\`.
|
|
6814
|
+
The isolation described below makes it safe to run markup that someone else
|
|
6815
|
+
wrote.
|
|
6100
6816
|
|
|
6101
6817
|
## How the preview is isolated
|
|
6102
6818
|
|
|
@@ -6243,7 +6959,7 @@ preview, and \`helix-caret\` from it fills the status bar's position. A wrapper
|
|
|
6243
6959
|
around CodeMirror:
|
|
6244
6960
|
|
|
6245
6961
|
\`\`\`ts
|
|
6246
|
-
import { HelixBeforeEditEvent } from '@fusion.dev/helix'
|
|
6962
|
+
import { HelixBeforeEditEvent } from '@fusion.dev/helix-core'
|
|
6247
6963
|
import { EditorView, basicSetup } from 'codemirror'
|
|
6248
6964
|
|
|
6249
6965
|
class CodeEditor extends HTMLElement {
|
|
@@ -6300,16 +7016,18 @@ own message from \`:state(failed)\`.
|
|
|
6300
7016
|
|
|
6301
7017
|
## The frame document
|
|
6302
7018
|
|
|
6303
|
-
\`helix-sandbox-preview\`
|
|
6304
|
-
document
|
|
6305
|
-
to the preview through \`postMessage\`.
|
|
7019
|
+
\`helix-sandbox-preview\` runs each example in a sandboxed frame. The frame
|
|
7020
|
+
document either comes with the preview or is one you host.
|
|
6306
7021
|
|
|
6307
|
-
Without \`src\`, the preview loads a built-in document
|
|
6308
|
-
HTML, CSS, and scripts, and reports the
|
|
6309
|
-
|
|
6310
|
-
|
|
6311
|
-
|
|
6312
|
-
|
|
7022
|
+
**Without \`src\`, the preview loads a built-in document**, so a sandbox needs
|
|
7023
|
+
nothing hosted. It runs the example's HTML, CSS, and scripts, and reports the
|
|
7024
|
+
console, uncaught errors, the height, and presses. It defines no element, so
|
|
7025
|
+
set \`src\` to a document of your own when your examples use Helix components.
|
|
7026
|
+
Its script is inline, so a page whose Content Security Policy refuses inline
|
|
7027
|
+
scripts needs a \`src\` too.
|
|
7028
|
+
|
|
7029
|
+
**With \`src\`, the preview loads your document.** It loads the styles and the
|
|
7030
|
+
elements your examples use, and it talks to the preview through \`postMessage\`.
|
|
6313
7031
|
|
|
6314
7032
|
The messages:
|
|
6315
7033
|
|
|
@@ -6333,9 +7051,18 @@ it was given. The types are exported from \`@fusion.dev/helix/sandbox\` as
|
|
|
6333
7051
|
you. Call it once in the frame document, after your elements are defined. It
|
|
6334
7052
|
writes each render into \`#preview-root\`, runs the example's scripts, relays
|
|
6335
7053
|
its console, its uncaught errors, and its unhandled promise rejections,
|
|
6336
|
-
reports its height and its presses, and adds the rules for the four layouts.
|
|
6337
|
-
|
|
6338
|
-
|
|
7054
|
+
reports its height and its presses, and adds the rules for the four layouts.
|
|
7055
|
+
|
|
7056
|
+
The frame's own script must be a classic script, bundled into one file. A
|
|
7057
|
+
module does not load there. The frame's origin is opaque, so a module import
|
|
7058
|
+
back to your site is a cross-origin request, which fails unless your server
|
|
7059
|
+
answers it with CORS headers. A stylesheet \`<link>\` is not checked, so the
|
|
7060
|
+
styles load as they are.
|
|
7061
|
+
|
|
7062
|
+
A frame follows. \`/frame.js\` is the bundle. \`/helix.css\` is the stylesheet
|
|
7063
|
+
from \`@fusion.dev/helix-styles\`, and it imports \`primitives.css\`, \`tokens.css\`,
|
|
7064
|
+
\`base.css\`, and \`elements.css\` from beside it, so serve the five files from
|
|
7065
|
+
the package's \`dist\` folder together:
|
|
6339
7066
|
|
|
6340
7067
|
\`\`\`html
|
|
6341
7068
|
<!doctype html>
|
|
@@ -6367,19 +7094,56 @@ Logs from the example appear in the console panel. Logs from the page around it
|
|
|
6367
7094
|
do not. We patch the console inside the frame on purpose, so the panel shows
|
|
6368
7095
|
what the example logs and nothing else.
|
|
6369
7096
|
|
|
6370
|
-
## The
|
|
7097
|
+
## The editor and the highlighter
|
|
7098
|
+
|
|
7099
|
+
\`@fusion.dev/helix-sandbox\` is the editor the component pages use, and the
|
|
7100
|
+
highlighter that colors their code. Install it beside the library:
|
|
7101
|
+
|
|
7102
|
+
\`\`\`sh
|
|
7103
|
+
npm install @fusion.dev/helix @fusion.dev/helix-styles @fusion.dev/helix-sandbox
|
|
7104
|
+
\`\`\`
|
|
7105
|
+
|
|
7106
|
+
Each entry does one thing:
|
|
7107
|
+
|
|
7108
|
+
- \`@fusion.dev/helix-sandbox/editor\` registers \`helix-sandbox-editor\`, a
|
|
7109
|
+
source pane with CodeMirror behind it.
|
|
7110
|
+
- \`@fusion.dev/helix-sandbox/highlight\` turns a snippet into highlighted HTML,
|
|
7111
|
+
on a server or in a page, with no editor.
|
|
7112
|
+
- \`@fusion.dev/helix-sandbox/code-block\` colors every Code Block on the page.
|
|
7113
|
+
|
|
7114
|
+
The editor is a source pane, so it goes in the \`source\` slot, with a \`name\`
|
|
7115
|
+
the sandbox reads:
|
|
7116
|
+
|
|
7117
|
+
\`\`\`html
|
|
7118
|
+
<helix-sandbox>
|
|
7119
|
+
<helix-sandbox-editor
|
|
7120
|
+
language="html"
|
|
7121
|
+
name="html"
|
|
7122
|
+
slot="source"
|
|
7123
|
+
value="<p>Hello</p>"
|
|
7124
|
+
></helix-sandbox-editor>
|
|
7125
|
+
<helix-sandbox-preview slot="preview"></helix-sandbox-preview>
|
|
7126
|
+
</helix-sandbox>
|
|
7127
|
+
\`\`\`
|
|
7128
|
+
|
|
7129
|
+
\`\`\`ts
|
|
7130
|
+
import '@fusion.dev/helix/sandbox'
|
|
7131
|
+
import '@fusion.dev/helix-sandbox/editor'
|
|
7132
|
+
\`\`\`
|
|
6371
7133
|
|
|
6372
|
-
|
|
6373
|
-
|
|
7134
|
+
The [Sandbox reference](/helix/components/sandbox#helix-sandbox-editor) lists
|
|
7135
|
+
the editor's properties, events, methods, and states.
|
|
6374
7136
|
|
|
6375
|
-
|
|
6376
|
-
|
|
6377
|
-
|
|
7137
|
+
\`highlight\` reads the same grammars as the editor. By default each token is a
|
|
7138
|
+
span with Lezer's \`tok-\` class, for a stylesheet of your own. Pass
|
|
7139
|
+
\`syntaxHighlighter\` for the editor's classes, and put \`syntaxRules\` in the
|
|
7140
|
+
stylesheet where the result is drawn for the editor's colors:
|
|
6378
7141
|
|
|
6379
|
-
|
|
6380
|
-
|
|
6381
|
-
|
|
6382
|
-
|
|
7142
|
+
\`\`\`ts
|
|
7143
|
+
import { highlight, syntaxHighlighter } from '@fusion.dev/helix-sandbox/highlight'
|
|
7144
|
+
|
|
7145
|
+
const markup = highlight('const total = 1', 'ts', syntaxHighlighter)
|
|
7146
|
+
\`\`\`
|
|
6383
7147
|
`,
|
|
6384
7148
|
'server-rendering': `---
|
|
6385
7149
|
description: How components render on a server with no DOM, and what waits for the browser.
|
|
@@ -6468,8 +7232,8 @@ Run your page's own scripts after the definitions load. Hydration expects that
|
|
|
6468
7232
|
order. A property you set before hydration is one the server did not render
|
|
6469
7233
|
with.
|
|
6470
7234
|
|
|
6471
|
-
|
|
6472
|
-
Chromium, and
|
|
7235
|
+
We render every component example in both tiers with Lit SSR, hydrate it in
|
|
7236
|
+
Chromium, and compare it with the same example rendered on a client.
|
|
6473
7237
|
|
|
6474
7238
|
React server components do not use declarative shadow DOM. Their output is
|
|
6475
7239
|
replayed through DOM APIs, and declarative shadow DOM does not activate for
|
|
@@ -7228,12 +7992,18 @@ state yourself, cancel the change and assign your own value:
|
|
|
7228
7992
|
label="Name"
|
|
7229
7993
|
onhelix-before-change={(event) => {
|
|
7230
7994
|
event.preventDefault()
|
|
7231
|
-
setName(sanitize(event.detail.value))
|
|
7995
|
+
flushSync(() => setName(sanitize(event.detail.value)))
|
|
7232
7996
|
}}
|
|
7233
7997
|
value={name}
|
|
7234
7998
|
/>
|
|
7235
7999
|
\`\`\`
|
|
7236
8000
|
|
|
8001
|
+
Assign your value before the next key arrives, or that key lands in the text
|
|
8002
|
+
from before your value. Vue, Svelte, and Solid apply a bound value in time. In
|
|
8003
|
+
React, set the state inside \`flushSync\`, as [React](./react) explains. In
|
|
8004
|
+
Angular without zone.js, call \`detectChanges()\` after you set the signal, as
|
|
8005
|
+
[Angular](./angular) explains.
|
|
8006
|
+
|
|
7237
8007
|
The platform uses the same pattern. You call \`preventDefault()\` to cancel:
|
|
7238
8008
|
|
|
7239
8009
|
- a form submit
|
|
@@ -7431,7 +8201,8 @@ its own components, and the same applies to anything you build with it.
|
|
|
7431
8201
|
The theme engine measures each ink against the ground it is actually painted
|
|
7432
8202
|
on. The ladder records which pairs occur. For example, \`foreground\` on
|
|
7433
8203
|
\`surface-overlay\` is a real pair, and \`foreground-on-primary\` on \`surface\` is
|
|
7434
|
-
not.
|
|
8204
|
+
not. A field's fill, \`well\`, is translucent, so the text in a field is measured
|
|
8205
|
+
against the well over each surface. See [Color](./color).
|
|
7435
8206
|
`,
|
|
7436
8207
|
svelte: `---
|
|
7437
8208
|
description: The Svelte typings, and how Svelte passes props and binds events.
|
|
@@ -7447,30 +8218,74 @@ import '@fusion.dev/helix/svelte'
|
|
|
7447
8218
|
\`\`\`
|
|
7448
8219
|
|
|
7449
8220
|
Add this types-only import once. It augments \`SvelteHTMLElements\`, so every
|
|
7450
|
-
Helix tag is typed in a component.
|
|
7451
|
-
|
|
7452
|
-
|
|
7453
|
-
cannot disagree.
|
|
8221
|
+
Helix tag is typed in a component. Svelte binds a handler as
|
|
8222
|
+
\`onhelix-before-change={…}\`, which is the shape the JSX typings already
|
|
8223
|
+
describe, so the Svelte typings read each tag's props from
|
|
8224
|
+
\`@fusion.dev/helix/jsx\`. The two cannot disagree.
|
|
8225
|
+
|
|
8226
|
+
The typings need only Svelte's own types, and they compile with
|
|
8227
|
+
\`skipLibCheck: false\`.
|
|
7454
8228
|
|
|
7455
8229
|
## Pass props and bind events
|
|
7456
8230
|
|
|
7457
8231
|
\`\`\`svelte
|
|
7458
|
-
<helix-input
|
|
8232
|
+
<helix-input
|
|
8233
|
+
label="Name"
|
|
8234
|
+
onchange={(event) => (name = event.currentTarget.value)}
|
|
8235
|
+
value={name}
|
|
8236
|
+
></helix-input>
|
|
7459
8237
|
\`\`\`
|
|
7460
8238
|
|
|
7461
8239
|
Svelte sets a property when the element has one, and an attribute otherwise. It
|
|
7462
8240
|
binds \`on*\` with \`addEventListener\`. Svelte shows no unknown-element warning,
|
|
7463
8241
|
and you do not need a compiler flag.
|
|
7464
8242
|
|
|
8243
|
+
Read the value from \`event.currentTarget\`, which is typed as the element.
|
|
8244
|
+
\`event.target\` is an \`EventTarget | null\`, which has no \`value\`.
|
|
8245
|
+
|
|
7465
8246
|
Svelte passes objects and arrays as properties on its own:
|
|
7466
8247
|
|
|
7467
8248
|
\`\`\`svelte
|
|
7468
|
-
<helix-data-table label="People" rows={people}
|
|
8249
|
+
<helix-data-table label="People" rows={people}></helix-data-table>
|
|
7469
8250
|
\`\`\`
|
|
7470
8251
|
|
|
7471
8252
|
Svelte's own attributes are typed on every Helix tag as well, so \`onclick\`,
|
|
7472
8253
|
\`class\`, and a \`style\` string check as they do on a \`div\`.
|
|
7473
8254
|
|
|
8255
|
+
## Hold the element
|
|
8256
|
+
|
|
8257
|
+
\`bind:this\` holds the element, typed as its class:
|
|
8258
|
+
|
|
8259
|
+
\`\`\`svelte
|
|
8260
|
+
<script lang="ts">
|
|
8261
|
+
import type { HelixInput } from '@fusion.dev/helix'
|
|
8262
|
+
|
|
8263
|
+
let input: HelixInput | undefined = $state()
|
|
8264
|
+
</script>
|
|
8265
|
+
|
|
8266
|
+
<helix-input bind:this={input} label="Name"></helix-input>
|
|
8267
|
+
\`\`\`
|
|
8268
|
+
|
|
8269
|
+
## Type a handler in the script
|
|
8270
|
+
|
|
8271
|
+
A handler written on the tag takes its event type from the tag. A handler
|
|
8272
|
+
written in the script names its event type, which \`@fusion.dev/helix/jsx\`
|
|
8273
|
+
exports for each element and event:
|
|
8274
|
+
|
|
8275
|
+
\`\`\`svelte
|
|
8276
|
+
<script lang="ts">
|
|
8277
|
+
import type { HelixInputHelixBeforeChangeElementEvent } from '@fusion.dev/helix/jsx'
|
|
8278
|
+
|
|
8279
|
+
const refuseLong = (event: HelixInputHelixBeforeChangeElementEvent) => {
|
|
8280
|
+
if (event.detail.value.length > 40) {
|
|
8281
|
+
event.preventDefault()
|
|
8282
|
+
}
|
|
8283
|
+
}
|
|
8284
|
+
</script>
|
|
8285
|
+
|
|
8286
|
+
<helix-input label="Handle" onhelix-before-change={refuseLong}></helix-input>
|
|
8287
|
+
\`\`\`
|
|
8288
|
+
|
|
7474
8289
|
## Ignore the click warnings on a control
|
|
7475
8290
|
|
|
7476
8291
|
\`svelte-check\` warns about an \`onclick\` on any Helix element. Its accessibility
|
|
@@ -7490,8 +8305,10 @@ right. Use a control there.
|
|
|
7490
8305
|
|
|
7491
8306
|
These two rules apply in every framework.
|
|
7492
8307
|
|
|
7493
|
-
|
|
7494
|
-
two-way binding convention to opt into.
|
|
8308
|
+
Two-way bindings do not work on a Helix control, because custom elements have
|
|
8309
|
+
no two-way binding convention to opt into. Svelte accepts \`bind:value\` only on
|
|
8310
|
+
\`<input>\`, \`<textarea>\`, and \`<select>\`, and \`bind:checked\` only on \`<input>\`.
|
|
8311
|
+
Read the value from the event.
|
|
7495
8312
|
|
|
7496
8313
|
A declared \`value\`, \`open\`, \`checked\`, or \`active\` is a seed. The element starts
|
|
7497
8314
|
with that value and then applies its own changes. To refuse a change, cancel the
|
|
@@ -7573,9 +8390,10 @@ its target. Its three tabs are:
|
|
|
7573
8390
|
- **Contrast.** The pairs that fail, or a line saying that every pair passes.
|
|
7574
8391
|
- **Collisions.** Colors too close to each other to tell apart, such as the
|
|
7575
8392
|
primary color and a status color.
|
|
7576
|
-
- **Unverifiable.** Pairs whose background is translucent
|
|
7577
|
-
|
|
7578
|
-
|
|
8393
|
+
- **Unverifiable.** Pairs whose background is translucent over something the
|
|
8394
|
+
engine cannot know, such as a photograph or the page under a translucent
|
|
8395
|
+
material, so there is no known color to measure against. This is the cost of
|
|
8396
|
+
a translucent material, and the theme builder reports it.
|
|
7579
8397
|
|
|
7580
8398
|
## What the theme builder gives you
|
|
7581
8399
|
|
|
@@ -7589,6 +8407,17 @@ stylesheet:
|
|
|
7589
8407
|
}
|
|
7590
8408
|
\`\`\`
|
|
7591
8409
|
|
|
8410
|
+
The block holds one mode, the one the preview shows, under the scope
|
|
8411
|
+
\`[data-theme="preview"]\`. Put that attribute on the element to theme, or change
|
|
8412
|
+
the selector. Under any selector other than \`:root\`, also add
|
|
8413
|
+
\`data-helix-theme\` to the element. The block sets the seeds the stylesheet
|
|
8414
|
+
derives the chart colors, the spacing, and the corner radii from, and the
|
|
8415
|
+
stylesheet reads seeds only at \`:root\` or on an element with
|
|
8416
|
+
\`data-helix-theme\`. See [Two ways to theme](./theming#two-ways-to-theme).
|
|
8417
|
+
|
|
8418
|
+
For a page with both modes, derive the intent with \`deriveModes\` or
|
|
8419
|
+
\`helix theme build\`, as [Derive both modes](./theming#derive-both-modes) shows.
|
|
8420
|
+
|
|
7592
8421
|
The page's link holds every input you changed, so a link is the theme. To derive
|
|
7593
8422
|
the theme in your own build, write the same inputs as an intent object:
|
|
7594
8423
|
|
|
@@ -7639,7 +8468,11 @@ changing them one at a time. A change to one value can break the contrast of a
|
|
|
7639
8468
|
component elsewhere, and nothing tells you.
|
|
7640
8469
|
|
|
7641
8470
|
\`@fusion.dev/helix-theme\` computes a complete token set from a few intent
|
|
7642
|
-
inputs
|
|
8471
|
+
inputs. It solves each text color against the backgrounds it lands on, measures
|
|
8472
|
+
every pair in its surface model, and reports any pair that misses its contrast
|
|
8473
|
+
target. \`foreground-subtle\` is not in the model. It is a fixed step of the
|
|
8474
|
+
stylesheet's gray ramp, and the engine does not solve it, so its contrast is not
|
|
8475
|
+
guaranteed.
|
|
7643
8476
|
|
|
7644
8477
|
## Two ways to theme
|
|
7645
8478
|
|
|
@@ -7855,8 +8688,8 @@ depends only on the packages below it.
|
|
|
7855
8688
|
Neither is a tier, and you do not need either one to render a component.
|
|
7856
8689
|
|
|
7857
8690
|
There is no React package. We generate the framework typings into
|
|
7858
|
-
\`@fusion.dev/helix\`, and the bridge that
|
|
7859
|
-
events is in \`@fusion.dev/helix-core\`. See [React](./react).
|
|
8691
|
+
\`@fusion.dev/helix\` and \`@fusion.dev/helix-headless\`, and the bridge that
|
|
8692
|
+
repairs React's hydration of custom events is in \`@fusion.dev/helix-core\`. See [React](./react).
|
|
7860
8693
|
|
|
7861
8694
|
## Why the tiers are separate packages
|
|
7862
8695
|
|
|
@@ -7898,7 +8731,7 @@ export class HelixHeadlessAccordionItem extends MixinControllerOpen(HelixElement
|
|
|
7898
8731
|
}
|
|
7899
8732
|
}
|
|
7900
8733
|
|
|
7901
|
-
//
|
|
8734
|
+
// styled/src/accordion/root/accordion-root.ts
|
|
7902
8735
|
// extends the headless one, adds presentation only
|
|
7903
8736
|
import { styles } from './accordion-root.css.js'
|
|
7904
8737
|
|
|
@@ -7912,7 +8745,7 @@ export class HelixAccordion extends HelixHeadlessAccordion {
|
|
|
7912
8745
|
\`\`\`
|
|
7913
8746
|
|
|
7914
8747
|
\`\`\`css
|
|
7915
|
-
/*
|
|
8748
|
+
/* styled/src/accordion/item/accordion-item.css */
|
|
7916
8749
|
[part='trigger'] {
|
|
7917
8750
|
min-block-size: var(--helix-size-control);
|
|
7918
8751
|
padding: var(--helix-space-sm);
|
|
@@ -7949,8 +8782,8 @@ as unstyled text and then changes into a card. The delay is short. It is most
|
|
|
7949
8782
|
visible on a server-rendered page, where the markup arrives long before the
|
|
7950
8783
|
JavaScript.
|
|
7951
8784
|
|
|
7952
|
-
\`@fusion.dev/helix\` includes \`elements.css\`, which hides each of
|
|
7953
|
-
the tag's definition loads:
|
|
8785
|
+
\`@fusion.dev/helix-styles\` includes \`elements.css\`, which hides each tag of the
|
|
8786
|
+
styled tier until the tag's definition loads:
|
|
7954
8787
|
|
|
7955
8788
|
\`\`\`css
|
|
7956
8789
|
helix-card:not(:defined) {
|
|
@@ -7958,7 +8791,7 @@ helix-card:not(:defined) {
|
|
|
7958
8791
|
}
|
|
7959
8792
|
\`\`\`
|
|
7960
8793
|
|
|
7961
|
-
**The
|
|
8794
|
+
**The headless tier ships no such file on purpose.** There are three reasons, and
|
|
7962
8795
|
the third one affects you most.
|
|
7963
8796
|
|
|
7964
8797
|
First, hiding an element is a design choice. Below the styled tier, we ship only
|
|
@@ -7972,20 +8805,21 @@ inside, because there is no shadow root before the upgrade. The rule has to
|
|
|
7972
8805
|
live in your stylesheet, so the choice is yours.
|
|
7973
8806
|
|
|
7974
8807
|
Third, one file for every tag would not fit your project. You import only the
|
|
7975
|
-
|
|
8808
|
+
headless elements you use, so a generated list would include tags you never ship.
|
|
7976
8809
|
Write the rule for the elements you have:
|
|
7977
8810
|
|
|
7978
8811
|
\`\`\`css
|
|
7979
|
-
helix-
|
|
7980
|
-
helix-
|
|
8812
|
+
helix-headless-accordion:not(:defined),
|
|
8813
|
+
helix-headless-dialog:not(:defined) {
|
|
7981
8814
|
visibility: hidden;
|
|
7982
8815
|
}
|
|
7983
8816
|
\`\`\`
|
|
7984
8817
|
|
|
7985
|
-
Use \`visibility
|
|
7986
|
-
|
|
7987
|
-
|
|
7988
|
-
|
|
8818
|
+
Use \`visibility\`, so a hidden element keeps the size of its unstyled content.
|
|
8819
|
+
The layout still settles when the element upgrades and takes its own size.
|
|
8820
|
+
\`display: none\` collapses the box, so the element takes no space until the
|
|
8821
|
+
definition arrives, and then everything below it shifts by the element's full
|
|
8822
|
+
size.
|
|
7989
8823
|
|
|
7990
8824
|
You may want to hide nothing. If your first paint matters more than the final
|
|
7991
8825
|
layout, or your page has to be readable with JavaScript turned off, let the
|
|
@@ -8289,8 +9123,11 @@ writes where an installed family stands in for it, as \`native-element\`.
|
|
|
8289
9123
|
|
|
8290
9124
|
Each family's API is in the packages you installed, and on this site:
|
|
8291
9125
|
|
|
8292
|
-
- \`helix api family <family>\`, or the MCP tool \`helix_family
|
|
8293
|
-
|
|
9126
|
+
- \`helix api family <family>\`, or the MCP tool \`helix_family\`, which read every
|
|
9127
|
+
Helix package you installed
|
|
9128
|
+
- \`node_modules/@fusion.dev/helix/api.json\`, and the \`api.json\` of each other
|
|
9129
|
+
Helix package you installed, which lists the elements it adds to a family,
|
|
9130
|
+
such as \`helix-sandbox-editor\` in \`@fusion.dev/helix-sandbox\`
|
|
8294
9131
|
- \`https://fusion.dev/helix/components/<family>.md\` and \`.json\`
|
|
8295
9132
|
|
|
8296
9133
|
Each lists the import lines, every tag, every property with its literal union,
|
|
@@ -8344,12 +9181,15 @@ block a change, call \`preventDefault()\` on the matching \`helix-before-*\` eve
|
|
|
8344
9181
|
label="Name"
|
|
8345
9182
|
onhelix-before-change={(event) => {
|
|
8346
9183
|
event.preventDefault()
|
|
8347
|
-
setName(clean(event.detail.value))
|
|
9184
|
+
flushSync(() => setName(clean(event.detail.value)))
|
|
8348
9185
|
}}
|
|
8349
9186
|
value={name}
|
|
8350
9187
|
/>
|
|
8351
9188
|
\`\`\`
|
|
8352
9189
|
|
|
9190
|
+
Set the state inside \`flushSync\` from \`react-dom\`. Without it, React renders the
|
|
9191
|
+
update in a later task, and keys typed faster than that are lost.
|
|
9192
|
+
|
|
8353
9193
|
A form reset returns the element to \`default-value\` and the matching attributes
|
|
8354
9194
|
for the other states. See [State and control](./state-and-control).
|
|
8355
9195
|
|
|
@@ -8459,12 +9299,13 @@ Run these after you write Helix code, and fix every finding at \`error\` and
|
|
|
8459
9299
|
|
|
8460
9300
|
| Command | MCP tool | What it finds |
|
|
8461
9301
|
|---|---|---|
|
|
8462
|
-
| \`helix scan\` | \`helix_check\` | Unknown tags, attributes, values, slots, parts, hooks, events, and tokens, a native element a Helix family replaces, a missing import, and the rest, each with its file and line |
|
|
9302
|
+
| \`helix scan\` | \`helix_check\` | Unknown tags, attributes, values, slots, parts, hooks, events, and tokens, a native element a Helix family replaces, a missing import, a button inside a button, \`disabled="false"\`, and the rest, each with its file and line |
|
|
8463
9303
|
| \`helix diagnostics --once <url>\` | \`helix_check_page\` | The same on the running page, and what the library logged as it ran |
|
|
8464
9304
|
| \`helix theme audit\` | \`helix_audit_theme\` | Every foreground and background pair in your theme that misses its contrast target |
|
|
8465
9305
|
|
|
8466
9306
|
\`helix scan --file <path>\` reports one file's findings and still reads the whole
|
|
8467
|
-
project, so an import in another file counts.
|
|
9307
|
+
project, so an import in another file counts. [Diagnostics](./diagnostics)
|
|
9308
|
+
lists every kind of finding, and what the checks do not cover.
|
|
8468
9309
|
|
|
8469
9310
|
## What this guide does not cover
|
|
8470
9311
|
|
|
@@ -8579,8 +9420,14 @@ import '@fusion.dev/helix-styles/helix.css'
|
|
|
8579
9420
|
base layer. If you import the layers one at a time, import \`elements.css\` last.
|
|
8580
9421
|
|
|
8581
9422
|
\`elements.css\` hides each tag with \`:not(:defined)\` until the element upgrades.
|
|
8582
|
-
It uses \`visibility\`, so
|
|
8583
|
-
|
|
9423
|
+
It uses \`visibility\`, so a hidden element keeps the size of its unstyled
|
|
9424
|
+
content. The layout settles when the elements upgrade and take their own size.
|
|
9425
|
+
Without the file, a card appears as unstyled text and then jumps into its card
|
|
9426
|
+
layout.
|
|
9427
|
+
|
|
9428
|
+
A data table that serves its first page as rows is the one element shown before
|
|
9429
|
+
it upgrades. \`elements.css\` lays out those rows as the table draws them. See
|
|
9430
|
+
[React](./react).
|
|
8584
9431
|
|
|
8585
9432
|
We generate the file from the element sources, and it lists every tag by name.
|
|
8586
9433
|
CSS has no selector for any custom element, and a bare \`:not(:defined)\` would
|
|
@@ -8598,36 +9445,31 @@ use a button.
|
|
|
8598
9445
|
|
|
8599
9446
|
## Turn off an attribute that is on by default
|
|
8600
9447
|
|
|
8601
|
-
A boolean attribute is off until you write it,
|
|
8602
|
-
on
|
|
8603
|
-
|
|
8604
|
-
|
|
8605
|
-
- \`playsinline\` on Video
|
|
8606
|
-
- \`default-pinned\` on Chat
|
|
8607
|
-
- \`tab-indents\` on Sandbox Source
|
|
9448
|
+
A boolean attribute is off until you write it, unless its default is \`true\`. The
|
|
9449
|
+
property table on each component's page shows the default. An attribute whose
|
|
9450
|
+
default is \`true\` stays on until you write the string \`"false"\`, such as \`modal\`
|
|
9451
|
+
on Drawer:
|
|
8608
9452
|
|
|
8609
9453
|
\`\`\`html
|
|
8610
9454
|
<helix-drawer modal="false"></helix-drawer>
|
|
8611
9455
|
\`\`\`
|
|
8612
9456
|
|
|
8613
|
-
|
|
8614
|
-
|
|
8615
|
-
|
|
9457
|
+
\`"false"\` is the only value that turns it off. An empty value, or any other
|
|
9458
|
+
string, leaves it on. In JSX, \`modal={false}\` sets the property and does the
|
|
9459
|
+
same. Other features that are on by default turn off with an attribute named
|
|
9460
|
+
\`no-\`, such as \`no-copy\`.
|
|
8616
9461
|
|
|
8617
9462
|
## Declarative shadow DOM is opt-in
|
|
8618
9463
|
|
|
8619
|
-
The
|
|
8620
|
-
|
|
8621
|
-
Lit SSR yourself,
|
|
8622
|
-
[Server rendering](./server-rendering).
|
|
8623
|
-
|
|
8624
|
-
The site ships no DSD for three reasons:
|
|
9464
|
+
The elements need no declarative shadow DOM (DSD). Each element attaches its
|
|
9465
|
+
shadow root and loads its styles with its definition, so you do not need to
|
|
9466
|
+
configure anything. If you render with Lit SSR yourself, the DSD it writes
|
|
9467
|
+
hydrates. See [Server rendering](./server-rendering).
|
|
8625
9468
|
|
|
8626
|
-
|
|
8627
|
-
|
|
8628
|
-
|
|
8629
|
-
|
|
8630
|
-
hook without an error.
|
|
9469
|
+
DSD does not work in a React server component. React inserts a server
|
|
9470
|
+
component's output by replaying the flight payload through DOM APIs, without
|
|
9471
|
+
running the component again, and DSD does not activate for nodes that a script
|
|
9472
|
+
inserts.
|
|
8631
9473
|
`,
|
|
8632
9474
|
versioning: `---
|
|
8633
9475
|
description: What a Helix version number means, and which packages are released together.
|
|
@@ -8662,13 +9504,13 @@ the same version were tested together.
|
|
|
8662
9504
|
|
|
8663
9505
|
## The version number comes from the Git tag
|
|
8664
9506
|
|
|
8665
|
-
|
|
8666
|
-
|
|
9507
|
+
CI computes each version from the Git tags and writes it into every package
|
|
9508
|
+
manifest seconds before packing. Once the release is promoted, CI commits the
|
|
9509
|
+
same number back to \`main\`. The \`version\` in each Helix \`package.json\` in the
|
|
9510
|
+
repository is therefore the last release, and the tag is the record it copies.
|
|
8667
9511
|
|
|
8668
|
-
|
|
8669
|
-
|
|
8670
|
-
with push access that overrides branch protection, and \`git tag\` already records
|
|
8671
|
-
the number.
|
|
9512
|
+
The Rust crates stay at \`0.0.0-dev\`. The \`helix\` binary reports the version of
|
|
9513
|
+
the npm package it was installed with.
|
|
8672
9514
|
|
|
8673
9515
|
Releases use two kinds of tag:
|
|
8674
9516
|
|
|
@@ -8700,8 +9542,15 @@ one of:
|
|
|
8700
9542
|
- \`stable\`
|
|
8701
9543
|
- \`deprecated\`
|
|
8702
9544
|
|
|
8703
|
-
Most components are experimental
|
|
8704
|
-
|
|
9545
|
+
Most components are stable. These families are experimental, and their status
|
|
9546
|
+
says so:
|
|
9547
|
+
|
|
9548
|
+
- the spatial charts, which draw in three dimensions
|
|
9549
|
+
- Log
|
|
9550
|
+
- Strands
|
|
9551
|
+
- Motion Toggle
|
|
9552
|
+
|
|
9553
|
+
We will not change a status to make the library look more finished than it is.
|
|
8705
9554
|
|
|
8706
9555
|
## Release notes
|
|
8707
9556
|
|
|
@@ -8739,7 +9588,7 @@ accept a subset of an axis's values.
|
|
|
8739
9588
|
tone="primary"
|
|
8740
9589
|
>Save</helix-button>
|
|
8741
9590
|
<helix-card separation="outlined">…</helix-card>
|
|
8742
|
-
<helix-
|
|
9591
|
+
<helix-drawer material="glass">…</helix-drawer>
|
|
8743
9592
|
\`\`\`
|
|
8744
9593
|
|
|
8745
9594
|
## Why there is no \`appearance\` attribute
|
|
@@ -8763,9 +9612,16 @@ build.
|
|
|
8763
9612
|
## When a property needs two axes
|
|
8764
9613
|
|
|
8765
9614
|
**If two values can apply at the same time, they belong on two axes.**
|
|
8766
|
-
\`
|
|
8767
|
-
|
|
8768
|
-
|
|
9615
|
+
\`Card\` is the example. \`separation\` sets where its surface ends, and \`material\`
|
|
9616
|
+
sets what fills it. A card can be outlined and glass at once, so the two values
|
|
9617
|
+
sit on two axes:
|
|
9618
|
+
|
|
9619
|
+
\`\`\`html
|
|
9620
|
+
<helix-card
|
|
9621
|
+
material="glass"
|
|
9622
|
+
separation="outlined"
|
|
9623
|
+
>…</helix-card>
|
|
9624
|
+
\`\`\`
|
|
8769
9625
|
|
|
8770
9626
|
If you are adding a property and cannot decide which axis it belongs on, it is
|
|
8771
9627
|
usually two properties.
|
|
@@ -9141,6 +9997,23 @@ Helix tag in an SFC template has typed props and events. The generated file
|
|
|
9141
9997
|
declares the augmentation under both \`vue\` and \`@vue/runtime-core\`, because
|
|
9142
9998
|
different tools read different ones.
|
|
9143
9999
|
|
|
10000
|
+
## Report a misspelled prop
|
|
10001
|
+
|
|
10002
|
+
\`vue-tsc\` checks the value of each prop a Helix tag declares. By default, it
|
|
10003
|
+
does not report a prop name that the tag does not declare, so a misspelled prop
|
|
10004
|
+
compiles. To report one, turn on \`checkUnknownProps\` in \`tsconfig.json\`:
|
|
10005
|
+
|
|
10006
|
+
\`\`\`json
|
|
10007
|
+
{
|
|
10008
|
+
"vueCompilerOptions": {
|
|
10009
|
+
"checkUnknownProps": true
|
|
10010
|
+
}
|
|
10011
|
+
}
|
|
10012
|
+
\`\`\`
|
|
10013
|
+
|
|
10014
|
+
\`strictTemplates\` turns it on with the rest of Vue's strict template checks.
|
|
10015
|
+
Either option also reports a \`data-*\` attribute on a Helix tag.
|
|
10016
|
+
|
|
9144
10017
|
## Mark Helix tags as custom elements
|
|
9145
10018
|
|
|
9146
10019
|
Without this setting, Vue treats \`<helix-button>\` as a component it cannot find
|
|
@@ -9195,7 +10068,7 @@ work. See [State and control](./state-and-control).
|
|
|
9195
10068
|
<helix-input default-value="Ada" label="Name" />
|
|
9196
10069
|
|
|
9197
10070
|
<!-- Mirrored: bind the notification and let the element apply. -->
|
|
9198
|
-
<helix-input label="Name" @change="name = $event.
|
|
10071
|
+
<helix-input label="Name" @change="name = ($event.currentTarget as HelixInput).value" />
|
|
9199
10072
|
|
|
9200
10073
|
<!-- Refused: you take the decision for this one change. -->
|
|
9201
10074
|
<helix-input
|
|
@@ -9204,6 +10077,16 @@ work. See [State and control](./state-and-control).
|
|
|
9204
10077
|
@helix-before-change="$event.preventDefault(); name = clean($event.detail.value)"
|
|
9205
10078
|
/>
|
|
9206
10079
|
\`\`\`
|
|
10080
|
+
|
|
10081
|
+
For a native event such as \`change\`, \`vue-tsc\` types \`$event.currentTarget\` as
|
|
10082
|
+
\`EventTarget | null\`, which has no \`value\`. The mirrored line casts it to the
|
|
10083
|
+
element's class, which you import as a type from the family's subpath:
|
|
10084
|
+
|
|
10085
|
+
\`\`\`vue
|
|
10086
|
+
<script setup lang="ts">
|
|
10087
|
+
import type { HelixInput } from '@fusion.dev/helix/input'
|
|
10088
|
+
</script>
|
|
10089
|
+
\`\`\`
|
|
9207
10090
|
`,
|
|
9208
10091
|
'without-a-bundler': `---
|
|
9209
10092
|
description: Use the Helix styles on a page with no build step, and why the elements need a bundler for now.
|
|
@@ -9239,10 +10122,10 @@ own URL. Copy the whole \`dist\` folder and link \`helix.css\`. We checked this
|
|
|
9239
10122
|
Chromium: one \`<link>\` makes five requests, and every layer resolves.
|
|
9240
10123
|
|
|
9241
10124
|
*Until August 24, 2026, this page listed \`tokens.css\` and \`base.css\` without
|
|
9242
|
-
\`primitives.css\`. \`tokens.css\` refers to
|
|
9243
|
-
nothing. If you followed the old instructions,
|
|
9244
|
-
|
|
9245
|
-
|
|
10125
|
+
\`primitives.css\`. \`tokens.css\` refers to the primitives throughout and imports
|
|
10126
|
+
nothing. If you followed the old instructions, every semantic token is declared
|
|
10127
|
+
and every color resolves to \`rgba(0, 0, 0, 0)\`. If your page looks unstyled in
|
|
10128
|
+
that way, link \`helix.css\`.*
|
|
9246
10129
|
|
|
9247
10130
|
\`elements.css\` matters most on a page with no bundler. Without a bundler, the
|
|
9248
10131
|
module graph loads over the network, so the markup is on the page longer before
|
|
@@ -9255,9 +10138,11 @@ package made its import relative. Only a bundler resolves a bare specifier in an
|
|
|
9255
10138
|
|
|
9256
10139
|
\`@fusion.dev/helix\` imports its dependencies by bare specifiers, such as
|
|
9257
10140
|
\`@fusion.dev/helix-core\` and \`lit\`. A browser resolves a bare specifier only
|
|
9258
|
-
through an import map
|
|
9259
|
-
\`@fusion.dev/helix-headless
|
|
9260
|
-
|
|
10141
|
+
through an import map. The elements import a separate subpath of
|
|
10142
|
+
\`@fusion.dev/helix-headless\` for each family, and the package's \`exports\` maps
|
|
10143
|
+
each one to an \`index.js\` in a folder of its own, so an import map would need an
|
|
10144
|
+
entry for each one. A subpath of \`lit\` is the path of its file, so one \`"lit/"\` entry
|
|
10145
|
+
covers all of them.
|
|
9261
10146
|
|
|
9262
10147
|
To use the elements, load them through a bundler, as
|
|
9263
10148
|
[Installation](./installation) shows. A CDN that rewrites bare specifiers into
|