@fusion.dev/helix-docs 0.148.0 → 0.149.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/content/color.mdx CHANGED
@@ -67,6 +67,23 @@ The eight syntax roles for code are `callable`, `comment`, `constant`,
67
67
  `invalid`, `keyword`, `name`, `string`, and `tag`. A full editor theme has more
68
68
  roles. Helix has fewer because code in documentation only needs to be read.
69
69
 
70
+ ### Scrollbars
71
+
72
+ `Scroll Area`, `Chat`, and `Virtual List` color their scrollbars with three
73
+ tokens:
74
+
75
+ - `--helix-scrollbar-thumb-color` is the thumb at rest. It is a mid gray that
76
+ measures at least 3:1 against the page and every surface, in both modes.
77
+ - `--helix-scrollbar-thumb-hover-color` is the thumb while the pointer is over
78
+ its scroller. It is `foreground-subtle`.
79
+ - `--helix-scrollbar-track-color` is the track behind the thumb. It is
80
+ `transparent`, so the scroller's own ground shows through.
81
+
82
+ Each of the three reads its own hook first, such as `--scroll-area-thumb-color`
83
+ or `--chat-scrollbar-thumb-color`, so you can recolor one scroller and leave
84
+ the rest. Set the tokens to recolor all three. Other families that scroll keep
85
+ the browser's own scrollbar.
86
+
70
87
  ## Why colors use OKLCH
71
88
 
72
89
  Every Helix color is written in OKLCH. In sRGB, equal numeric steps do not look
@@ -65,6 +65,12 @@ A property that holds content, such as a tooltip's text, is the fallback content
65
65
  for a slot of the same name. Helix never renders both.
66
66
 
67
67
  ```html
68
+ <!-- The trigger, which names the tooltip in aria-describedby -->
69
+ <helix-button
70
+ aria-describedby="save-tip"
71
+ id="save"
72
+ >Save</helix-button>
73
+
68
74
  <!-- The property -->
69
75
  <helix-tooltip
70
76
  content="Save"
@@ -431,9 +431,40 @@ leaves removing it to your framework.
431
431
  - In the search field, Down moves into the list, and Escape empties the search.
432
432
  With `no-panel`, Escape on an empty search closes the list.
433
433
  - Screen readers read the selected note first, then how each other note links
434
- to it, such as "Linked from Roadmap", and the count as a search narrows it.
434
+ to it, such as "Linked from Roadmap". They hear the count when a search or
435
+ the legend changes it.
436
+ - After the arrow keys, plus, minus, or the buttons move the drawing, screen
437
+ readers hear which way it went, such as "Moved right" or "Zoomed in".
438
+ `connectGraph` tells the frame once the drawing has moved. If you answer
439
+ `helix-graph-pan` and `helix-graph-zoom` yourself, call `frame.moved()` once
440
+ your view has moved.
441
+ - When you change `nodes` or `links`, the list keeps the note the keys are on.
442
+ When that note is removed, the keys move to the note that took its row.
435
443
  - Change the words with `search-label`, `count-label`, `selected-label`,
436
- `links-to-label`, `linked-from-label`, and `linked-both-label`.
444
+ `links-to-label`, `linked-from-label`, `linked-both-label`,
445
+ `moved-up-label`, `moved-down-label`, `moved-left-label`,
446
+ `moved-right-label`, `zoomed-in-label`, and `zoomed-out-label`, or register
447
+ them for a language with `registerWords` (see
448
+ [Localization](./localization)).
449
+
450
+ ## Events
451
+
452
+ The frame sends these events. Their names follow Map's: `helix-graph-pan`,
453
+ `helix-graph-zoom`, `helix-graph-ready`, and `helix-graph-part-change` match
454
+ `helix-map-pan`, `helix-map-zoom`, `helix-map-ready`, and
455
+ `helix-map-part-change`. These are the names Graph keeps.
456
+
457
+ | Event | Sent when | Cancelable | Who listens |
458
+ |---|---|---|---|
459
+ | `helix-before-activate` | A note is about to be selected | Yes | You, to keep the selection in your own state |
460
+ | `helix-activate` | A note was selected, in the list or the drawing | No | You |
461
+ | `helix-graph-filter` | A group in the legend was pressed, before its notes are shown or hidden | Yes | You, to set `hiddenGroups` yourself |
462
+ | `helix-graph-layout` | A layout settled, with where it put each note | No | You, to keep the positions |
463
+ | `helix-graph-ready` | The graph has been drawn whole | No | You, to show the graph once it is finished |
464
+ | `helix-graph-pan` | An arrow key or a pan button asked the drawing to move | No | `connectGraph`, or your own renderer |
465
+ | `helix-graph-zoom` | Plus, minus, or a zoom button asked the drawing to zoom | No | `connectGraph`, or your own renderer |
466
+ | `helix-graph-part-change` | The notes, links, selection, depth, or gestures changed | No | `connectGraph` only. It stays on the frame. |
467
+ | `helix-graph-signal` | `signal()` was called | No | `connectGraph` only. It stays on the frame. |
437
468
 
438
469
  ## Write a driver
439
470
 
@@ -10,7 +10,7 @@ title: Iconography
10
10
  <helix-icon name="trash"></helix-icon>
11
11
  ```
12
12
 
13
- Helix has 125 glyphs, 120 of them from Phosphor, drawn on a 256 box at
13
+ Helix has 126 glyphs, 121 of them from Phosphor, drawn on a 256 box at
14
14
  Phosphor's bold weight. We keep Phosphor's own geometry. Rescaling to a 24 box
15
15
  would change the optical weight of every glyph, and the set would stop matching
16
16
  Phosphor. We use the bold weight because Phosphor's regular weight is a
@@ -90,7 +90,7 @@ both are set, `icon` wins.
90
90
 
91
91
  `icon` also keeps your bundle small. `name` looks up glyphs in an index of the
92
92
  whole set, and no bundler can tree-shake that index. If you render one icon by
93
- `name`, you ship the whole set: 17 kB gzipped for 125 glyphs. With `icon`, you
93
+ `name`, you ship the whole set: 17 kB gzipped for 126 glyphs. With `icon`, you
94
94
  ship only the glyph you import.
95
95
 
96
96
  `icon` also draws glyphs from outside this set. Anything that exports the same
@@ -1,5 +1,5 @@
1
1
  ---
2
- description: Set the locale of date, time, and number controls, translate component strings, and lay out pages right to left.
2
+ description: Set the locale of date, time, and number controls, translate the built-in words, and lay out pages right to left.
3
3
  section: Essentials
4
4
  title: Localization and RTL
5
5
  ---
@@ -104,27 +104,175 @@ Six families format numbers or dates and take a `locale`:
104
104
  ></helix-pagination>
105
105
  ```
106
106
 
107
- If you do not set `locale`, these controls use the browser's locale, which is
108
- usually correct. Set `locale` when your users choose your application's
107
+ If you do not set `locale`, these controls use the nearest `lang` attribute,
108
+ then the browser's locale, which is usually correct. Set `locale` when your users choose your application's
109
109
  language separately from the language of their operating system.
110
110
 
111
111
  The first day of the week comes from `Intl.Locale`'s week information, which
112
112
  Firefox does not provide. There, and where you set no `locale`, the week starts
113
113
  on Sunday. Set `first-day-of-week` to choose the day yourself.
114
114
 
115
- `locale` formats dates, times, and numbers. It does not translate the words a
116
- control speaks, such as "Previous month" on a calendar. Set those through their
117
- properties, such as `previous-label` and `next-label` on Date Picker and Date
118
- Input. A time wheel's name, such as "Hour", comes from the locale.
115
+ `locale` formats dates, times, and numbers. On a family that reads the words
116
+ registry, it also picks the words. On the others, set the words a control
117
+ speaks through their properties, such as `previous-label` and `next-label` on
118
+ Date Picker and Date Input. See
119
+ [Translate the built-in words](#translate-the-built-in-words). A time wheel's
120
+ name, such as "Hour", comes from the locale.
119
121
 
120
122
  Formatting uses `Intl`, so Helix bundles no locale data, and you have nothing to
121
123
  keep up to date.
122
124
 
123
- ## Translate component strings
125
+ ## Calendars
124
126
 
125
- More than a hundred properties in the headless tier set text that your users
126
- see. Examples include `pan-up-label` on a map and `remove-label` on a combobox's
127
- selected tags. Properties that combine values are format templates:
127
+ Date Picker, Date Input, and Format Date write dates in five calendars. Each
128
+ has the Gregorian months, so the days in a Date Picker are the same in all
129
+ five:
130
+
131
+ | Calendar | October 6, 2026 | The locale's own calendar for |
132
+ |---|---|---|
133
+ | `gregory` | October 6, 2026 | Most locales |
134
+ | `buddhist` | 6 ตุลาคม 2569 | `th-TH` |
135
+ | `japanese` | 令和8年10月6日 | `ja-JP-u-ca-japanese` |
136
+ | `roc` | 民國115年10月6日 | `zh-TW-u-ca-roc` |
137
+ | `iso8601` | 2026 October 6 | A locale written with `-u-ca-iso8601` |
138
+
139
+ If you do not set `calendar`, these components use the locale's own calendar
140
+ when it is one of the five. Set `calendar` to choose one yourself:
141
+
142
+ ```html
143
+ <helix-date-picker
144
+ calendar="buddhist"
145
+ locale="en-US"
146
+ ></helix-date-picker>
147
+ ```
148
+
149
+ **Every other calendar is written as Gregorian**, in the locale's language and
150
+ digits. This includes the Persian, Islamic, Hebrew, Ethiopic, Indian, Coptic,
151
+ Chinese, and Dangi calendars, whose months are not the Gregorian months. A page
152
+ in `fa-IR`, whose own calendar is Persian, shows October 2026 as "اکتبر ۲۰۲۶".
153
+
154
+ The value is `YYYY-MM-DD` in every calendar. In a `segmented` Date Input, the
155
+ segments hold the Gregorian day, month, and year, so a Buddhist date is typed
156
+ with the year 2026.
157
+
158
+ ## Translate the built-in words
159
+
160
+ Components have English words built in: the names a screen reader hears, the
161
+ sentences a live region says, and the text on buttons such as "Load more".
162
+ Register the words for a language once, and every element in that language
163
+ speaks them:
164
+
165
+ ```js
166
+ import { registerWords } from '@fusion.dev/helix'
167
+
168
+ registerWords('pl', {
169
+ 'helix-combobox': {
170
+ emptyText: 'Brak wyników',
171
+ resultsLabel:
172
+ '{count, plural, one {# wynik} few {# wyniki} many {# wyników} other {# wyniku}}',
173
+ },
174
+ 'helix-data-table': {
175
+ nextLabel: 'Następna strona',
176
+ previousLabel: 'Poprzednia strona',
177
+ },
178
+ })
179
+ ```
180
+
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. To type the
183
+ keys, import the generated types once:
184
+
185
+ ```ts
186
+ import type {} from '@fusion.dev/helix/words'
187
+ ```
188
+
189
+ Every family reads the registry for the words it declares, the words several
190
+ families share included. Register a shared word under each family's key: the
191
+ name of a dismiss button, `dismissLabel`, under `helix-callout`; a collection's
192
+ `emptyText` and `loadingText` under `helix-tree`; and the sentence a field
193
+ reports when a required value is missing, `valueMissingMessage`, under
194
+ `helix-checkbox`. A family that has its own English for a shared word, such as
195
+ Tag's `Remove {label}`, keeps it until you register another.
196
+
197
+ The name a family gives itself where nothing else names it is `fallbackLabel`
198
+ in every family, such as `{ 'helix-table': { fallbackLabel: 'Tabelle' } }`.
199
+
200
+ ### Which words an element uses
201
+
202
+ 1. **A value you set on the element**, as an attribute or a property. The
203
+ English counts as a value, so `empty-text="No matches"` keeps the English
204
+ on every page.
205
+ 2. The words registered for the element's locale.
206
+ 3. The English.
207
+
208
+ Set the property to `null`, or remove the attribute, to return to the
209
+ registered words. Reading the property gives the word the element uses, in its
210
+ locale.
211
+
212
+ ### Which locale an element speaks
213
+
214
+ 1. The element's `locale` property.
215
+ 2. The nearest `lang` attribute, read through shadow roots, such as
216
+ `<html lang="pl">`.
217
+ 3. The browser's language.
218
+
219
+ The same locale picks the words, the plural forms, and the way numbers are
220
+ written, so one sentence never mixes two languages.
221
+
222
+ A word that a language does not have falls back one key at a time, through
223
+ the regional, script, and language forms of the tag and then to English. A
224
+ `de-CH` page uses a `de-CH` word, then a `de` word, then the English. Hong Kong
225
+ (`zh-HK`) reaches a `zh-Hant` word before a `zh` word. Name a set of words by
226
+ its full tag where a language has regional forms, such as `pt-BR`, `pt-PT`, and
227
+ `es-419`.
228
+
229
+ When you register words, or change the `lang` of the `<html>` element, every
230
+ element on the page updates once. When you change the `lang` of another
231
+ element, call `refreshWords()`.
232
+
233
+ ### Write a message
234
+
235
+ Words are messages in a small part of the ICU MessageFormat syntax, which
236
+ translation tools read:
237
+
238
+ | Syntax | What it does |
239
+ |---|---|
240
+ | `{name}` | Puts in a value. A number is written in the locale, such as `1.200` in German. |
241
+ | `{p, number, percent}` | Writes a number as a percentage, with the sign where the locale puts it. `{n, number, integer}` rounds. |
242
+ | `{count, plural, one {# wynik} few {# wyniki} other {# wyniku}}` | Picks a form by the language's plural rules. `#` is the number. `=0 {...}` matches one exact number, and `offset:1` counts from one. |
243
+ | `{n, selectordinal, one {#st} two {#nd} few {#rd} other {#th}}` | Picks a form for a position. |
244
+ | `{direction, select, ascending {...} descending {...} other {...}}` | Picks a whole sentence by a value, so a language can phrase each one its own way. |
245
+ | `''` and `'{'` | Write one apostrophe, and a brace as text. A single apostrophe is text, so `L'élément` needs no escaping. |
246
+
247
+ Each property's reference entry names its placeholders. A message that does
248
+ not parse shows as written, so a typo appears on the page where you can see it.
249
+
250
+ `formatMessage(message, values, locale)` formats a message for your own
251
+ elements, and `wordsLocale(element)` gives the locale an element speaks.
252
+
253
+ ### Server rendering
254
+
255
+ A server knows the element's own attributes and the words your server code
256
+ registered. It does not know the page around the element. So on a server, and
257
+ in the first render while an element hydrates, a word uses the element's
258
+ `locale`, then its own `lang`, then the locale you set with
259
+ `setDefaultWordsLocale`, then English. After hydration, the element reads the
260
+ nearest `lang` and updates once if its words change.
261
+
262
+ - Register the same words on the server and in the browser, before the
263
+ elements are defined, so the two renders agree.
264
+ - A server that renders one language calls `setDefaultWordsLocale('pl')` once.
265
+ - A server that renders several languages sets `lang` or `locale` on the
266
+ elements.
267
+
268
+ The registry holds no current language, so two requests in two languages in
269
+ one process do not affect each other.
270
+
271
+ ### Set the words of other families
272
+
273
+ A value set on the element always wins, so any family takes its words as
274
+ properties too, such as `empty-text` on a listbox. Properties that combine
275
+ values are format templates:
128
276
 
129
277
  ```html
130
278
  <helix-pagination-summary
@@ -156,7 +304,8 @@ or `disable-times` rules out.
156
304
  A message appears in the browser's own validation bubble and on the line under
157
305
  the field. `feedback` does not reach the bubble. If you do not set a message, a
158
306
  text field shows the browser's own message, which is already in your user's
159
- language. Every other control shows its English sentence.
307
+ language. Every other control shows its English sentence. An empty message
308
+ counts as not set.
160
309
 
161
310
  ```html
162
311
  <helix-number-input
@@ -178,9 +327,8 @@ Aria's `@react-stately/color` translates, so you can reuse that table.
178
327
  ></helix-color-picker>
179
328
  ```
180
329
 
181
- **Pass these strings from your own translation system.** Helix ships English
182
- defaults and no translation tools. If Helix had its own string catalog, you
183
- would have to keep two catalogs in step.
330
+ Helix ships English words and no translations. Pass the words from your own
331
+ translation system, either to `registerWords` or to the properties.
184
332
 
185
333
  ## What to check when you add a language
186
334
 
@@ -212,9 +360,9 @@ would have to keep two catalogs in step.
212
360
 
213
361
  Helix does not include:
214
362
 
215
- - a message catalog
216
- - a pluralization helper
363
+ - translations of its words into other languages
364
+ - a message catalog for your own application's text
217
365
  - a date-range formatter, beyond what the controls format themselves
218
366
 
219
- These belong to your application's i18n layer. You can set every translatable
220
- property from that layer.
367
+ These belong to your application's i18n layer. You can register every word,
368
+ and set every translatable property, from that layer.
package/content/maps.mdx CHANGED
@@ -327,7 +327,8 @@ frame with the marker's `name`, or its `label` if it has no name.
327
327
 
328
328
  To show that a place has been visited, add `passed`. The marker is drawn in a
329
329
  muted color, and screen readers hear its name followed by "passed". Use
330
- `passed-label` to change that word.
330
+ `passed-label` to change that sentence, where `{label}` is the place's name,
331
+ such as `{label} (visited)`.
331
332
 
332
333
  ### Clusters
333
334