@sylwellsoftware/fray 0.5.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. package/README.md +198 -30
  2. package/colors/README.md +28 -13
  3. package/colors/gray/colors.css +54 -26
  4. package/colors/green/colors.css +54 -26
  5. package/colors/iceblue/colors.css +54 -26
  6. package/colors/ocean/colors.css +54 -26
  7. package/colors/orange/colors.css +54 -26
  8. package/colors/purple/colors.css +54 -26
  9. package/colors/red/colors.css +54 -26
  10. package/colors/yellow/colors.css +54 -26
  11. package/dist/Components/component.d.ts.map +1 -1
  12. package/dist/Components/data/descriptionList.d.ts.map +1 -1
  13. package/dist/Components/data/listview/listview.d.ts.map +1 -1
  14. package/dist/Components/data/table/DataTable.d.ts.map +1 -1
  15. package/dist/Components/data/table/FilterPanel.d.ts.map +1 -1
  16. package/dist/Components/data/table/TableHeaderCell.d.ts.map +1 -1
  17. package/dist/Components/data/treeview/treeview.d.ts.map +1 -1
  18. package/dist/Components/dialog/dialog.d.ts.map +1 -1
  19. package/dist/Components/layout/panel.d.ts +0 -1
  20. package/dist/Components/layout/panel.d.ts.map +1 -1
  21. package/dist/Components/layout/sidebar.d.ts +0 -1
  22. package/dist/Components/layout/sidebar.d.ts.map +1 -1
  23. package/dist/Components/layout/tabpanel/tab.d.ts +2 -0
  24. package/dist/Components/layout/tabpanel/tab.d.ts.map +1 -1
  25. package/dist/Components/layout/tabpanel/tabline.d.ts.map +1 -1
  26. package/dist/Components/layout/tabpanel/tabpanel.d.ts +9 -1
  27. package/dist/Components/layout/tabpanel/tabpanel.d.ts.map +1 -1
  28. package/dist/Components/lineinputs/checkbox/Checkbox.d.ts +0 -1
  29. package/dist/Components/lineinputs/checkbox/Checkbox.d.ts.map +1 -1
  30. package/dist/Components/lineinputs/dropdown.d.ts.map +1 -1
  31. package/dist/Components/lineinputs/radio.d.ts +51 -0
  32. package/dist/Components/lineinputs/radio.d.ts.map +1 -0
  33. package/dist/Components/lineinputs/textbox.d.ts.map +1 -1
  34. package/dist/Components/lineinputs/toggle.d.ts.map +1 -1
  35. package/dist/Components/menu/toolbar.d.ts +0 -1
  36. package/dist/Components/menu/toolbar.d.ts.map +1 -1
  37. package/dist/Components/status/progressBar.d.ts.map +1 -1
  38. package/dist/Components/theme/stylesheetPicker.d.ts.map +1 -1
  39. package/dist/index.d.ts +8 -0
  40. package/dist/index.d.ts.map +1 -1
  41. package/dist/index.js +2485 -1456
  42. package/dist/index.js.map +1 -1
  43. package/dist/{jsx-dev-runtime-pzseD2Sz.js → jsx-dev-runtime-Kf1Dhu-y.js} +412 -469
  44. package/dist/jsx-dev-runtime-Kf1Dhu-y.js.map +1 -0
  45. package/dist/jsx-dev-runtime.js +1 -1
  46. package/dist/jsx-runtime.js +1 -1
  47. package/dist/routing/RouteLink.d.ts +17 -0
  48. package/dist/routing/RouteLink.d.ts.map +1 -0
  49. package/dist/routing/RouteQuery.d.ts +18 -0
  50. package/dist/routing/RouteQuery.d.ts.map +1 -0
  51. package/dist/routing/RouteScope.d.ts +17 -0
  52. package/dist/routing/RouteScope.d.ts.map +1 -0
  53. package/dist/routing/RouteValue.d.ts +18 -0
  54. package/dist/routing/RouteValue.d.ts.map +1 -0
  55. package/dist/routing/navigationAdapter.d.ts +33 -0
  56. package/dist/routing/navigationAdapter.d.ts.map +1 -0
  57. package/dist/routing/route.d.ts +64 -0
  58. package/dist/routing/route.d.ts.map +1 -0
  59. package/dist/routing/router.d.ts +109 -0
  60. package/dist/routing/router.d.ts.map +1 -0
  61. package/dist/runtime.d.ts +5 -0
  62. package/dist/runtime.d.ts.map +1 -1
  63. package/dist/styling/baseStyleDefinitions.d.ts +0 -82
  64. package/dist/styling/baseStyleDefinitions.d.ts.map +1 -1
  65. package/dist/styling/theme.d.ts +3 -3
  66. package/dist/styling/theme.d.ts.map +1 -1
  67. package/package.json +3 -3
  68. package/styles/structural.css +220 -429
  69. package/themes/README.md +68 -66
  70. package/themes/dark.css +60 -1
  71. package/themes/java/theme.css +422 -135
  72. package/themes/light.css +60 -1
  73. package/themes/minimal/theme.css +420 -125
  74. package/themes/shiny/theme.css +492 -294
  75. package/dist/jsx-dev-runtime-pzseD2Sz.js.map +0 -1
package/README.md CHANGED
@@ -85,7 +85,8 @@ Responsibility stays at the narrowest layer that understands it:
85
85
  | Domain state, service implementations/providers, endpoint configuration, page composition, theme availability and selection policy | Application |
86
86
  | DOM structure, native events, accessibility, component lifetime, service-scope propagation, visual async states | Fray components/runtime |
87
87
  | Mutable/computed values, query timing/results, fetch state, optional causality | Glue |
88
- | URL/wire serialization and retrieval mechanism | Injected Glue query handler/application adapter |
88
+ | Remote wire serialization and retrieval mechanism | Injected Glue query handler/application adapter |
89
+ | Browser navigation placement, restoration, and routed component integration | Caller-owned Fray router with an injected navigation adapter |
89
90
  | Layout/flow CSS and stable component/part hooks | Fray structural styling |
90
91
  | Look-and-feel treatment and palette | Separately loaded Fray-compatible theme/color CSS |
91
92
 
@@ -214,6 +215,164 @@ query state comes from caller-owned endpoint results. Function components stay
214
215
  presentation-oriented and receive rendered values or emitters from a nearby
215
216
  lifecycle-owning class component.
216
217
 
218
+ ## Hierarchical browser routing
219
+
220
+ Routing is optional and application-scoped. Route descriptors name immutable
221
+ relative segments; the mounted component hierarchy supplies their parentage.
222
+ This lets a `TabPanel` discover immediate routes from ordinary `Tab`
223
+ annotations without requiring a duplicate central route tree:
224
+
225
+ ```tsx
226
+ import {Emitter} from '@sylwellsoftware/glue'
227
+ import {
228
+ Component,
229
+ RouteLink,
230
+ RouteQuery,
231
+ RouteUnavailableError,
232
+ RouteValue,
233
+ Tab,
234
+ TabPanel,
235
+ createBrowserRouter,
236
+ createHashNavigation,
237
+ createFrayRuntime,
238
+ defineRoute,
239
+ defineRouteParameter,
240
+ routeTarget,
241
+ stringRouteCodec,
242
+ waitForRouteValue,
243
+ } from '@sylwellsoftware/fray'
244
+
245
+ const routes = {
246
+ changes: defineRoute('changes'),
247
+ security: defineRoute('security'),
248
+ overview: defineRoute('security-overview', 'overview'),
249
+ projects: defineRoute('security-projects', 'projects'),
250
+ }
251
+ const activeApplication = new Emitter('changes')
252
+ const activeSecurityView = new Emitter('overview')
253
+
254
+ class SecurityApplication extends Component {
255
+ render() {
256
+ return <TabPanel valueEmitter={activeSecurityView} label="Security views">
257
+ <Tab id="overview" label="Overview" route={routes.overview}>Summary</Tab>
258
+ <Tab id="projects" label="Projects" route={routes.projects}>Projects</Tab>
259
+ </TabPanel>
260
+ }
261
+
262
+ static dependencies = [Tab, TabPanel]
263
+ }
264
+
265
+ class App extends Component {
266
+ render() {
267
+ return <>
268
+ <nav aria-label="Applications">
269
+ <RouteLink to={routeTarget(routes.security, routes.projects)}>
270
+ Security projects
271
+ </RouteLink>
272
+ </nav>
273
+ <TabPanel valueEmitter={activeApplication} label="Applications">
274
+ <Tab id="changes" label="Changes" route={routes.changes}>Changes</Tab>
275
+ <Tab id="security" label="Security" route={routes.security}>
276
+ <SecurityApplication />
277
+ </Tab>
278
+ </TabPanel>
279
+ </>
280
+ }
281
+
282
+ static dependencies = [RouteLink, SecurityApplication, Tab, TabPanel]
283
+ }
284
+
285
+ const router = createBrowserRouter({adapter: createHashNavigation(window)})
286
+ const runtime = createFrayRuntime({router})
287
+ const app = runtime.mount(runtime.create(App), document.querySelector('#app')!)
288
+
289
+ addEventListener('pagehide', () => {
290
+ app.destroy()
291
+ router.dispose()
292
+ }, {once: true})
293
+ ```
294
+
295
+ The runtime opens the root scope. A selected routed tab opens its own scope for
296
+ nested components, so `/security/projects` restores the outer tab first and
297
+ then discovers and restores the inner tab. Every scope declares when its
298
+ immediate registrations are complete; this distinguishes an unknown child
299
+ from one whose parent has not mounted yet. Duplicate IDs or literal paths and
300
+ multiple parameter routes in one scope fail at completion. Literal routes take
301
+ precedence over the optional parameter route.
302
+
303
+ `RouteLink` is a native anchor. A descriptor resolves against the current
304
+ lineage, which is convenient for sibling destinations. A link outside the
305
+ destination's mounted lineage uses `routeTarget(...)` with the full chain from
306
+ the root. Modified clicks, downloads, and non-`_self` targets retain native
307
+ browser behavior. `router.navigate(target)` pushes by default;
308
+ `router.redirect(target)` and `navigate(target, {history: 'replace'})` replace.
309
+ `router.href(target)`, `router.resolve(descriptor, context)`, and
310
+ `router.isActive(target, exact)` support advanced composition.
311
+
312
+ Dynamic path values use a typed codec and an application-owned nullable
313
+ emitter:
314
+
315
+ ```tsx
316
+ const projectRoute = defineRouteParameter('project', stringRouteCodec, 'project-id')
317
+ const selectedProjectId = new Emitter<string | null>(null)
318
+
319
+ <RouteValue
320
+ route={projectRoute}
321
+ valueEmitter={selectedProjectId}
322
+ resolve={async (projectId, _context, signal) => {
323
+ const projects = await waitForRouteValue(
324
+ projectResults,
325
+ (items) => items.length > 0,
326
+ signal,
327
+ )
328
+ if (!projects.some(({id}) => id === projectId)) {
329
+ throw new RouteUnavailableError(`Unknown project "${projectId}"`)
330
+ }
331
+ return projectId
332
+ }}
333
+ >
334
+ <ProjectView />
335
+ </RouteValue>
336
+ ```
337
+
338
+ Resolvers run in path order, may normalize the decoded value, and receive the
339
+ settled ancestor values plus an `AbortSignal`. A newer navigation or router
340
+ disposal aborts outstanding work. `waitForRouteValue` is a convenience for a
341
+ Glue readable; fetching, authorization, retries, and domain validation remain
342
+ application-owned. Set `scopeChildren` on `RouteValue` only when further route
343
+ levels live beneath the dynamic value. Leaving it off avoids remounting an
344
+ otherwise stable leaf view when selection changes.
345
+
346
+ Explicit query bindings make selected shareable view state routable without
347
+ coupling the originating control to the router:
348
+
349
+ ```tsx
350
+ <RouteQuery
351
+ name="range"
352
+ valueEmitter={historyRange}
353
+ codec={historyRangeCodec}
354
+ defaultValue="12m"
355
+ >
356
+ <HistoryView />
357
+ </RouteQuery>
358
+ ```
359
+
360
+ Defaults are omitted, owned names sort deterministically, foreign query keys
361
+ are preserved, and passive emitter changes replace the current URL. Invalid
362
+ owned values reset to the default and produce a structured `router.issue`.
363
+ Path failures similarly fall back to the deepest resolved parent and replace
364
+ the failed entry. Applications observe `router.transition` for pending/idle
365
+ state and render `router.issue` as localized, accessible feedback. An explicit
366
+ navigation clears the issue.
367
+
368
+ Choose `createHashNavigation(window)` for static hosting; it reserves the URL
369
+ fragment. Choose `createHistoryNavigation(window, {basePath: '/app/'})` for
370
+ ordinary paths; the deployment must serve the application entry point for
371
+ direct requests below that base. `MemoryNavigationAdapter` supplies
372
+ deterministic tests and does not imply server rendering. The application owns
373
+ and disposes the router; destroying a runtime root removes mounted route
374
+ registrations but does not dispose the caller-owned router.
375
+
217
376
  ## Component host elements
218
377
 
219
378
  Fray components with a wrapper render a standards-valid custom host element,
@@ -221,7 +380,7 @@ not a framework identity class. The default application runtime therefore
221
380
  produces DOM such as:
222
381
 
223
382
  ```html
224
- <fray-panel data-fray-component="panel">
383
+ <fray-panel class="panellike" data-fray-component="panel">
225
384
  <fray-textbox data-fray-component="textbox">
226
385
  <input type="text">
227
386
  </fray-textbox>
@@ -233,8 +392,10 @@ Native semantics remain native: `Button` renders `button`, `Toggle` renders
233
392
  `fieldset`, `Sidebar` renders `aside`, and table header components render
234
393
  `thead`/`th`. `Tab` is a declarative child consumed by `TabPanel` and has no
235
394
  independent root. The
236
- `data-fray-component` attribute keeps diagnostics unambiguous without taking
237
- ownership of the consumer's `class`/`className`.
395
+ `data-fray-component` keeps diagnostics unambiguous. It is not a structural or
396
+ theme selector. Fray may merge public presentation traits such as `panellike`
397
+ with an application-supplied `class`/`className`; those traits describe a
398
+ reusable capability, not component identity.
238
399
 
239
400
  Element naming is an immutable application-runtime setting:
240
401
 
@@ -538,6 +699,8 @@ sibling-local `key` through the common component props.
538
699
  | `Toolbar` | `label`, `orientation`, `id`, `children` | None | Stateless named toolbar; orientation is horizontal or vertical. |
539
700
  | `Textbox` | `label` or `ariaLabel`, value props, `disabled`, `required`, `readOnly`, `error`, native text constraints, `inputRef` | `onInput(value, event)`, `onChange(value, event)` | String `valueEmitter`; external emitter changes patch the native input without replacing it. |
540
701
  | `Dropdown<T>` | `options`, `label` or `ariaLabel`, value props, `disabled`, `required`, `error`, `placeholder`, `name` | `onChange(value, event)` | Typed string/number `valueEmitter`; `options` may be an array or readable emitter. |
702
+ | `RadioButton` | `label`, `name`, `value`, `checked`, `disabled`, `required` | `onChange(checked, event)` | Native radio input with a label; use a shared `name` to associate standalone buttons. |
703
+ | `RadioGroup<T>` | `options`, `label` or `ariaLabel`, value props, `name`, `disabled`, `required` | `onChange(value, event)` | Native radio inputs with one selected `valueEmitter` option. |
541
704
  | `Toggle<T>` | `options`, `label` or `ariaLabel`, value props, `disabled`, `required` | `onChange(value, event)` | One selected value; arrow keys, Home, and End move and select within the radio group. |
542
705
  | `Checkbox<T>` | `symbols`, `label`, value props, `disabled`, `required`, `name` | `onChange(value, event)` | Two-state semantic value by default; click/Space advances and arrow keys move in either direction. |
543
706
  | `TriCheckbox` | Checkbox props except `symbols` | `onChange(value, event)` | Cycles deny → neutral → prefer using `FilterMode`. |
@@ -549,9 +712,9 @@ sibling-local `key` through the common component props.
549
712
  | `ProgressBar` | `label`, `value` or `valueEmitter`, `max`, `valueText` | None | Labelled native progress; a null value is indeterminate. |
550
713
  | `ThemePicker` | `label` or `ariaLabel`, theme `options`, value props, `targetDocument`, `disabled` | `onChange(value, option, event)` | String `valueEmitter`; replaces only the theme stylesheet link. |
551
714
  | `ColorPicker` | `label` or `ariaLabel`, color `options`, value props, `targetDocument`, `disabled` | `onChange(value, option, event)` | String `valueEmitter`; replaces only the color stylesheet link. |
552
- | `Tab` | `id`, `label`, `disabled`, `children` | None | Declarative content marker consumed by `TabPanel`; not rendered as a tab by itself. |
715
+ | `Tab` | `id`, `label`, `disabled`, optional literal `route`, `children` | None | Declarative content marker consumed by `TabPanel`; a route annotation binds tab activation to the current route scope. |
553
716
  | `TabLine` | `tabs`, `label`, `baseId`, value props | `onChange(id, event)` | Active-tab `valueEmitter`; arrow keys skip disabled tabs, with Home/End support. |
554
- | `TabPanel` | `tabs` or `Tab` children, `label`, `id`, value props | `onChange(id, event)` | Owns or consumes the active-tab emitter and wires the selected tab to its tabpanel. |
717
+ | `TabPanel` | `tabs` or `Tab` children, `label`, `id`, value props | `onChange(id, event)` | Owns or consumes the active-tab emitter, wires the selected tabpanel, and contextually registers annotated tabs when a router is present. |
555
718
 
556
719
  Invalid option arrays, duplicate tab IDs, unsupported orientations, malformed
557
720
  emitters, and non-function callbacks fail with descriptive errors.
@@ -680,7 +843,7 @@ Fray's styling system has three physically and conceptually separate layers:
680
843
  | --- | --- | --- | --- |
681
844
  | Structure | `styles/structural.css` | Generated component layout, flow, sizing, positioning, accessibility mechanics, stable hooks, and variable consumption | Loaded once; remains stable during presentation changes |
682
845
  | Theme | `themes/<name>/theme.css` | Typography, spacing, geometry, depth, surface treatment, semantic family mappings, and exceptional pseudo/native rendering | Loaded separately and independently replaceable |
683
- | Colors | `colors/<name>/colors.css` | Palette primitives and semantic color roles | Loaded separately and independently replaceable |
846
+ | Colors | `colors/<name>/colors.css` | Primary, secondary, and neutral ramps plus contrast/color primitives; no UI-semantic roles | Loaded separately and independently replaceable |
684
847
 
685
848
  Component authors place only structure and mechanics in `static css`: display,
686
849
  flow, sizing, positioning, overflow, stable state hooks, and consumption of
@@ -708,7 +871,7 @@ CSS custom properties are Fray's primary theme integration protocol:
708
871
 
709
872
  ```text
710
873
  colors.css
711
- --fray-color-* palette/semantic roles
874
+ --palette-primary-* / --palette-secondary-* / --palette-neutral-*
712
875
 
713
876
 
714
877
  theme.css
@@ -732,39 +895,44 @@ that should look different. `frayThemeVariableCatalog` exports this contract in
732
895
  machine-readable form, including every variable's layer, family, value kind,
733
896
  purpose, and optional fallback.
734
897
 
735
- A custom component should consume the same hierarchy rather than forcing every
736
- theme to learn a new selector:
898
+ A custom component can apply the public trait matching the treatment it needs
899
+ and optionally consume the same variable hierarchy:
737
900
 
738
901
  ```css
739
- acme-grid > header {
740
- color: var(
741
- --acme-grid-header-color,
742
- var(--fray-table-header-color, var(--fray-header-color))
743
- );
744
- background: var(
745
- --acme-grid-header-background,
746
- var(--fray-table-header-background, var(--fray-header-background))
747
- );
902
+ acme-grid.datacomponentlike > header {
903
+ color: var(--table-header-color, var(--header-color));
904
+ background: var(--table-header-background, var(--header-background));
748
905
  }
749
906
  ```
750
907
 
751
- That component immediately follows compatible Fray themes while retaining a
752
- consumer/component-specific escape hatch.
753
-
754
- Variables are preferred, not mandatory. Some visual treatments require actual
755
- selectors and pseudo-elements. Shiny uses theme-scoped stable
756
- `data-fray-component`, `data-part`, role, and ARIA-state hooks to layer
757
- highlights over headers and button-like controls, draw custom dropdown arrows
758
- and checkbox surfaces, and style native progress pseudo-parts. These rules must
759
- not depend on application classes or generated IDs, must never intercept
760
- pointer events, and must yield to native representation under forced colors.
908
+ Complete traits use the `like` suffix. A component whose outer and content
909
+ regions are distinct uses `shell` and `inner`, for example `buttonshell` with
910
+ `buttoninner`, or `datacomponentshell` with `datacomponentinner`. Other public
911
+ families include `inputlike`, `headerlike`, `coloredlike`, `panellike`, and
912
+ `toolbarlike`; the panel and toolbar families also expose
913
+ `panelshell`/`panelinner` and `toolbarshell`/`toolbarinner`. Wrappers are not
914
+ introduced solely to carry a split trait.
915
+
916
+ Themes directly target native elements, public traits, native pseudo-parts,
917
+ and native/ARIA state. They never target `data-fray-component` or `data-part`.
918
+ Every theme seeds inherited variables with a zero-specificity
919
+ `:root`/`[data-theme]` boundary rule. Presentation selectors use `@scope` with
920
+ nested theme roots and `[data-theme-exclude]` limits, a named cascade layer,
921
+ and low-specificity `:where()` selectors. Shiny's highlights and
922
+ select/progress decoration follow that contract and yield to native
923
+ representation under forced colors.
924
+
925
+ `coloredlike` defaults to the primary palette. Components can provide
926
+ `--colored-base`, `--colored-light`, `--colored-dark`, and
927
+ `--colored-contrast`; the active theme decides whether those inputs become a
928
+ flat color, gradient, other polish, or no special treatment.
761
929
 
762
930
  ### Runtime selection
763
931
 
764
932
  `replaceFrayStylesheet` maintains one
765
933
  `link[data-fray-stylesheet="theme"]` and one
766
934
  `link[data-fray-stylesheet="colors"]`. Replacing either link also sets the
767
- corresponding `data-fray-theme` or `data-fray-color` root attribute. The
935
+ corresponding `data-theme` or `data-color` root attribute. The
768
936
  `ThemePicker` and `ColorPicker` controls expose the same operation through the
769
937
  normal Fray value-control contract.
770
938
 
package/colors/README.md CHANGED
@@ -1,15 +1,30 @@
1
1
  # Fray color palettes
2
2
 
3
- Each `<name>/colors.css` file defines the complete `--fray-color-*` contract
4
- without defining component geometry or treatment. It can therefore be replaced
5
- without rebuilding Fray components or replacing the active theme stylesheet.
6
-
7
- Palette files must provide canvas and surface colors, primary and muted text,
8
- ordinary and strong borders, primary/hover/active/soft accents, focus and
9
- selection colors, disabled colors, success/error roles, and highlight/shadow
10
- tints. The required list is available programmatically from
11
- `frayThemeVariableCatalog` entries whose `layer` is `color`.
12
-
13
- Palette names describe a color direction, not a theme. A theme may constrain
14
- which palettes it advertises, but the CSS contract itself keeps the two assets
15
- independently replaceable.
3
+ Each `<name>/colors.css` file defines a replaceable, prefix-free color
4
+ language. It contains palette values only; it does not decide what a panel,
5
+ button, input, selection, or application surface looks like.
6
+
7
+ Every palette supplies three complete ramps:
8
+
9
+ ```text
10
+ --palette-primary-{50,100,200,300,400,500,600,700,800,900,950}
11
+ --palette-secondary-{50,100,200,300,400,500,600,700,800,900,950}
12
+ --palette-neutral-{50,100,200,300,400,500,600,700,800,900,950}
13
+ ```
14
+
15
+ It also supplies `--palette-<family>`, `--palette-<family>-light`, and
16
+ `--palette-<family>-dark` aliases; `--palette-light`, `--palette-dark`,
17
+ `--palette-contrast-light`, and `--palette-contrast-dark`; and named hue
18
+ primitives such as `--palette-red` and `--palette-green`. Additional numeric
19
+ stops may be added without changing the existing contract.
20
+
21
+ Themes map these values to semantic variables. For example, a theme may map a
22
+ light-mode button to `--palette-neutral-100`, a selected item to
23
+ `--palette-primary-600`, and an error to `--palette-red`. A different theme can
24
+ use the same palette in entirely different places.
25
+
26
+ Palette declarations use a zero-specificity boundary selector for `:root` or
27
+ the matching `[data-color]` root. They select no descendants; the custom
28
+ properties propagate through normal inheritance and a nested color root seeds
29
+ its own palette. `frayThemeVariableCatalog` entries whose `layer` is `palette`
30
+ expose the machine-readable required vocabulary.
@@ -1,29 +1,57 @@
1
1
  @charset "utf-8";
2
2
 
3
- :where(:root), [data-fray-color="gray"] {
4
- --fray-color-canvas: #f5f6f7;
5
- --fray-color-surface: #ffffff;
6
- --fray-color-surface-muted: #eceff1;
7
- --fray-color-surface-raised: #ffffff;
8
- --fray-color-surface-inset: #dde1e4;
9
- --fray-color-text: #25292d;
10
- --fray-color-text-muted: #626a72;
11
- --fray-color-border: #c3c8cd;
12
- --fray-color-border-strong: #858d95;
13
- --fray-color-primary: #5d6873;
14
- --fray-color-primary-hover: #4d5862;
15
- --fray-color-primary-active: #3d464e;
16
- --fray-color-primary-soft: #e1e6eb;
17
- --fray-color-on-primary: #ffffff;
18
- --fray-color-focus: #1769aa;
19
- --fray-color-selection: #5d6873;
20
- --fray-color-on-selection: #ffffff;
21
- --fray-color-disabled: #e4e7e9;
22
- --fray-color-on-disabled: #6c737a;
23
- --fray-color-error: #b3261e;
24
- --fray-color-on-error: #ffffff;
25
- --fray-color-success: #287a3c;
26
- --fray-color-on-success: #ffffff;
27
- --fray-color-highlight: #ffffff;
28
- --fray-color-shadow: #20252a;
3
+ @layer palette {
4
+ :where(:root:not([data-color]), [data-color="gray"]) {
5
+ --palette-light: #ffffff;
6
+ --palette-dark: #111827;
7
+ --palette-contrast-light: var(--palette-light);
8
+ --palette-contrast-dark: var(--palette-dark);
9
+ --palette-red: #b3261e;
10
+ --palette-green: #287a3c;
11
+
12
+ --palette-primary-500: #5d6873;
13
+ --palette-primary-50: color-mix(in oklch, var(--palette-primary-500) 8%, var(--palette-light));
14
+ --palette-primary-100: color-mix(in oklch, var(--palette-primary-500) 16%, var(--palette-light));
15
+ --palette-primary-200: color-mix(in oklch, var(--palette-primary-500) 32%, var(--palette-light));
16
+ --palette-primary-300: color-mix(in oklch, var(--palette-primary-500) 50%, var(--palette-light));
17
+ --palette-primary-400: color-mix(in oklch, var(--palette-primary-500) 72%, var(--palette-light));
18
+ --palette-primary-600: color-mix(in oklch, var(--palette-primary-500) 85%, var(--palette-dark));
19
+ --palette-primary-700: color-mix(in oklch, var(--palette-primary-500) 72%, var(--palette-dark));
20
+ --palette-primary-800: color-mix(in oklch, var(--palette-primary-500) 58%, var(--palette-dark));
21
+ --palette-primary-900: color-mix(in oklch, var(--palette-primary-500) 42%, var(--palette-dark));
22
+ --palette-primary-950: color-mix(in oklch, var(--palette-primary-500) 28%, var(--palette-dark));
23
+ --palette-primary: var(--palette-primary-500);
24
+ --palette-primary-light: var(--palette-primary-200);
25
+ --palette-primary-dark: var(--palette-primary-800);
26
+
27
+ --palette-secondary-500: #1769aa;
28
+ --palette-secondary-50: color-mix(in oklch, var(--palette-secondary-500) 8%, var(--palette-light));
29
+ --palette-secondary-100: color-mix(in oklch, var(--palette-secondary-500) 16%, var(--palette-light));
30
+ --palette-secondary-200: color-mix(in oklch, var(--palette-secondary-500) 32%, var(--palette-light));
31
+ --palette-secondary-300: color-mix(in oklch, var(--palette-secondary-500) 50%, var(--palette-light));
32
+ --palette-secondary-400: color-mix(in oklch, var(--palette-secondary-500) 72%, var(--palette-light));
33
+ --palette-secondary-600: color-mix(in oklch, var(--palette-secondary-500) 85%, var(--palette-dark));
34
+ --palette-secondary-700: color-mix(in oklch, var(--palette-secondary-500) 72%, var(--palette-dark));
35
+ --palette-secondary-800: color-mix(in oklch, var(--palette-secondary-500) 58%, var(--palette-dark));
36
+ --palette-secondary-900: color-mix(in oklch, var(--palette-secondary-500) 42%, var(--palette-dark));
37
+ --palette-secondary-950: color-mix(in oklch, var(--palette-secondary-500) 28%, var(--palette-dark));
38
+ --palette-secondary: var(--palette-secondary-500);
39
+ --palette-secondary-light: var(--palette-secondary-200);
40
+ --palette-secondary-dark: var(--palette-secondary-800);
41
+
42
+ --palette-neutral-500: #858d95;
43
+ --palette-neutral-50: color-mix(in oklch, var(--palette-neutral-500) 6%, var(--palette-light));
44
+ --palette-neutral-100: color-mix(in oklch, var(--palette-neutral-500) 13%, var(--palette-light));
45
+ --palette-neutral-200: color-mix(in oklch, var(--palette-neutral-500) 26%, var(--palette-light));
46
+ --palette-neutral-300: color-mix(in oklch, var(--palette-neutral-500) 43%, var(--palette-light));
47
+ --palette-neutral-400: color-mix(in oklch, var(--palette-neutral-500) 68%, var(--palette-light));
48
+ --palette-neutral-600: color-mix(in oklch, var(--palette-neutral-500) 84%, var(--palette-dark));
49
+ --palette-neutral-700: color-mix(in oklch, var(--palette-neutral-500) 67%, var(--palette-dark));
50
+ --palette-neutral-800: color-mix(in oklch, var(--palette-neutral-500) 50%, var(--palette-dark));
51
+ --palette-neutral-900: color-mix(in oklch, var(--palette-neutral-500) 32%, var(--palette-dark));
52
+ --palette-neutral-950: color-mix(in oklch, var(--palette-neutral-500) 18%, var(--palette-dark));
53
+ --palette-neutral: var(--palette-neutral-500);
54
+ --palette-neutral-light: var(--palette-neutral-200);
55
+ --palette-neutral-dark: var(--palette-neutral-800);
56
+ }
29
57
  }
@@ -1,29 +1,57 @@
1
1
  @charset "utf-8";
2
2
 
3
- :where(:root), [data-fray-color="green"] {
4
- --fray-color-canvas: #f4faf4;
5
- --fray-color-surface: #ffffff;
6
- --fray-color-surface-muted: #e6f3e7;
7
- --fray-color-surface-raised: #ffffff;
8
- --fray-color-surface-inset: #d2e7d4;
9
- --fray-color-text: #18301d;
10
- --fray-color-text-muted: #536b57;
11
- --fray-color-border: #b4ccb8;
12
- --fray-color-border-strong: #75947a;
13
- --fray-color-primary: #287a3c;
14
- --fray-color-primary-hover: #206b33;
15
- --fray-color-primary-active: #185529;
16
- --fray-color-primary-soft: #d5f0da;
17
- --fray-color-on-primary: #ffffff;
18
- --fray-color-focus: #19883a;
19
- --fray-color-selection: #287a3c;
20
- --fray-color-on-selection: #ffffff;
21
- --fray-color-disabled: #e0e9e1;
22
- --fray-color-on-disabled: #657968;
23
- --fray-color-error: #b3261e;
24
- --fray-color-on-error: #ffffff;
25
- --fray-color-success: #287a3c;
26
- --fray-color-on-success: #ffffff;
27
- --fray-color-highlight: #ffffff;
28
- --fray-color-shadow: #15321b;
3
+ @layer palette {
4
+ :where(:root:not([data-color]), [data-color="green"]) {
5
+ --palette-light: #ffffff;
6
+ --palette-dark: #111827;
7
+ --palette-contrast-light: var(--palette-light);
8
+ --palette-contrast-dark: var(--palette-dark);
9
+ --palette-red: #b3261e;
10
+ --palette-green: #287a3c;
11
+
12
+ --palette-primary-500: #287a3c;
13
+ --palette-primary-50: color-mix(in oklch, var(--palette-primary-500) 8%, var(--palette-light));
14
+ --palette-primary-100: color-mix(in oklch, var(--palette-primary-500) 16%, var(--palette-light));
15
+ --palette-primary-200: color-mix(in oklch, var(--palette-primary-500) 32%, var(--palette-light));
16
+ --palette-primary-300: color-mix(in oklch, var(--palette-primary-500) 50%, var(--palette-light));
17
+ --palette-primary-400: color-mix(in oklch, var(--palette-primary-500) 72%, var(--palette-light));
18
+ --palette-primary-600: color-mix(in oklch, var(--palette-primary-500) 85%, var(--palette-dark));
19
+ --palette-primary-700: color-mix(in oklch, var(--palette-primary-500) 72%, var(--palette-dark));
20
+ --palette-primary-800: color-mix(in oklch, var(--palette-primary-500) 58%, var(--palette-dark));
21
+ --palette-primary-900: color-mix(in oklch, var(--palette-primary-500) 42%, var(--palette-dark));
22
+ --palette-primary-950: color-mix(in oklch, var(--palette-primary-500) 28%, var(--palette-dark));
23
+ --palette-primary: var(--palette-primary-500);
24
+ --palette-primary-light: var(--palette-primary-200);
25
+ --palette-primary-dark: var(--palette-primary-800);
26
+
27
+ --palette-secondary-500: #087f78;
28
+ --palette-secondary-50: color-mix(in oklch, var(--palette-secondary-500) 8%, var(--palette-light));
29
+ --palette-secondary-100: color-mix(in oklch, var(--palette-secondary-500) 16%, var(--palette-light));
30
+ --palette-secondary-200: color-mix(in oklch, var(--palette-secondary-500) 32%, var(--palette-light));
31
+ --palette-secondary-300: color-mix(in oklch, var(--palette-secondary-500) 50%, var(--palette-light));
32
+ --palette-secondary-400: color-mix(in oklch, var(--palette-secondary-500) 72%, var(--palette-light));
33
+ --palette-secondary-600: color-mix(in oklch, var(--palette-secondary-500) 85%, var(--palette-dark));
34
+ --palette-secondary-700: color-mix(in oklch, var(--palette-secondary-500) 72%, var(--palette-dark));
35
+ --palette-secondary-800: color-mix(in oklch, var(--palette-secondary-500) 58%, var(--palette-dark));
36
+ --palette-secondary-900: color-mix(in oklch, var(--palette-secondary-500) 42%, var(--palette-dark));
37
+ --palette-secondary-950: color-mix(in oklch, var(--palette-secondary-500) 28%, var(--palette-dark));
38
+ --palette-secondary: var(--palette-secondary-500);
39
+ --palette-secondary-light: var(--palette-secondary-200);
40
+ --palette-secondary-dark: var(--palette-secondary-800);
41
+
42
+ --palette-neutral-500: #75947a;
43
+ --palette-neutral-50: color-mix(in oklch, var(--palette-neutral-500) 6%, var(--palette-light));
44
+ --palette-neutral-100: color-mix(in oklch, var(--palette-neutral-500) 13%, var(--palette-light));
45
+ --palette-neutral-200: color-mix(in oklch, var(--palette-neutral-500) 26%, var(--palette-light));
46
+ --palette-neutral-300: color-mix(in oklch, var(--palette-neutral-500) 43%, var(--palette-light));
47
+ --palette-neutral-400: color-mix(in oklch, var(--palette-neutral-500) 68%, var(--palette-light));
48
+ --palette-neutral-600: color-mix(in oklch, var(--palette-neutral-500) 84%, var(--palette-dark));
49
+ --palette-neutral-700: color-mix(in oklch, var(--palette-neutral-500) 67%, var(--palette-dark));
50
+ --palette-neutral-800: color-mix(in oklch, var(--palette-neutral-500) 50%, var(--palette-dark));
51
+ --palette-neutral-900: color-mix(in oklch, var(--palette-neutral-500) 32%, var(--palette-dark));
52
+ --palette-neutral-950: color-mix(in oklch, var(--palette-neutral-500) 18%, var(--palette-dark));
53
+ --palette-neutral: var(--palette-neutral-500);
54
+ --palette-neutral-light: var(--palette-neutral-200);
55
+ --palette-neutral-dark: var(--palette-neutral-800);
56
+ }
29
57
  }
@@ -1,29 +1,57 @@
1
1
  @charset "utf-8";
2
2
 
3
- :where(:root), [data-fray-color="iceblue"] {
4
- --fray-color-canvas: #f4f8fc;
5
- --fray-color-surface: #ffffff;
6
- --fray-color-surface-muted: #eaf2f9;
7
- --fray-color-surface-raised: #ffffff;
8
- --fray-color-surface-inset: #d9e7f3;
9
- --fray-color-text: #162536;
10
- --fray-color-text-muted: #53687c;
11
- --fray-color-border: #b8c9d9;
12
- --fray-color-border-strong: #7892aa;
13
- --fray-color-primary: #1769aa;
14
- --fray-color-primary-hover: #0f5b96;
15
- --fray-color-primary-active: #0a4778;
16
- --fray-color-primary-soft: #d7ebfb;
17
- --fray-color-on-primary: #ffffff;
18
- --fray-color-focus: #1683d8;
19
- --fray-color-selection: #1769aa;
20
- --fray-color-on-selection: #ffffff;
21
- --fray-color-disabled: #e2e9ef;
22
- --fray-color-on-disabled: #697a89;
23
- --fray-color-error: #b3261e;
24
- --fray-color-on-error: #ffffff;
25
- --fray-color-success: #287a3c;
26
- --fray-color-on-success: #ffffff;
27
- --fray-color-highlight: #ffffff;
28
- --fray-color-shadow: #11283d;
3
+ @layer palette {
4
+ :where(:root:not([data-color]), [data-color="iceblue"]) {
5
+ --palette-light: #ffffff;
6
+ --palette-dark: #111827;
7
+ --palette-contrast-light: var(--palette-light);
8
+ --palette-contrast-dark: var(--palette-dark);
9
+ --palette-red: #b3261e;
10
+ --palette-green: #287a3c;
11
+
12
+ --palette-primary-500: #1769aa;
13
+ --palette-primary-50: color-mix(in oklch, var(--palette-primary-500) 8%, var(--palette-light));
14
+ --palette-primary-100: color-mix(in oklch, var(--palette-primary-500) 16%, var(--palette-light));
15
+ --palette-primary-200: color-mix(in oklch, var(--palette-primary-500) 32%, var(--palette-light));
16
+ --palette-primary-300: color-mix(in oklch, var(--palette-primary-500) 50%, var(--palette-light));
17
+ --palette-primary-400: color-mix(in oklch, var(--palette-primary-500) 72%, var(--palette-light));
18
+ --palette-primary-600: color-mix(in oklch, var(--palette-primary-500) 85%, var(--palette-dark));
19
+ --palette-primary-700: color-mix(in oklch, var(--palette-primary-500) 72%, var(--palette-dark));
20
+ --palette-primary-800: color-mix(in oklch, var(--palette-primary-500) 58%, var(--palette-dark));
21
+ --palette-primary-900: color-mix(in oklch, var(--palette-primary-500) 42%, var(--palette-dark));
22
+ --palette-primary-950: color-mix(in oklch, var(--palette-primary-500) 28%, var(--palette-dark));
23
+ --palette-primary: var(--palette-primary-500);
24
+ --palette-primary-light: var(--palette-primary-200);
25
+ --palette-primary-dark: var(--palette-primary-800);
26
+
27
+ --palette-secondary-500: #7137a8;
28
+ --palette-secondary-50: color-mix(in oklch, var(--palette-secondary-500) 8%, var(--palette-light));
29
+ --palette-secondary-100: color-mix(in oklch, var(--palette-secondary-500) 16%, var(--palette-light));
30
+ --palette-secondary-200: color-mix(in oklch, var(--palette-secondary-500) 32%, var(--palette-light));
31
+ --palette-secondary-300: color-mix(in oklch, var(--palette-secondary-500) 50%, var(--palette-light));
32
+ --palette-secondary-400: color-mix(in oklch, var(--palette-secondary-500) 72%, var(--palette-light));
33
+ --palette-secondary-600: color-mix(in oklch, var(--palette-secondary-500) 85%, var(--palette-dark));
34
+ --palette-secondary-700: color-mix(in oklch, var(--palette-secondary-500) 72%, var(--palette-dark));
35
+ --palette-secondary-800: color-mix(in oklch, var(--palette-secondary-500) 58%, var(--palette-dark));
36
+ --palette-secondary-900: color-mix(in oklch, var(--palette-secondary-500) 42%, var(--palette-dark));
37
+ --palette-secondary-950: color-mix(in oklch, var(--palette-secondary-500) 28%, var(--palette-dark));
38
+ --palette-secondary: var(--palette-secondary-500);
39
+ --palette-secondary-light: var(--palette-secondary-200);
40
+ --palette-secondary-dark: var(--palette-secondary-800);
41
+
42
+ --palette-neutral-500: #7892aa;
43
+ --palette-neutral-50: color-mix(in oklch, var(--palette-neutral-500) 6%, var(--palette-light));
44
+ --palette-neutral-100: color-mix(in oklch, var(--palette-neutral-500) 13%, var(--palette-light));
45
+ --palette-neutral-200: color-mix(in oklch, var(--palette-neutral-500) 26%, var(--palette-light));
46
+ --palette-neutral-300: color-mix(in oklch, var(--palette-neutral-500) 43%, var(--palette-light));
47
+ --palette-neutral-400: color-mix(in oklch, var(--palette-neutral-500) 68%, var(--palette-light));
48
+ --palette-neutral-600: color-mix(in oklch, var(--palette-neutral-500) 84%, var(--palette-dark));
49
+ --palette-neutral-700: color-mix(in oklch, var(--palette-neutral-500) 67%, var(--palette-dark));
50
+ --palette-neutral-800: color-mix(in oklch, var(--palette-neutral-500) 50%, var(--palette-dark));
51
+ --palette-neutral-900: color-mix(in oklch, var(--palette-neutral-500) 32%, var(--palette-dark));
52
+ --palette-neutral-950: color-mix(in oklch, var(--palette-neutral-500) 18%, var(--palette-dark));
53
+ --palette-neutral: var(--palette-neutral-500);
54
+ --palette-neutral-light: var(--palette-neutral-200);
55
+ --palette-neutral-dark: var(--palette-neutral-800);
56
+ }
29
57
  }