@nxds/core 0.0.0-stage → 0.7.2

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Nexus Labs
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,200 @@
1
- # Temporary Holding Version
1
+ # @nxds/core
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ The framework-agnostic engine behind Nexus 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
+
5
+ ## Install
6
+
7
+ ```bash
8
+ pnpm add @nxds/core
9
+ ```
10
+
11
+ ## Primary Exports
12
+
13
+ - `DEFAULT_NEXUS_APPEARANCE`, `sanitizeNexusAppearance`, `NexusAppearanceState`: the appearance model.
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
+ - `createNexusThemeContract`, `deriveTheme`, `themeToCss`: derive a full token set from appearance state and render it to CSS.
16
+ - `measureThemeContrast`, `ThemeContrastCheck`, `Mode`, `Tier`: measure a derived theme against every registered APCA pair. See [Contrast report](#contrast-report).
17
+ - `createNexusAppearanceSnapshotFromState`, `createNexusAppearanceBootstrapScript`, `resolveFirstPaint`, `DEFAULT_STORAGE_KEY`: first-paint, no-flash bootstrap.
18
+
19
+ ## Advanced / Engine Exports
20
+
21
+ `adjustContrast`, `PALETTE_KEYS`, `TIER_THRESHOLDS`, `isColor`: low-level palette and contrast utilities. Stability is not guaranteed pre-1.0.
22
+
23
+ See the Nexus docs, Theming -> Appearance, for setup recipes.
24
+
25
+ ## Contrast report
26
+
27
+ `measureThemeContrast(theme)` measures a derived theme after derivation. It returns one `ThemeContrastCheck` per registered foreground/background pair in each mode:
28
+
29
+ ```ts
30
+ import {
31
+ createNexusThemeContract,
32
+ DEFAULT_NEXUS_APPEARANCE,
33
+ deriveTheme,
34
+ measureThemeContrast,
35
+ } from '@nxds/core';
36
+
37
+ const checks = measureThemeContrast(
38
+ deriveTheme(createNexusThemeContract(DEFAULT_NEXUS_APPEARANCE))
39
+ );
40
+ const failing = checks.filter((check) => !check.pass);
41
+ ```
42
+
43
+ Each check has `mode`, the `fg` and `bg` CSS variable names (such as `--nx-color-foreground`), an optional `backdrop` (a CSS variable name or hex color), `tier`, the measured absolute APCA `lc`, the tier `floor`, and `pass` (`lc >= floor`). Translucent colors are composited over their backdrop before measurement.
44
+
45
+ ## Authored palette lookup
46
+
47
+ The browser-safe `@nxds/core/palette` entry resolves the same authored palettes used by CSS generation:
48
+
49
+ ```ts
50
+ import { getPaletteRamp, getPaletteShade } from '@nxds/core/palette';
51
+
52
+ getPaletteShade('green', '600'); // oklch(0.62 0.2233 140.055)
53
+ const neutral = getPaletteRamp('neutral'); // Frozen, readonly shade map
54
+ ```
55
+
56
+ `PRIMITIVE_PALETTE_NAMES` lists the 22 shade families typed by `PrimitivePaletteName`; `SHADES` lists 50 through 950, typed by `Shade`. White and Black are singleton colors, not ramps. `PALETTE_KEYS` identifies only the five surface-tone families. Lookups resolve on first use and cache immutable values; importing the engine does not convert palettes.
57
+
58
+ The engine starts from authored palettes: Success uses Green, Warning Orange, Error Red, and Information Blue. Secondary colors and near-black/near-white primary interaction endpoints use Neutral. The contrast solver can adjust these starting colors before emission.
59
+
60
+ Under deuteranopia the Success (Green) and Warning (Orange) 600 shades are hard to tell apart, so status UI must pair color with an icon and label. The color-vision audit reports this pair as its one accepted limitation.
61
+
62
+ Custom brand ramps and raw surface-tone references retain their separate algorithms. A palette's processed 600 shade is not interchangeable with its authored hex as a brand seed.
63
+
64
+ ## Token catalogue
65
+
66
+ The browser-safe `@nxds/core/catalogue` entry describes every token family for tools and docs: colour, typography, spacing, radius, border width, shadow, motion, z-index, and breakpoints. Each token has its `--nx-*` name, aliases, values per theme mode and preset as the generated CSS declares them, and the authored source leaf. It builds a fresh array and derives both runtime colour modes on each call, so call it once per build or page, not per render.
67
+
68
+ ```ts
69
+ import { createTokenCatalogue } from '@nxds/core/catalogue';
70
+
71
+ const tokens = createTokenCatalogue();
72
+ ```
73
+
74
+ ## Non-React Shell Example
75
+
76
+ Use the engine directly when a host shell owns DOM or native styling.
77
+
78
+ ```ts
79
+ import {
80
+ createNexusAppearanceSnapshotFromState,
81
+ createNexusThemeContract,
82
+ DEFAULT_NEXUS_APPEARANCE,
83
+ deriveTheme,
84
+ resolveFirstPaint,
85
+ themeToCss,
86
+ } from '@nxds/core';
87
+
88
+ const state = {
89
+ ...DEFAULT_NEXUS_APPEARANCE,
90
+ brandColor: '#2563eb',
91
+ surfaceTone: 'slate',
92
+ };
93
+ const snapshot = createNexusAppearanceSnapshotFromState(state);
94
+ const firstPaint = resolveFirstPaint(snapshot, false);
95
+ const themeStyle =
96
+ document.querySelector<HTMLStyleElement>('style[data-theme]') ??
97
+ document.head.appendChild(document.createElement('style'));
98
+ themeStyle.dataset.theme = '';
99
+
100
+ document.documentElement.classList.toggle(
101
+ 'dark',
102
+ firstPaint.className === 'dark'
103
+ );
104
+ themeStyle.textContent = themeToCss(
105
+ deriveTheme(createNexusThemeContract(snapshot.state))
106
+ );
107
+ ```
108
+
109
+ ## Token Architecture
110
+
111
+ ### DTCG Format (W3C Standard)
112
+
113
+ All tokens follow the [Design Tokens Community Group](https://tr.designtokens.org/) specification:
114
+
115
+ ```json
116
+ {
117
+ "token-name": {
118
+ "$value": "#ffffff",
119
+ "$type": "color",
120
+ "$description": "Optional description"
121
+ }
122
+ }
123
+ ```
124
+
125
+ ### Token Hierarchy
126
+
127
+ **Primitives** (`tokens/primitives/`)
128
+
129
+ - Context-independent base values
130
+ - Color, radius, border-width, shadow, typography, motion
131
+ - Output: CSS variables with the `--nx-*` prefix
132
+ - Example: `--nx-color-gray-950`, `--nx-radius-md`
133
+ - Motion primitives also promote named Tailwind utilities such as `nx:duration-fast` and `nx:ease-enter`: durations are emitted as explicit `@utility` rules, while easing uses Tailwind's `@theme` namespace. Existing Tailwind numeric duration/ease defaults remain available until the repo-wide migration lands.
134
+
135
+ **Semantic** (`tokens/semantic/`)
136
+
137
+ - Contextual meanings that reference primitives (and per-mode direct values for spacing)
138
+ - Semantic **color** is engine-derived (`deriveTheme`), not authored here; `tokens/semantic/` now holds only spacing, breakpoints, and z-index
139
+ - Output: Tailwind v4 `@theme` block (semantic color via `@theme inline`, accepting runtime `--nx-color-*` overrides) + per-mode `[data-density="X"]` blocks
140
+ - Example: `--color-background: var(--nx-color-background, oklch(1 0 0))` (engine color floor), `--nx-spacing-4: 16px`
141
+
142
+ > **Spacing is two-tier, not three.** Unlike color/radius/shadow/typography, spacing has no `--nx-size-*` primitive layer — `semantic/spacing-{mode}.json` files carry direct px values, and the build emits per-mode `[data-density="X"]` blocks plus role utilities (`nx:p-container`, `nx:gap-layout-section`, …). Mode swap is runtime via the `data-density` attribute on `<html>`.
143
+
144
+ **Component** (future)
145
+
146
+ - Component-specific tokens
147
+ - References semantic tokens
148
+
149
+ ### Color generation
150
+
151
+ Color tokens don't ship the values stored on disk. Source files hold hex. Build and runtime share authored-palette conversion: chromatic families use their hue-specific lightness curves and P3 cusp chroma; neutral families use the flat lightness grid and source chroma. Both preserve each authored shade's hue. Runtime semantic text/surface pairs are checked against the registered APCA constraints in CI.
152
+
153
+ Generated global CSS sets the native browser UI policy alongside the tokens: `:root` advertises light/dark support, `.dark` pins native controls and scrollbars to dark, and the light root stays light when `.dark` is absent.
154
+
155
+ ## Reference Resolution
156
+
157
+ Semantic **color** is produced by the engine (`deriveTheme`), not authored as
158
+ JSON. The build bakes each derived value into `nexus.css` as a `@theme inline`
159
+ fallback that still accepts a runtime override:
160
+
161
+ ```css
162
+ --color-background: var(--nx-color-background, oklch(1 0 0));
163
+ ```
164
+
165
+ The `--color-*` name is the Tailwind v4 `@theme` key that generates the utility
166
+ (`nx:bg-background`); the `--nx-color-*` fallback is the runtime override the
167
+ appearance provider injects. Non-color families (spacing, radius, shadow,
168
+ borderwidth, …) still use DTCG `{reference}` syntax, resolved to `var(--nx-*)`
169
+ at build time.
170
+
171
+ ## Build Process
172
+
173
+ ### Generate CSS
174
+
175
+ ```bash
176
+ pnpm tokens:tailwind # From the repo root: build core and generate @nxds/tailwind CSS
177
+ ```
178
+
179
+ ### Output Files
180
+
181
+ - `../tailwind/nexus.css` - Main Tailwind theme with `nx:` prefix
182
+ - `../tailwind/variables.css` - Primitive CSS variables with `--nx-*` prefix
183
+
184
+ ## Future Platform Support
185
+
186
+ Tokens are in DTCG format, making them portable to:
187
+
188
+ - iOS (Swift)
189
+ - Android (Kotlin/XML)
190
+ - React Native
191
+ - Any tool supporting DTCG standard
192
+
193
+ When multi-platform support is needed, tools like Style Dictionary can be added to generate platform-specific outputs from the same DTCG token files.
194
+
195
+ ## Adding New Tokens
196
+
197
+ - **Color** is engine-owned: edit the derivation in `src/lib/surface-ladder.ts` / `src/lib/derive-theme.ts` (color primitives live in `tokens/primitives/color.json`).
198
+ - **Non-color** (spacing, radius, shadow, borderwidth, motion, typography): edit the DTCG token files in `tokens/` (`$value`, `$type`, `$description`).
199
+
200
+ Then run `make tokens` (or `pnpm tokens:tailwind`) to regenerate CSS; the output is copied into the `@nxds/tailwind` package. A new or renamed token file also needs an import in `src/catalogue/token-files.ts`; `pnpm test:unit` fails until the manifest matches the files on disk.