@nxds/core 0.7.2 → 0.9.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 +16 -16
- package/dist/runtime/catalogue.cjs +113 -20
- package/dist/runtime/catalogue.cjs.map +1 -1
- package/dist/runtime/catalogue.d.cts +8 -8
- package/dist/runtime/catalogue.d.ts +8 -8
- package/dist/runtime/catalogue.js +113 -20
- package/dist/runtime/catalogue.js.map +1 -1
- package/dist/runtime/index.cjs +179 -69
- package/dist/runtime/index.cjs.map +1 -1
- package/dist/runtime/index.d.cts +61 -28
- package/dist/runtime/index.d.ts +61 -28
- package/dist/runtime/index.js +156 -54
- package/dist/runtime/index.js.map +1 -1
- package/dist/runtime/{palette-DgJXuoTz.d.cts → palette-BKTIQ0WC.d.cts} +3 -3
- package/dist/runtime/{palette-DgJXuoTz.d.ts → palette-BKTIQ0WC.d.ts} +3 -3
- package/dist/runtime/palette.cjs.map +1 -1
- package/dist/runtime/palette.d.cts +2 -2
- package/dist/runtime/palette.d.ts +2 -2
- package/dist/runtime/palette.js.map +1 -1
- package/dist/runtime/{surface-ladder-yQyRTCRt.d.ts → surface-ladder-B950TU4_.d.ts} +25 -25
- package/dist/runtime/{surface-ladder-C2sSp67b.d.cts → surface-ladder-C8HTwT7z.d.cts} +25 -25
- package/package.json +17 -17
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @nxds/core
|
|
2
2
|
|
|
3
|
-
The framework-agnostic engine behind
|
|
3
|
+
The framework-agnostic engine behind nxds Appearance: appearance model, runtime theme derivation, and first-paint snapshots. Use it directly for non-React targets like Electron, React Native, or native shells, or via `@nxds/react/appearance` in React apps. It also contains the design tokens in DTCG format and CSS generation documented below.
|
|
4
4
|
|
|
5
5
|
## Install
|
|
6
6
|
|
|
@@ -10,17 +10,17 @@ pnpm add @nxds/core
|
|
|
10
10
|
|
|
11
11
|
## Primary Exports
|
|
12
12
|
|
|
13
|
-
- `
|
|
13
|
+
- `DEFAULT_NXDS_APPEARANCE`, `sanitizeNxdsAppearance`, `NxdsAppearanceState`: the appearance model.
|
|
14
14
|
- `BRAND_COLOR_PRESETS`, `BrandColorPreset`, `findBrandColorPreset`, `DEFAULT_BRAND_COLOR`: brand color choices for appearance editors. Default is `DEFAULT_BRAND_COLOR`; Indigo, Blue, Violet, Rose, Orange, Amber, Green, and Teal use each family's authored 600 hex. Assign a preset's `color` to `brandColor`; `findBrandColorPreset(brandColor)` returns the preset an opaque saved color matches in any CSS notation the engine parses (`#4F46E5`, `4f46e5`, `rgb(79 70 229)`), ignoring surrounding whitespace, or `undefined` for a custom or unparseable color.
|
|
15
|
-
- `
|
|
15
|
+
- `createNxdsThemeContract`, `deriveTheme`, `themeToCss`: derive a full token set from appearance state and render it to CSS.
|
|
16
16
|
- `measureThemeContrast`, `ThemeContrastCheck`, `Mode`, `Tier`: measure a derived theme against every registered APCA pair. See [Contrast report](#contrast-report).
|
|
17
|
-
- `
|
|
17
|
+
- `createNxdsAppearanceSnapshotFromState`, `createNxdsAppearanceBootstrapScript`, `resolveFirstPaint`, `DEFAULT_STORAGE_KEY`: first-paint, no-flash bootstrap.
|
|
18
18
|
|
|
19
19
|
## Advanced / Engine Exports
|
|
20
20
|
|
|
21
21
|
`adjustContrast`, `PALETTE_KEYS`, `TIER_THRESHOLDS`, `isColor`: low-level palette and contrast utilities. Stability is not guaranteed pre-1.0.
|
|
22
22
|
|
|
23
|
-
See the
|
|
23
|
+
See the nxds docs, Theming -> Appearance, for setup recipes.
|
|
24
24
|
|
|
25
25
|
## Contrast report
|
|
26
26
|
|
|
@@ -28,14 +28,14 @@ See the Nexus docs, Theming -> Appearance, for setup recipes.
|
|
|
28
28
|
|
|
29
29
|
```ts
|
|
30
30
|
import {
|
|
31
|
-
|
|
32
|
-
|
|
31
|
+
createNxdsThemeContract,
|
|
32
|
+
DEFAULT_NXDS_APPEARANCE,
|
|
33
33
|
deriveTheme,
|
|
34
34
|
measureThemeContrast,
|
|
35
35
|
} from '@nxds/core';
|
|
36
36
|
|
|
37
37
|
const checks = measureThemeContrast(
|
|
38
|
-
deriveTheme(
|
|
38
|
+
deriveTheme(createNxdsThemeContract(DEFAULT_NXDS_APPEARANCE))
|
|
39
39
|
);
|
|
40
40
|
const failing = checks.filter((check) => !check.pass);
|
|
41
41
|
```
|
|
@@ -77,20 +77,20 @@ Use the engine directly when a host shell owns DOM or native styling.
|
|
|
77
77
|
|
|
78
78
|
```ts
|
|
79
79
|
import {
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
80
|
+
createNxdsAppearanceSnapshotFromState,
|
|
81
|
+
createNxdsThemeContract,
|
|
82
|
+
DEFAULT_NXDS_APPEARANCE,
|
|
83
83
|
deriveTheme,
|
|
84
84
|
resolveFirstPaint,
|
|
85
85
|
themeToCss,
|
|
86
86
|
} from '@nxds/core';
|
|
87
87
|
|
|
88
88
|
const state = {
|
|
89
|
-
...
|
|
89
|
+
...DEFAULT_NXDS_APPEARANCE,
|
|
90
90
|
brandColor: '#2563eb',
|
|
91
91
|
surfaceTone: 'slate',
|
|
92
92
|
};
|
|
93
|
-
const snapshot =
|
|
93
|
+
const snapshot = createNxdsAppearanceSnapshotFromState(state);
|
|
94
94
|
const firstPaint = resolveFirstPaint(snapshot, false);
|
|
95
95
|
const themeStyle =
|
|
96
96
|
document.querySelector<HTMLStyleElement>('style[data-theme]') ??
|
|
@@ -102,7 +102,7 @@ document.documentElement.classList.toggle(
|
|
|
102
102
|
firstPaint.className === 'dark'
|
|
103
103
|
);
|
|
104
104
|
themeStyle.textContent = themeToCss(
|
|
105
|
-
deriveTheme(
|
|
105
|
+
deriveTheme(createNxdsThemeContract(snapshot.state))
|
|
106
106
|
);
|
|
107
107
|
```
|
|
108
108
|
|
|
@@ -155,7 +155,7 @@ Generated global CSS sets the native browser UI policy alongside the tokens: `:r
|
|
|
155
155
|
## Reference Resolution
|
|
156
156
|
|
|
157
157
|
Semantic **color** is produced by the engine (`deriveTheme`), not authored as
|
|
158
|
-
JSON. The build bakes each derived value into `
|
|
158
|
+
JSON. The build bakes each derived value into `nxds.css` as a `@theme inline`
|
|
159
159
|
fallback that still accepts a runtime override:
|
|
160
160
|
|
|
161
161
|
```css
|
|
@@ -178,7 +178,7 @@ pnpm tokens:tailwind # From the repo root: build core and generate @nxds/ta
|
|
|
178
178
|
|
|
179
179
|
### Output Files
|
|
180
180
|
|
|
181
|
-
- `../tailwind/
|
|
181
|
+
- `../tailwind/nxds.css` - Main Tailwind theme with `nx:` prefix
|
|
182
182
|
- `../tailwind/variables.css` - Primitive CSS variables with `--nx-*` prefix
|
|
183
183
|
|
|
184
184
|
## Future Platform Support
|
|
@@ -1107,7 +1107,7 @@ var STROKE_OPTIONS = [
|
|
|
1107
1107
|
{ value: "normal", label: "Normal" },
|
|
1108
1108
|
{ value: "strong", label: "Strong" }
|
|
1109
1109
|
];
|
|
1110
|
-
var
|
|
1110
|
+
var DEFAULT_NXDS_APPEARANCE = {
|
|
1111
1111
|
mode: "light",
|
|
1112
1112
|
brandColor: DEFAULT_BRAND_COLOR,
|
|
1113
1113
|
surfaceTone: "zinc",
|
|
@@ -1142,7 +1142,7 @@ var ELEVATIONS = new Set(
|
|
|
1142
1142
|
var STROKES = new Set(
|
|
1143
1143
|
STROKE_OPTIONS.map((option) => option.value)
|
|
1144
1144
|
);
|
|
1145
|
-
function
|
|
1145
|
+
function createNxdsThemeContract(state) {
|
|
1146
1146
|
const tone = BASE_TONE_SEEDS[state.surfaceTone];
|
|
1147
1147
|
return {
|
|
1148
1148
|
surfaceTone: state.surfaceTone,
|
|
@@ -3346,24 +3346,117 @@ function borderColorAliasName(tokenName) {
|
|
|
3346
3346
|
}
|
|
3347
3347
|
|
|
3348
3348
|
// src/catalogue/descriptions.ts
|
|
3349
|
-
var
|
|
3350
|
-
|
|
3351
|
-
|
|
3352
|
-
|
|
3353
|
-
|
|
3354
|
-
|
|
3355
|
-
|
|
3356
|
-
|
|
3357
|
-
|
|
3358
|
-
|
|
3359
|
-
|
|
3360
|
-
)
|
|
3361
|
-
|
|
3349
|
+
var FAMILY_ROLES = {
|
|
3350
|
+
primary: "the main action on a surface",
|
|
3351
|
+
secondary: "a supporting action beside a primary one",
|
|
3352
|
+
success: "a completed or healthy state",
|
|
3353
|
+
warning: "a state that needs attention soon",
|
|
3354
|
+
error: "a failed, invalid or destructive state",
|
|
3355
|
+
information: "neutral guidance or a status update"
|
|
3356
|
+
};
|
|
3357
|
+
var FAMILY_TEMPLATES = {
|
|
3358
|
+
background: (family, role) => `Solid fill for buttons, badges and other strong emphasis in this family. Pair with ${family}-foreground. Marks ${role}.`,
|
|
3359
|
+
"background-hover": (family) => `${family}-background while the pointer is over the control.`,
|
|
3360
|
+
"background-active": (family) => `${family}-background while the control is pressed.`,
|
|
3361
|
+
foreground: (family) => `Foreground on ${family}-background. The surface relationship defines this role, not text alone: text and icons share it, and contrasting control parts or indicators may use it on the same surface. Other pairings require contrast coverage.`,
|
|
3362
|
+
disabled: () => "Fill of a disabled control in this family. Pair with disabled-foreground.",
|
|
3363
|
+
subtle: (family, role) => `Tinted fill for light badges, alerts and selected rows on background or container. Pair with ${family}-subtle-foreground. Marks ${role}.`,
|
|
3364
|
+
"subtle-foreground": (family) => `Foreground on ${family}-subtle. The surface relationship defines this role, not text alone: text and icons share it, and contrasting indicators may use it on that surface. Use on neutral surfaces only with contrast coverage for the intended content tier.`,
|
|
3365
|
+
"subtle-hover": (family) => `${family}-subtle while the pointer is over the control.`,
|
|
3366
|
+
"subtle-active": (family) => `${family}-subtle while the control is pressed.`,
|
|
3367
|
+
border: () => "Border of an outlined control or alert in this family, on background or container.",
|
|
3368
|
+
"border-active": (family) => `${family}-border while the control is pressed or selected.`
|
|
3369
|
+
};
|
|
3370
|
+
function familyRole(name) {
|
|
3371
|
+
for (const family of COLOR_FAMILIES) {
|
|
3372
|
+
if (name === `border-${family}-active`) return [family, "border-active"];
|
|
3373
|
+
if (name.startsWith(`${family}-`)) {
|
|
3374
|
+
return [family, name.slice(family.length + 1)];
|
|
3375
|
+
}
|
|
3376
|
+
}
|
|
3377
|
+
return void 0;
|
|
3378
|
+
}
|
|
3379
|
+
var NEUTRAL_DESCRIPTIONS = {
|
|
3380
|
+
background: "Page background, the base surface everything sits on. Pair with foreground.",
|
|
3381
|
+
"background-hover": "Row or ghost-control hover on background. Pair with foreground.",
|
|
3382
|
+
"background-active": "Row or ghost-control press and selection on background. Pair with foreground.",
|
|
3383
|
+
muted: "Quiet fill for wells, code, skeletons and inactive tracks on background. Pair with muted-foreground.",
|
|
3362
3384
|
"muted-extralight": "Quietest muted surface \u2014 sits between background and muted for barely-there fills (empty states, subtle panels).",
|
|
3385
|
+
container: "Card and panel surface on background. Pair with container-foreground.",
|
|
3386
|
+
"container-hover": "Row or control hover inside a container.",
|
|
3387
|
+
"container-active": "Row or control press and selection inside a container.",
|
|
3388
|
+
popover: "Surface of menus, popovers, tooltips and dialogs. Pair with popover-foreground.",
|
|
3389
|
+
"popover-hover": "Highlighted menu row inside a popover.",
|
|
3390
|
+
"popover-active": "Pressed or checked menu row inside a popover.",
|
|
3391
|
+
"control-background": "Rail of neutral controls: switch tracks, slider tracks, segmented tabs. Also the hover fill on a neutral control rail.",
|
|
3392
|
+
"control-background-hover": "control-background while the pointer is over it.",
|
|
3393
|
+
"control-thumb": "Thumb of switches and sliders, on control-background.",
|
|
3394
|
+
"nav-background": "Sidebar and navigation surface. Pair with nav-foreground.",
|
|
3395
|
+
"nav-item-hover": "Navigation item hover on nav-background.",
|
|
3396
|
+
"nav-item-active": "Current navigation item on nav-background.",
|
|
3397
|
+
"nav-border": "Border between the sidebar and the page, and inside it.",
|
|
3398
|
+
disabled: "Fill of a disabled neutral control or input. Pair with disabled-foreground.",
|
|
3399
|
+
"border-focus": "Border of a focused input, on background or container.",
|
|
3400
|
+
foreground: "Body text and icons on background, muted and the hover fills.",
|
|
3401
|
+
"container-foreground": "Text and icons on container.",
|
|
3402
|
+
"popover-foreground": "Text and icons on popover and its row fills.",
|
|
3403
|
+
"nav-foreground": "Navigation text and icons on nav-background.",
|
|
3363
3404
|
"muted-foreground": "It's a gray that softens contrast so primary content stands forward.",
|
|
3405
|
+
"nav-muted-foreground": "Secondary navigation text, such as group labels, on nav-background.",
|
|
3364
3406
|
"muted-foreground-subtle": "Tertiary text tier below muted-foreground - helper text, captions, divider labels.",
|
|
3365
|
-
|
|
3407
|
+
"disabled-foreground": "Text and icons of a disabled control, on disabled and the family disabled fills.",
|
|
3408
|
+
"border-default": "Default border of inputs, cards, tables and separators, on background or container.",
|
|
3409
|
+
"border-disabled": "Border of a disabled control.",
|
|
3410
|
+
"border-hairline": "Faint divider between rows and grouped items.",
|
|
3411
|
+
"chart-categorical-1": "First chart series, on container or background. Series colours stay in order.",
|
|
3412
|
+
"chart-categorical-2": "Second chart series, on container or background.",
|
|
3413
|
+
"chart-categorical-3": "Third chart series, on container or background.",
|
|
3414
|
+
"chart-categorical-4": "Fourth chart series, on container or background.",
|
|
3415
|
+
"chart-categorical-5": "Fifth chart series, on container or background.",
|
|
3416
|
+
"focus-default": "Focus ring of every interactive control, on any surface.",
|
|
3417
|
+
"focus-error": "Focus ring of an invalid control.",
|
|
3418
|
+
"background-hover-alpha": "Translucent hover fill for controls that sit on an image or an unknown surface.",
|
|
3419
|
+
"border-default-alpha": "Translucent border for controls that sit on an image or an unknown surface.",
|
|
3420
|
+
overlay: "Scrim behind dialogs, sheets and drawers.",
|
|
3421
|
+
"popover-alpha": "Translucent popover surface for frosted overlays.",
|
|
3422
|
+
"popover-backdrop": "Backdrop behind a frosted popover."
|
|
3366
3423
|
};
|
|
3424
|
+
var REGISTRY_NAMES = SEMANTIC_TOKEN_REGISTRY.map(
|
|
3425
|
+
(token2) => token2.name
|
|
3426
|
+
);
|
|
3427
|
+
function familyDescriptions() {
|
|
3428
|
+
const used = /* @__PURE__ */ new Set();
|
|
3429
|
+
const entries = REGISTRY_NAMES.flatMap((name) => {
|
|
3430
|
+
const match = familyRole(name);
|
|
3431
|
+
if (!match) return [];
|
|
3432
|
+
const [family, role] = match;
|
|
3433
|
+
const template = FAMILY_TEMPLATES[role];
|
|
3434
|
+
if (!template) return [];
|
|
3435
|
+
used.add(role);
|
|
3436
|
+
return [[name, template(family, FAMILY_ROLES[family])]];
|
|
3437
|
+
});
|
|
3438
|
+
const unused = Object.keys(FAMILY_TEMPLATES).filter(
|
|
3439
|
+
(role) => !used.has(role)
|
|
3440
|
+
);
|
|
3441
|
+
if (unused.length > 0) {
|
|
3442
|
+
throw new Error(
|
|
3443
|
+
`FAMILY_TEMPLATES describes ${unused.join(", ")}, which no registry colour has.`
|
|
3444
|
+
);
|
|
3445
|
+
}
|
|
3446
|
+
return entries;
|
|
3447
|
+
}
|
|
3448
|
+
function neutralDescriptions() {
|
|
3449
|
+
const unknown = Object.keys(NEUTRAL_DESCRIPTIONS).filter(
|
|
3450
|
+
(name) => !REGISTRY_NAMES.includes(name)
|
|
3451
|
+
);
|
|
3452
|
+
if (unknown.length > 0) {
|
|
3453
|
+
throw new Error(
|
|
3454
|
+
`NEUTRAL_DESCRIPTIONS describes ${unknown.join(", ")}, which the registry doesn't have.`
|
|
3455
|
+
);
|
|
3456
|
+
}
|
|
3457
|
+
return Object.entries(NEUTRAL_DESCRIPTIONS);
|
|
3458
|
+
}
|
|
3459
|
+
var RUNTIME_COLOR_DESCRIPTIONS = Object.fromEntries([...neutralDescriptions(), ...familyDescriptions()]);
|
|
3367
3460
|
|
|
3368
3461
|
// tokens/primitives/borderwidth/borderwidth-fine.json
|
|
3369
3462
|
var borderwidth_fine_default = {
|
|
@@ -8820,7 +8913,7 @@ var shadow_strong_light_default = {
|
|
|
8820
8913
|
|
|
8821
8914
|
// tokens/semantic/breakpoints.json
|
|
8822
8915
|
var breakpoints_default = {
|
|
8823
|
-
$description: "Responsive breakpoints (rem). Values match Tailwind v4's own defaults exactly \u2014
|
|
8916
|
+
$description: "Responsive breakpoints (rem). Values match Tailwind v4's own defaults exactly \u2014 nxds declares them explicitly so they fall under the same reset-and-own discipline as every other namespace (color/spacing/radius/shadow) instead of being inherited implicitly. Tailwind v4 auto-discovers --breakpoint-* from @theme and uses them to generate the nx:sm:\u2026nx:2xl: responsive variants. Override at BUILD TIME by re-declaring a value in a consumer @theme block \u2014 NOT at runtime: @media cannot read var(), so Tailwind bakes the resolved value into the generated media queries at build time.",
|
|
8824
8917
|
"breakpoint-sm": {
|
|
8825
8918
|
$value: {
|
|
8826
8919
|
value: 40,
|
|
@@ -11167,8 +11260,8 @@ function styleLeaves(family, file) {
|
|
|
11167
11260
|
}
|
|
11168
11261
|
function runtimeAppearance(mode) {
|
|
11169
11262
|
return {
|
|
11170
|
-
...
|
|
11171
|
-
prefs: { ...
|
|
11263
|
+
...DEFAULT_NXDS_APPEARANCE,
|
|
11264
|
+
prefs: { ...DEFAULT_NXDS_APPEARANCE.prefs },
|
|
11172
11265
|
mode
|
|
11173
11266
|
};
|
|
11174
11267
|
}
|
|
@@ -11186,7 +11279,7 @@ function runtimeColorTokens() {
|
|
|
11186
11279
|
return {
|
|
11187
11280
|
mode,
|
|
11188
11281
|
appearance,
|
|
11189
|
-
values: deriveThemeMode(
|
|
11282
|
+
values: deriveThemeMode(createNxdsThemeContract(appearance), mode)
|
|
11190
11283
|
};
|
|
11191
11284
|
});
|
|
11192
11285
|
return SEMANTIC_TOKEN_REGISTRY.map(({ name, category }) => {
|