@fusion.dev/helix-docs 0.206.0 → 0.207.1
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 +68 -14
- 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 +1346 -459
- package/package.json +13 -12
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
---
|
|
2
|
+
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.
|
|
3
|
+
section: Tooling
|
|
4
|
+
title: Diagnostics
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Diagnostics
|
|
8
|
+
|
|
9
|
+
Helix diagnostics finds the mistakes in your markup and styles that the browser
|
|
10
|
+
accepts without an error. A `::part()` that names a part nothing publishes
|
|
11
|
+
matches nothing, an attribute value outside the set a component takes falls
|
|
12
|
+
back to the default, and `disabled="false"` turns `disabled` on. Each one is
|
|
13
|
+
valid HTML or CSS, so nothing else reports it.
|
|
14
|
+
|
|
15
|
+
Two tools check your project, in two places:
|
|
16
|
+
|
|
17
|
+
- **`initDiagnostics()`**, from `@fusion.dev/helix-diagnostics`, checks each
|
|
18
|
+
page as it runs in development. It prints each finding in DevTools, and a
|
|
19
|
+
badge in the corner of the page lists them.
|
|
20
|
+
- **`helix scan`**, from `@fusion.dev/helix-cli`, reads every stylesheet,
|
|
21
|
+
template, and module in your project, and prints each finding with its file
|
|
22
|
+
and line.
|
|
23
|
+
|
|
24
|
+
Both judge your code against the lint data of the Helix version you installed.
|
|
25
|
+
A fault that both find has the same kind and the same fingerprint in each, so a
|
|
26
|
+
report can join a page's findings to the scan's.
|
|
27
|
+
|
|
28
|
+
## Start diagnostics in your app
|
|
29
|
+
|
|
30
|
+
```sh
|
|
31
|
+
npm install --save-dev @fusion.dev/helix-diagnostics
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Call `initDiagnostics()` once, before your app mounts. Put the call in a module
|
|
35
|
+
of its own, so a production build can leave it out:
|
|
36
|
+
|
|
37
|
+
```ts
|
|
38
|
+
// src/diagnostics.ts
|
|
39
|
+
import { initDiagnostics } from '@fusion.dev/helix-diagnostics'
|
|
40
|
+
import lint from '@fusion.dev/helix/lint.json'
|
|
41
|
+
|
|
42
|
+
initDiagnostics({ environment: 'development', tiers: [{ lint }] })
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
`tiers` passes the lint data from your own `node_modules`, so diagnostics loads
|
|
46
|
+
nothing from the network. Without it, diagnostics loads the lint data for the
|
|
47
|
+
version on the page from a public npm CDN.
|
|
48
|
+
|
|
49
|
+
An add-on package publishes the lint data for its own elements, and you pass it
|
|
50
|
+
in `tiers` the same way. `@fusion.dev/helix-sandbox` publishes it for
|
|
51
|
+
`helix-sandbox-editor`:
|
|
52
|
+
|
|
53
|
+
```ts
|
|
54
|
+
import lint from '@fusion.dev/helix/lint.json'
|
|
55
|
+
import sandbox from '@fusion.dev/helix-sandbox/lint.json'
|
|
56
|
+
|
|
57
|
+
initDiagnostics({ environment: 'development', tiers: [{ lint }, { lint: sandbox }] })
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
The CDN serves the two tiers' lint data alone, so an add-on's elements are
|
|
61
|
+
checked only where its lint data is in `tiers`. Without it, each of them is an
|
|
62
|
+
unknown tag.
|
|
63
|
+
|
|
64
|
+
Load the module in development only. In Vite, with React or any other
|
|
65
|
+
framework, import it from your entry:
|
|
66
|
+
|
|
67
|
+
```tsx
|
|
68
|
+
// src/main.tsx
|
|
69
|
+
if (import.meta.env.DEV) {
|
|
70
|
+
void import('./diagnostics.js')
|
|
71
|
+
}
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
`vite build` replaces `import.meta.env.DEV` with `false` and drops the import
|
|
75
|
+
with it. In Next.js, import it from `instrumentation-client.ts`, which runs in
|
|
76
|
+
the browser before your app hydrates:
|
|
77
|
+
|
|
78
|
+
```ts
|
|
79
|
+
// instrumentation-client.ts
|
|
80
|
+
if (process.env.NODE_ENV === 'development') {
|
|
81
|
+
void import('./app/diagnostics')
|
|
82
|
+
}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Every Helix template starts diagnostics this way. Angular, Svelte, Vue, Solid,
|
|
86
|
+
and a page with no bundler call it the same way, and the package's README says
|
|
87
|
+
where each one's entry is.
|
|
88
|
+
|
|
89
|
+
From then on, diagnostics runs after the page loads, after a navigation, and
|
|
90
|
+
after the page changes. It prints each finding once per page, and the badge
|
|
91
|
+
shows only while the page has findings. Hover over a finding in the badge's
|
|
92
|
+
list to outline the element it is about.
|
|
93
|
+
|
|
94
|
+
## Print the findings in your terminal
|
|
95
|
+
|
|
96
|
+
A page cannot write to your terminal, so the Helix command line receives each
|
|
97
|
+
run beside your dev server. Install it, and turn the relay on where you call
|
|
98
|
+
`initDiagnostics()`:
|
|
99
|
+
|
|
100
|
+
```sh
|
|
101
|
+
npm install --save-dev @fusion.dev/helix-cli
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
```ts
|
|
105
|
+
initDiagnostics({
|
|
106
|
+
environment: 'development',
|
|
107
|
+
sinks: { relay: true },
|
|
108
|
+
tiers: [{ lint }],
|
|
109
|
+
})
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Then run the relay in your project's directory, beside the dev server:
|
|
113
|
+
|
|
114
|
+
```sh
|
|
115
|
+
npx helix diagnostics
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
Each Helix template has the script for it, so `npm run diagnostics` starts it
|
|
119
|
+
there. The command prints each finding the first time a page has it. It listens
|
|
120
|
+
on `127.0.0.1:7417`, takes runs only from pages on a local host, and serves the
|
|
121
|
+
`helix-diagnostics.json` in the directory it runs in to the page.
|
|
122
|
+
|
|
123
|
+
## Scan your code
|
|
124
|
+
|
|
125
|
+
```sh
|
|
126
|
+
npx helix scan
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
`helix scan` judges your code against the lint data of every Helix package you
|
|
130
|
+
install that publishes `lint.json`, the two tiers and an add-on such as
|
|
131
|
+
`@fusion.dev/helix-sandbox`. It reads every file `.gitignore` leaves in, apart
|
|
132
|
+
from tests and the folders a build writes. It reads CSS, Sass, and Less; HTML, Vue, Svelte,
|
|
133
|
+
and Astro templates; MDX; and JSX, Lit's `html` and `css` templates, and
|
|
134
|
+
Angular's inline templates in JavaScript and TypeScript. Each finding has its
|
|
135
|
+
file and line:
|
|
136
|
+
|
|
137
|
+
```
|
|
138
|
+
helix scan: 1 error, 0 warnings, 0 info, and 0 unsettled, in 42 files
|
|
139
|
+
error nested-interactive <helix-button> <helix-icon-button>
|
|
140
|
+
`helix-icon-button` is inside `helix-button`, which draws its content inside a native button. …
|
|
141
|
+
in src/toolbar.tsx:12
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
- **It reads every route.** A page sees only the routes someone opened.
|
|
145
|
+
- **What only a page can settle is listed as `unsettled`**, such as a hook set
|
|
146
|
+
on a tag that does not have it, which works where an element inside the tag
|
|
147
|
+
has it. An unsettled finding never fails the scan.
|
|
148
|
+
- **It checks an attribute where your code writes it as one.**
|
|
149
|
+
`disabled="false"` is checked. `?disabled=${false}` in Lit, `:disabled="false"`
|
|
150
|
+
in Vue, and `disabled={false}` in JSX set the value at run time, and the page
|
|
151
|
+
judges what they write.
|
|
152
|
+
- **`--file <path>` reports one file's findings**, and still reads the whole
|
|
153
|
+
project, so an import in another file counts. `--stdin <language>` checks a
|
|
154
|
+
snippet.
|
|
155
|
+
|
|
156
|
+
It exits 1 on a finding at `error`, and 3 where it cannot run, such as where no
|
|
157
|
+
installed Helix package has lint data.
|
|
158
|
+
|
|
159
|
+
## Run the checks in CI
|
|
160
|
+
|
|
161
|
+
`--out` writes a run to a CI file, and each command keeps the runs the others
|
|
162
|
+
wrote there, so one file holds your code's run and your pages' runs:
|
|
163
|
+
|
|
164
|
+
```sh
|
|
165
|
+
npx helix scan --out reports/helix-diagnostics.json
|
|
166
|
+
npx helix diagnostics --once http://localhost:4173/ --out reports/helix-diagnostics.json
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
`helix diagnostics --once <url>` opens a page headless, runs diagnostics on it
|
|
170
|
+
once the page holds still, prints each finding, and exits. Repeat `--once` for
|
|
171
|
+
each page. It needs Playwright installed beside `@fusion.dev/helix-cli`, and
|
|
172
|
+
adds nothing to your app.
|
|
173
|
+
|
|
174
|
+
Where your own end-to-end tests open the pages, gather their runs with
|
|
175
|
+
`@fusion.dev/helix-diagnostics/ci`:
|
|
176
|
+
|
|
177
|
+
```ts
|
|
178
|
+
import { ciSink } from '@fusion.dev/helix-diagnostics/ci'
|
|
179
|
+
|
|
180
|
+
// `runs` are the reports diagnostics returned in each page your job opened.
|
|
181
|
+
const { code, summary } = ciSink(runs, { file: 'reports/helix-diagnostics.json' })
|
|
182
|
+
console.log(summary)
|
|
183
|
+
process.exitCode = code
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
A fault found on many pages is one line, with the pages it was on. A finding at
|
|
187
|
+
`error` fails the job. `failOn: 'warn'` fails it on warnings too, and
|
|
188
|
+
`failOn: 'never'` reports without failing. `ciSink` writes the whole file, so
|
|
189
|
+
run `helix scan --out` after it to add your code's run.
|
|
190
|
+
|
|
191
|
+
## What it finds
|
|
192
|
+
|
|
193
|
+
| Kind | Severity | Found by | Means |
|
|
194
|
+
| --- | --- | --- | --- |
|
|
195
|
+
| `dead-hook` | error | both | A hook is set where nothing answers it, or a name close to a hook is set that nothing reads |
|
|
196
|
+
| `dead-part` | error | both | A `::part()` names a part the element does not publish, or follows another `::part()` |
|
|
197
|
+
| `disabled-attribute` | error | scan | A rule styles a form control's `[disabled]`, which a control a `<fieldset disabled>` disables does not carry. Write `:disabled` |
|
|
198
|
+
| `duplicate-registration` | error | page | Two copies of Helix are on the page |
|
|
199
|
+
| `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 |
|
|
200
|
+
| `invalid-value` | error | both | An attribute with a closed set of values carries one outside it, so the element falls back to its default |
|
|
201
|
+
| `manifest-skew` | error | page | The lint data is from another version than the page runs, so no other finding is reported |
|
|
202
|
+
| `native-element` | warn | scan | A native element is written where an installed Helix family stands in for it, such as `<button>` for `helix-button` |
|
|
203
|
+
| `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>` |
|
|
204
|
+
| `primitive-token` | error | scan | A rule reads a `--_helix-` primitive, which is internal to `@fusion.dev/helix-styles` |
|
|
205
|
+
| `unforwarded-part` | warn | both | The headless tag publishes a part the styled tag does not forward, which is a gap in Helix |
|
|
206
|
+
| `unimported-tag` | warn | scan | An element is written, and nothing in the project imports the entry that registers it |
|
|
207
|
+
| `unknown-attribute` | error | both | A Helix element carries an attribute it does not take |
|
|
208
|
+
| `unknown-event` | error | scan | A React `on` prop names an event the element does not dispatch |
|
|
209
|
+
| `unknown-slot` | error | scan | A child names a slot its Helix element does not have, so it is not drawn |
|
|
210
|
+
| `unknown-tag` | error | both | A rule or an element names a tag no installed package lists |
|
|
211
|
+
| `unknown-token` | error | scan | A rule reads or sets a `--helix-` name the token set does not have |
|
|
212
|
+
| `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 |
|
|
213
|
+
|
|
214
|
+
**A button inside a button is judged where the markup settles it.** Button,
|
|
215
|
+
Icon Button, Copy Button, Toggle, Motion Toggle, Radio, and Tab draw their
|
|
216
|
+
content inside a native button, and Skip Link draws its content inside a link.
|
|
217
|
+
A slot for actions, such as a card's `footer-actions`, is not inside anything
|
|
218
|
+
pressable. A framework's component, such as a router's `<Link>`, places its
|
|
219
|
+
children itself, so `helix scan` stops at it, and the page sees the `<a>` it
|
|
220
|
+
renders.
|
|
221
|
+
|
|
222
|
+
## Configure the checks
|
|
223
|
+
|
|
224
|
+
Both tools read `helix-diagnostics.json` from your project. The page cannot read
|
|
225
|
+
files, so import it and pass it as `file`, or let the relay serve it:
|
|
226
|
+
|
|
227
|
+
```json
|
|
228
|
+
{
|
|
229
|
+
"rules": {
|
|
230
|
+
"native-element": "off",
|
|
231
|
+
"unknown-tag": { "severity": "off", "tags": ["helix-mine"] }
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
A kind takes `error`, `warn`, `info`, or `off`, for every tag or for the tags
|
|
237
|
+
you name. A setting that names nothing is refused, with the closest one that
|
|
238
|
+
exists. Every setting is in the README of `@fusion.dev/helix-diagnostics`.
|
|
239
|
+
|
|
240
|
+
## Scope
|
|
241
|
+
|
|
242
|
+
The checks cover what the lint data can settle. They do not check:
|
|
243
|
+
|
|
244
|
+
- the value a token or a hook is set to, such as `--helix-radii-md: red`.
|
|
245
|
+
`helix scan` checks each `--helix-` name you write;
|
|
246
|
+
- a selector that descends into a component, such as `helix-card .title` or
|
|
247
|
+
`helix-button span`. It matches only the elements you put inside the
|
|
248
|
+
component;
|
|
249
|
+
- a `::part()` on a selector with no Helix tag in it, such as
|
|
250
|
+
`.cta::part(label)`, since the class names no element whose parts can be
|
|
251
|
+
read;
|
|
252
|
+
- an attribute value an expression sets, such as `tone={tone}` in JSX, in
|
|
253
|
+
`helix scan`. The page judges the attribute it writes;
|
|
254
|
+
- the contrast of a color you write yourself. `helix theme audit` checks the
|
|
255
|
+
contrast of your theme's tokens.
|
package/content/graphs.mdx
CHANGED
|
@@ -452,6 +452,12 @@ helix-graph:state(ready) > div {
|
|
|
452
452
|
}
|
|
453
453
|
```
|
|
454
454
|
|
|
455
|
+
Use the fade where the layout settles in a moment, such as a graph whose notes
|
|
456
|
+
all have places. Leave a cosmos.gl graph shown. Its layout runs for several
|
|
457
|
+
seconds by design, and the view follows the notes as they spread, so the reader
|
|
458
|
+
watches the graph settle. Faded in on the state, its drawing is hidden for that
|
|
459
|
+
whole time.
|
|
460
|
+
|
|
455
461
|
## Draw the graph before it loads
|
|
456
462
|
|
|
457
463
|
`graphSnapshot(nodes, links, options)` draws the graph on your server as an
|
package/content/iconography.mdx
CHANGED
|
@@ -167,21 +167,39 @@ bundler can remove that record.
|
|
|
167
167
|
an order of precedence, the glyph on the page would come from whichever
|
|
168
168
|
directory ranked higher, and nothing would look wrong.
|
|
169
169
|
|
|
170
|
-
##
|
|
170
|
+
## Draw your own glyphs by name
|
|
171
171
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
one line. You do not copy a path into a TypeScript object by hand.
|
|
172
|
+
`registerIcons` makes your glyphs resolve by `name`, so plain HTML can draw
|
|
173
|
+
them:
|
|
175
174
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
175
|
+
```ts
|
|
176
|
+
import { registerIcons } from '@fusion.dev/helix/icon'
|
|
177
|
+
import { peak } from './icons'
|
|
178
|
+
|
|
179
|
+
registerIcons([peak])
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
```html
|
|
183
|
+
<helix-icon name="peak"></helix-icon>
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
Each glyph registers under its own `name`. To register a whole set, build it
|
|
187
|
+
with `--index` and pass the `icons` record the command writes to
|
|
188
|
+
`registerIcons`.
|
|
189
|
+
|
|
190
|
+
`registerIconLibrary` resolves names under a prefix through a function of your
|
|
191
|
+
own, such as one that points into a sprite sheet or fetches from a server. After
|
|
192
|
+
`registerIconLibrary('brand', resolve)`, `name="brand:peak"` draws the glyph that
|
|
193
|
+
`resolve('peak')` returns.
|
|
194
|
+
|
|
195
|
+
To sit beside the Helix set, draw a glyph with the same geometry, a 256 box at
|
|
196
|
+
the bold weight. A glyph drawn at a different weight stands out next to the
|
|
197
|
+
others.
|
|
180
198
|
|
|
181
199
|
## Product marks
|
|
182
200
|
|
|
183
|
-
Helix and Cortex
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
201
|
+
The set includes the Fusion, Helix, and Cortex marks, and the React and HTML5
|
|
202
|
+
logos, so `<helix-icon name="helix">` draws the Helix mark. A mark identifies a
|
|
203
|
+
product. An icon stands for an action. **The Helix and Cortex marks are each one
|
|
204
|
+
rounded body with material removed.** Neither is a pictogram of a metaphor,
|
|
205
|
+
because each stands beside the Fusion mark, a geometric monogram.
|
package/content/installation.mdx
CHANGED
|
@@ -16,6 +16,24 @@ use its semantic tokens and draw nothing without them. If the elements are
|
|
|
16
16
|
installed and the tokens are missing, your page shows unstyled boxes. There is
|
|
17
17
|
no fallback theme.
|
|
18
18
|
|
|
19
|
+
## Start a new project
|
|
20
|
+
|
|
21
|
+
If you do not have a project yet, the scaffolder writes one with Helix
|
|
22
|
+
installed, the stylesheets imported, and every element registered:
|
|
23
|
+
|
|
24
|
+
```sh
|
|
25
|
+
npm create @fusion.dev/helix-app@latest my-app -- --template react-vite
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Leave out `--template`, and the command lists the templates and writes nothing.
|
|
29
|
+
Each template starts with the same three pages:
|
|
30
|
+
|
|
31
|
+
- a landing page
|
|
32
|
+
- a sorted, paged data table
|
|
33
|
+
- a form that a `<form>` reads with `FormData`
|
|
34
|
+
|
|
35
|
+
The rest of this page adds Helix to a project you already have.
|
|
36
|
+
|
|
19
37
|
## Import the stylesheets
|
|
20
38
|
|
|
21
39
|
```ts
|
|
@@ -49,18 +67,19 @@ import the two files you want and skip `helix.css`.
|
|
|
49
67
|
`tokens.css` refers to the primitives throughout, so without
|
|
50
68
|
`primitives.css` every semantic token resolves to nothing. We measured a page without it:
|
|
51
69
|
`--helix-color-background` computes to `rgba(0, 0, 0, 0)`, and so does every
|
|
52
|
-
other color. [Theming](theming
|
|
70
|
+
other color. [Theming](./theming) also depends on this file, because without
|
|
53
71
|
it, setting `--helix-seed-brand` changes nothing.
|
|
54
72
|
- **`tokens.css`** declares the semantic layer, the custom properties grouped
|
|
55
73
|
by category, plus `color-scheme: light dark` on `:root`. The files below read
|
|
56
74
|
from it, and it reads from `primitives.css`.
|
|
57
75
|
- **`base.css`** holds the element reset, the type scale on bare `h1` through
|
|
58
76
|
`h6`, and the recipes for markup that Helix does not render. It reads tokens.
|
|
59
|
-
- **`elements.css`**
|
|
60
|
-
often left out, because a page works without it until the page
|
|
61
|
-
server-rendered. An element that has not upgraded has no shadow root and no
|
|
62
|
-
styles of its own, so the markup flashes unstyled
|
|
63
|
-
|
|
77
|
+
- **`elements.css`** hides each Helix element until its definition loads. It is
|
|
78
|
+
the file most often left out, because a page works without it until the page
|
|
79
|
+
is server-rendered. An element that has not upgraded has no shadow root and no
|
|
80
|
+
styles of its own, so without the file the markup flashes unstyled. With it,
|
|
81
|
+
the element stays hidden until it upgrades, and the layout settles when it
|
|
82
|
+
does.
|
|
64
83
|
|
|
65
84
|
Import the files from a stylesheet you control, and keep them out of individual
|
|
66
85
|
components. The order then stays visible in one place.
|
|
@@ -91,13 +110,14 @@ Render one element and check whether it upgraded:
|
|
|
91
110
|
|
|
92
111
|
```ts
|
|
93
112
|
await customElements.whenDefined('helix-button')
|
|
94
|
-
console.log(document.querySelector('helix-button')?.shadowRoot
|
|
113
|
+
console.log(document.querySelector('helix-button')?.shadowRoot instanceof ShadowRoot)
|
|
95
114
|
```
|
|
96
115
|
|
|
97
116
|
`true` means the definition loaded and the element built its shadow root.
|
|
98
|
-
`false
|
|
99
|
-
|
|
100
|
-
the subpath of a component you did
|
|
117
|
+
`false` means the page has no `helix-button` to check. A promise that never
|
|
118
|
+
settles means the registration import did not run. Usually the import is outside
|
|
119
|
+
a `'use client'` boundary, or you imported the subpath of a component you did
|
|
120
|
+
not end up using.
|
|
101
121
|
|
|
102
122
|
If the element upgraded and the page still looks wrong, check the stylesheets
|
|
103
123
|
in the order above. The symptom tells you which layer is missing:
|
|
@@ -107,6 +127,10 @@ in the order above. The symptom tells you which layer is missing:
|
|
|
107
127
|
- Components that look right once JavaScript runs, but flash unstyled before
|
|
108
128
|
it, mean `elements.css` is missing.
|
|
109
129
|
|
|
130
|
+
To check the rest of your markup and styles against the Helix you installed,
|
|
131
|
+
start [Diagnostics](./diagnostics) in development, and run `helix scan` on your
|
|
132
|
+
code. Each template starts diagnostics for you.
|
|
133
|
+
|
|
110
134
|
## Framework setup
|
|
111
135
|
|
|
112
136
|
The elements are custom elements, and every framework can render them.
|
|
@@ -31,7 +31,7 @@ empty intent, `{}`, is a valid theme.
|
|
|
31
31
|
| `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 |
|
|
32
32
|
| `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 |
|
|
33
33
|
| `neutral` | The gray ramp, which can be tinted toward the brand. With `background`, it takes the page's tint |
|
|
34
|
-
| `status` | `info`, `success`, `warning`, and `danger`. A color pins one.
|
|
34
|
+
| `status` | `info`, `success`, `warning`, and `danger`. A color pins one. `{ hue }` moves its hue within the band that keeps its meaning |
|
|
35
35
|
| `chroma` | How saturated the palette is, independent of hue |
|
|
36
36
|
| `scheme` | `'light'` or `'dark'`, for a theme that has only one mode |
|
|
37
37
|
| `tokens` | Literal overrides by token name, for values no other field sets |
|
|
@@ -46,6 +46,17 @@ lightness moves only as far as the target needs.
|
|
|
46
46
|
`danger` rotated with the brand, your users would have to learn a new warning
|
|
47
47
|
color for each product.
|
|
48
48
|
|
|
49
|
+
**A status hue stays in its band.** Each band, in OKLCH degrees, covers the
|
|
50
|
+
colors that still read as that status. A hue outside its band is refused, and
|
|
51
|
+
the error names the band.
|
|
52
|
+
|
|
53
|
+
| Status | Hue band | Colors |
|
|
54
|
+
|---|---|---|
|
|
55
|
+
| `danger` | 15 to 40 | The reds, from crimson to red-orange |
|
|
56
|
+
| `warning` | 45 to 105 | Orange and amber to yellow |
|
|
57
|
+
| `success` | 125 to 180 | The greens, from yellow-green to teal-green |
|
|
58
|
+
| `info` | 200 to 280 | Cyan and blue to indigo |
|
|
59
|
+
|
|
49
60
|
`chroma` sets how saturated the theme feels. Monotone, muted, restrained, and
|
|
50
61
|
vivid all describe chroma, and none of them describes a hue. `chroma` has two
|
|
51
62
|
settings:
|
|
@@ -59,12 +70,20 @@ the gray nearest the page that meets the contrast target, which is a mid gray.
|
|
|
59
70
|
For black and white, leave out `brand`. Neither applies to a color you pinned. A
|
|
60
71
|
pinned color is used exactly as you set it.
|
|
61
72
|
|
|
73
|
+
`tokens` sets each value after the engine has solved the rest, so no other color
|
|
74
|
+
is solved against it. Each pair that names the token measures it, and a value
|
|
75
|
+
that misses its target is reported in `violations`. A name the engine does not
|
|
76
|
+
emit is added as a token of your own. No default pair names it, so only a pair
|
|
77
|
+
you list in `pairings` measures it. A value the engine cannot read is skipped: a
|
|
78
|
+
token the engine emits keeps its derived value, and a name it does not emit is
|
|
79
|
+
left out.
|
|
80
|
+
|
|
62
81
|
## Contrast
|
|
63
82
|
|
|
64
83
|
| Field | What it controls |
|
|
65
84
|
|---|---|
|
|
66
85
|
| `contrastTarget` | Target contrast for body text. 4.5 is WCAG AA, and 7 is AAA |
|
|
67
|
-
| `pairings` |
|
|
86
|
+
| `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` |
|
|
68
87
|
|
|
69
88
|
The default is 4.5, the WCAG 2.1 AA figure. The European Accessibility Act
|
|
70
89
|
refers to WCAG 2.1 AA, so 4.5 is the figure with legal weight.
|
|
@@ -118,8 +137,13 @@ you the trade-off that a theme with heavy material makes.
|
|
|
118
137
|
|
|
119
138
|
| Field | What it controls |
|
|
120
139
|
|---|---|
|
|
121
|
-
| `namespace` | The token prefix. Defaults to `helix` |
|
|
140
|
+
| `namespace` | The token prefix. Defaults to `helix`. `acme` emits `--acme-color-primary` |
|
|
122
141
|
|
|
123
142
|
Use `namespace` when your product ships its own token layer built on the
|
|
124
143
|
engine. The rest of the library still reads `--helix-*`, so a new namespace
|
|
125
144
|
changes only your own tokens.
|
|
145
|
+
|
|
146
|
+
**A namespace is ASCII letters, digits, hyphens, and underscores, with no
|
|
147
|
+
hyphen at either end.** The engine writes the hyphens around it. Any other
|
|
148
|
+
namespace is refused with the value it received, such as an empty one, `--acme`,
|
|
149
|
+
or `acme tokens`.
|
package/content/introduction.mdx
CHANGED
|
@@ -18,8 +18,9 @@ The components are custom elements, so you can use them in any framework or
|
|
|
18
18
|
without one.
|
|
19
19
|
|
|
20
20
|
**Helix computes your theme.** You enter a few inputs, such as your brand color,
|
|
21
|
-
and Helix computes every token from them.
|
|
22
|
-
|
|
21
|
+
and Helix computes every token from them. Helix measures the contrast of each
|
|
22
|
+
text and background pair it solves, and reports any pair that misses its target.
|
|
23
|
+
See [Theming](./theming).
|
|
23
24
|
|
|
24
25
|
**AI agents can read it.** A custom elements manifest describes every component,
|
|
25
26
|
with typed options, documented parts, and generated React prop names, so an
|
package/content/localization.mdx
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
description: Set the locale of date, time, and number controls, translate the built-in words,
|
|
2
|
+
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.
|
|
3
3
|
section: Essentials
|
|
4
4
|
title: Localization and RTL
|
|
5
5
|
---
|
|
@@ -163,7 +163,7 @@ Register the words for a language once, and every element in that language
|
|
|
163
163
|
speaks them:
|
|
164
164
|
|
|
165
165
|
```js
|
|
166
|
-
import { registerWords } from '@fusion.dev/helix'
|
|
166
|
+
import { registerWords } from '@fusion.dev/helix/words'
|
|
167
167
|
|
|
168
168
|
registerWords('pl', {
|
|
169
169
|
'helix-combobox': {
|
|
@@ -179,8 +179,12 @@ registerWords('pl', {
|
|
|
179
179
|
```
|
|
180
180
|
|
|
181
181
|
Each family's words are keyed by its tag, such as `helix-combobox`, and then
|
|
182
|
-
by the property name. The headless and styled tiers share one key.
|
|
183
|
-
|
|
182
|
+
by the property name. The headless and styled tiers share one key.
|
|
183
|
+
`@fusion.dev/helix/words` holds `registerWords` without the elements, so a page
|
|
184
|
+
can register its words before it loads any, and it types each key and property.
|
|
185
|
+
A page that uses only the headless tier imports
|
|
186
|
+
`@fusion.dev/helix-headless/words`. With `registerWords` imported from another
|
|
187
|
+
entry, import the types once:
|
|
184
188
|
|
|
185
189
|
```ts
|
|
186
190
|
import type {} from '@fusion.dev/helix/words'
|
|
@@ -330,6 +334,81 @@ Aria's `@react-stately/color` translates, so you can reuse that table.
|
|
|
330
334
|
Helix ships English words and no translations. Pass the words from your own
|
|
331
335
|
translation system, either to `registerWords` or to the properties.
|
|
332
336
|
|
|
337
|
+
## Rise and fall colors
|
|
338
|
+
|
|
339
|
+
Markets draw a price movement in different colors:
|
|
340
|
+
|
|
341
|
+
- Mainland China, Taiwan, and Japan draw a rise in red and a fall in green.
|
|
342
|
+
- Hong Kong, like most other markets, draws a rise in green and a fall in red.
|
|
343
|
+
|
|
344
|
+
Colors that show a status, such as an error or a warning, are the same in all
|
|
345
|
+
of these markets. Only a color that shows which way a value moved changes.
|
|
346
|
+
|
|
347
|
+
Two tokens hold these colors:
|
|
348
|
+
|
|
349
|
+
| Token | Default |
|
|
350
|
+
|---|---|
|
|
351
|
+
| `--helix-color-rise` | `--helix-color-success`, which is green |
|
|
352
|
+
| `--helix-color-fall` | `--helix-color-danger`, which is red |
|
|
353
|
+
|
|
354
|
+
`Trend` uses them when you set `color-by="movement"`. Without it, the color of a
|
|
355
|
+
trend follows `good`, which says whether the change is an improvement.
|
|
356
|
+
|
|
357
|
+
For a page that shows one of the markets that draw a rise in red, swap the two
|
|
358
|
+
tokens at the root of the page, and set `color-by` on each trend:
|
|
359
|
+
|
|
360
|
+
```css
|
|
361
|
+
:root,
|
|
362
|
+
[data-helix-theme] {
|
|
363
|
+
--helix-color-fall: var(--helix-color-success);
|
|
364
|
+
--helix-color-rise: var(--helix-color-danger);
|
|
365
|
+
}
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
```html
|
|
369
|
+
<helix-trend
|
|
370
|
+
color-by="movement"
|
|
371
|
+
format-options='{"style":"percent","maximumFractionDigits":2}'
|
|
372
|
+
value="0.0124"
|
|
373
|
+
>
|
|
374
|
+
<span slot="label">Shanghai Composite</span>
|
|
375
|
+
</helix-trend>
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
An element with `data-helix-theme` declares every token again for the part of
|
|
379
|
+
the page it themes, so the rule names `[data-helix-theme]` beside `:root`. To
|
|
380
|
+
color one part of a page, such as a panel of Shanghai prices on a page about
|
|
381
|
+
several markets, set the two tokens on that panel.
|
|
382
|
+
|
|
383
|
+
The two tokens point at the status colors, so after the swap each one still
|
|
384
|
+
measures at least 4.5:1 on the page and on every surface, in both modes. A
|
|
385
|
+
theme that changes `success` and `danger` changes them too.
|
|
386
|
+
|
|
387
|
+
**Helix does not set these from `lang`.** The language of a page does not name
|
|
388
|
+
its market. A page in Chinese for Hong Kong draws a rise in green, a `zh-Hant`
|
|
389
|
+
page with no region could be for Taiwan or for Hong Kong, and a page in English
|
|
390
|
+
can show Shanghai prices. If the language of your page does decide its market,
|
|
391
|
+
select on the language yourself:
|
|
392
|
+
|
|
393
|
+
```css
|
|
394
|
+
:is(:root, [data-helix-theme]):is(:lang(zh-CN), :lang(zh-TW), :lang(ja)) {
|
|
395
|
+
--helix-color-fall: var(--helix-color-success);
|
|
396
|
+
--helix-color-rise: var(--helix-color-danger);
|
|
397
|
+
}
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
A page in `zh-HK` matches none of these, so it keeps green for a rise.
|
|
401
|
+
|
|
402
|
+
Other components color a series by its place in the list, or by a tone you
|
|
403
|
+
name. To draw one in the color of a rise or a fall, set its own color to the
|
|
404
|
+
token, such as `--sparkline-color: var(--helix-color-rise)` on the sparkline of
|
|
405
|
+
a price that went up.
|
|
406
|
+
|
|
407
|
+
Sources:
|
|
408
|
+
|
|
409
|
+
- [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)
|
|
410
|
+
- [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)
|
|
411
|
+
|
|
333
412
|
## What to check when you add a language
|
|
334
413
|
|
|
335
414
|
- **Long strings.** Labels in German and Finnish are often long. A control sized
|
|
@@ -338,6 +417,9 @@ translation system, either to `registerWords` or to the properties.
|
|
|
338
417
|
buttons may now fit four.
|
|
339
418
|
- **The `lang` attribute.** Set it as well as translating the strings.
|
|
340
419
|
Hyphenation, quotation marks, and font fallback all depend on it.
|
|
420
|
+
- **A market's colors.** If the new language comes with a market that draws a
|
|
421
|
+
rise in red, swap the rise and fall tokens. See
|
|
422
|
+
[Rise and fall colors](#rise-and-fall-colors).
|
|
341
423
|
- **Numerals.** `Intl` formats the digits. A `%` or a currency symbol hardcoded
|
|
342
424
|
in your own markup does not move to the correct side.
|
|
343
425
|
- **Icons that point.** A caret that a component draws turns by itself. An icon
|