nuvox 0.1.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.
- package/BUILDING-COMPONENTS.md +544 -0
- package/CONTRACT.md +255 -0
- package/LICENSE +28 -0
- package/README.md +99 -0
- package/THIRD-PARTY-NOTICES.md +52 -0
- package/dist/chunk-3F7OVF4J.js +1126 -0
- package/dist/chunk-3F7OVF4J.js.map +1 -0
- package/dist/context-menu-CBdFZq5L.d.cts +627 -0
- package/dist/context-menu-CBdFZq5L.d.ts +627 -0
- package/dist/index.cjs +896 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +58 -0
- package/dist/index.d.ts +58 -0
- package/dist/index.js +71 -0
- package/dist/index.js.map +1 -0
- package/dist/react/index.cjs +23038 -0
- package/dist/react/index.cjs.map +1 -0
- package/dist/react/index.css +5731 -0
- package/dist/react/index.css.map +1 -0
- package/dist/react/index.d.cts +1937 -0
- package/dist/react/index.d.ts +1937 -0
- package/dist/react/index.js +21500 -0
- package/dist/react/index.js.map +1 -0
- package/dist/themes/generated.css +1155 -0
- package/package.json +72 -0
package/CONTRACT.md
ADDED
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
# Nuvox Foundation — Contract
|
|
2
|
+
|
|
3
|
+
This is the rulebook for building each future component on top of this
|
|
4
|
+
base. It exists so decisions get made once, here, instead of once per
|
|
5
|
+
component. Every rule below is backed by a specific bug found in the old
|
|
6
|
+
codebase — see the parenthetical.
|
|
7
|
+
|
|
8
|
+
## 1. Size scale
|
|
9
|
+
Import `Size` from `src/core/types.ts`. Don't declare a component-local
|
|
10
|
+
size union. If a component genuinely needs a wider scale (Modal + "full",
|
|
11
|
+
Avatar + "2xl"), extend the shared type explicitly:
|
|
12
|
+
```ts
|
|
13
|
+
export type ModalSize = Size | "full"
|
|
14
|
+
```
|
|
15
|
+
(Old bug: 4 different, undocumented size scales across 41 components.)
|
|
16
|
+
|
|
17
|
+
## 2. Base props
|
|
18
|
+
Every component's prop interface extends `BaseComponentProps`
|
|
19
|
+
(`className`, `style`, `theme`). Only add `color` / `radius` as
|
|
20
|
+
component-specific overrides if the component genuinely needs one —
|
|
21
|
+
but when you do, use the same shape every other component uses.
|
|
22
|
+
(Old bug: `color` on 6/41 components, `radius` on 2/41, no rule for
|
|
23
|
+
which.)
|
|
24
|
+
|
|
25
|
+
## 3. Nullish coalescing only
|
|
26
|
+
Use `??` for every prop default, never `||`. `||` silently discards a
|
|
27
|
+
valid `false`/`0`/`""`.
|
|
28
|
+
(Old bug: 36 files used `??`, 5 used `||`, inconsistently.)
|
|
29
|
+
|
|
30
|
+
## 4. Ref forwarding is the default, not a judgment call
|
|
31
|
+
Every component uses `forwardRef`. See `src/react/Button.tsx` as the
|
|
32
|
+
template. The only exemption is a component with no focusable or
|
|
33
|
+
interactive native element at all (`Divider`, `NuvoxProvider`,
|
|
34
|
+
`ToastProvider` — it renders a variable-length list of toasts inside
|
|
35
|
+
a portal, with no single focusable element to forward to — other
|
|
36
|
+
pure layout/context wrappers) — that's a concrete, checkable exception,
|
|
37
|
+
not a "forward it if it seems worth it" judgment call. Anything with a
|
|
38
|
+
real DOM node a consumer might plausibly focus, measure, or scroll
|
|
39
|
+
forwards its ref, full stop.
|
|
40
|
+
(Old bug: 1 of 41 components forwarded a ref — and the failure mode
|
|
41
|
+
was exactly "judgment calls made independently, 41 times." A vague
|
|
42
|
+
exception clause reopens that same door.)
|
|
43
|
+
|
|
44
|
+
## 5. Theme/accent override, not theme/accent logic
|
|
45
|
+
A component with `theme?: string` and/or `color?: string` props
|
|
46
|
+
(both from `BaseComponentProps`) wraps its output with
|
|
47
|
+
`withThemeOverride(theme, ...)` and/or `withAccentOverride(color, ...)`
|
|
48
|
+
from `theme-engine/NuvoxProvider`. A component never branches on
|
|
49
|
+
theme or accent identity (`if (theme === "glass")`, `if (color ===
|
|
50
|
+
"teal")`) — it only ever consumes semantic CSS variables. This should
|
|
51
|
+
read naturally whether the theme catalog stays at 3 or grows to 15,
|
|
52
|
+
and whether the accent palette stays at 9 or grows to 30.
|
|
53
|
+
(Old bug: components didn't have this override at all; the doc's
|
|
54
|
+
planned priority rule — component > provider > default — was never
|
|
55
|
+
implemented in code. A second bug, found later: a component accepted
|
|
56
|
+
`theme`/`color` via `BaseComponentProps` and silently ignored both —
|
|
57
|
+
accepting a prop is not the same as applying it; test for the
|
|
58
|
+
override actually taking effect, not just that the prop compiles.)
|
|
59
|
+
|
|
60
|
+
## 5b. Theme is shape, accent is color — never conflate them
|
|
61
|
+
`ThemeTokens` (see `contract.ts`) has no `color.brand` field, on
|
|
62
|
+
purpose. A theme controls radius, shadow, motion, border treatment,
|
|
63
|
+
and glass/gradient/glow effects — the properties in the doc's own
|
|
64
|
+
original vision of what makes Cyberpunk feel different from Brutalism.
|
|
65
|
+
It does not control the brand accent color at all. Brand color is a
|
|
66
|
+
separate, orthogonal registry (`accents.ts`) that a consumer picks
|
|
67
|
+
independently — "our brand is teal" has to work whether the shape is
|
|
68
|
+
Light, Cyberpunk, or Brutalism, not just the one theme an author
|
|
69
|
+
happened to design it against.
|
|
70
|
+
|
|
71
|
+
This was a real bug, not a hypothetical: `gradient`, `minimalism`, and
|
|
72
|
+
`skeuomorphism` were each authored with their own hardcoded
|
|
73
|
+
`color.brand` scale, which meant every Button rendered pixel-identical
|
|
74
|
+
to Light for anyone who hadn't noticed the hue never differed. It
|
|
75
|
+
compiled fine and passed every completeness check, because
|
|
76
|
+
completeness and distinctness are different properties (see Rule 7's
|
|
77
|
+
note on this) — and it was orthogonality that was actually missing,
|
|
78
|
+
not distinctness.
|
|
79
|
+
|
|
80
|
+
**The one deliberate exception:** `success`/`danger`/`warning`/`info`
|
|
81
|
+
stay theme-defined and are NOT part of the accent system. Their exact
|
|
82
|
+
shade can differ per theme for contrast (dark themes need a lighter
|
|
83
|
+
green than light themes to stay legible), but their meaning — green
|
|
84
|
+
reads as success, red as danger — is not something a consumer should
|
|
85
|
+
be able to accidentally reassign by picking an accent. If you ever
|
|
86
|
+
find yourself wanting `color="success"` to be a valid accent value,
|
|
87
|
+
that's the signal to stop — semantic color and brand color solve
|
|
88
|
+
different problems and must stay separate registries.
|
|
89
|
+
|
|
90
|
+
## 5c. Text on a solid accent surface uses `--color-brand-onAccent`, never `--color-textInverse`
|
|
91
|
+
This was also a real bug, not a hypothetical: Button's `solid`
|
|
92
|
+
variant, Badge's `solid` variant, and Checkbox's checkmark all paired
|
|
93
|
+
`--color-brand-500` (the accent's fixed color — same value in every
|
|
94
|
+
theme, per Rule 5b) with `--color-textInverse` (a THEME token,
|
|
95
|
+
computed assuming the surface underneath is light or dark depending
|
|
96
|
+
on *that theme*). Those two axes usually agree — until an accent like
|
|
97
|
+
`graphite` (a gray scale, 500: `#525252`) meets `dark` theme
|
|
98
|
+
(`textInverse: #111827`, chosen assuming a *light* surface). Dark
|
|
99
|
+
gray text on a mid-gray background is close to unreadable, and
|
|
100
|
+
nothing in the type system catches it, because both tokens
|
|
101
|
+
individually resolve fine.
|
|
102
|
+
|
|
103
|
+
The fix: `generate-css.ts` computes `--color-brand-onAccent` per
|
|
104
|
+
accent, from that accent's own 500 shade via WCAG relative luminance
|
|
105
|
+
(`tokens/contrast.ts`) — not borrowed from whichever theme happens to
|
|
106
|
+
be active. Any component painting text directly on
|
|
107
|
+
`--color-brand-500` (or another accent step used as a fill) uses
|
|
108
|
+
`--color-brand-onAccent` for that text, full stop.
|
|
109
|
+
`tests/accent-contrast.test.ts` enforces every registered accent's
|
|
110
|
+
computed value clears WCAG AA (4.5:1) against its own 500 shade — a
|
|
111
|
+
new accent that fails this fails CI, not a future bug report.
|
|
112
|
+
|
|
113
|
+
The related, softer case — outline-variant text (`color-mix(in srgb,
|
|
114
|
+
var(--color-brand-600) 65%, var(--color-text) 35%)`) sitting directly
|
|
115
|
+
on a theme's own ambient background rather than a solid accent fill —
|
|
116
|
+
is audited across all 99 theme × accent pairs in
|
|
117
|
+
`tests/outline-text-contrast.test.ts`. That audit surfaced two real
|
|
118
|
+
bugs *in the audit itself* before confirming the actual blend was
|
|
119
|
+
fine: naive hex parsing silently turned `"transparent"` and CSS
|
|
120
|
+
gradient strings into black (bitwise ops coerce `NaN` to `0`), and a
|
|
121
|
+
translucent `rgba()` surface color had its alpha dropped, testing
|
|
122
|
+
against the wrong effective color entirely. Both are fixed in
|
|
123
|
+
`tokens/contrast.ts` (`parseColorWithAlpha`, `compositeOver`,
|
|
124
|
+
`isSolidColor`) — worth knowing about if a future theme introduces
|
|
125
|
+
another non-solid color value, since the same class of silent
|
|
126
|
+
mis-parsing is exactly the kind of bug that doesn't announce itself.
|
|
127
|
+
|
|
128
|
+
## 6. Portals go through ThemedPortal, never createPortal directly
|
|
129
|
+
Any component that renders outside its own DOM subtree (Popover, Modal,
|
|
130
|
+
Toast, Tooltip, Select, Combobox, DatePicker, Dropdown, ContextMenu,
|
|
131
|
+
Drawer, ColorPicker, PhoneInput, Command, Tour, Kanban's drag layer...)
|
|
132
|
+
imports `ThemedPortal` from `theme-engine/ThemedPortal.tsx` and passes
|
|
133
|
+
it an `anchorRef`. Do not hand-roll the `closest('[data-theme]')` +
|
|
134
|
+
`MutationObserver` fix again — that's exactly how it ended up
|
|
135
|
+
duplicated 12 times and missing from 3 components last time.
|
|
136
|
+
|
|
137
|
+
## 7. Every new theme is a `.ts` file satisfying `ThemeTokens`
|
|
138
|
+
Author it with `defineTheme()` — an incomplete theme is a compile
|
|
139
|
+
error, not a rendering bug. Register it via `registerTheme()`. Run
|
|
140
|
+
`npm run build:tokens` to regenerate the CSS. Never hand-write a theme
|
|
141
|
+
CSS file directly.
|
|
142
|
+
(Old bug: themes were hand-authored CSS with no completeness check —
|
|
143
|
+
a theme could silently ship missing a variable.)
|
|
144
|
+
|
|
145
|
+
**Completeness is not the same as distinctness.** A theme derived from
|
|
146
|
+
another via spread (the recommended pattern) can be 100% complete —
|
|
147
|
+
every required key present and non-empty — while still being
|
|
148
|
+
structurally indistinguishable from the theme it was derived from (same
|
|
149
|
+
radius, same shadow, same motion). This is exactly what happened before
|
|
150
|
+
`color.brand` was removed from `ThemeTokens` entirely (see Rule 5b):
|
|
151
|
+
`gradient`, `minimalism`, and `skeuomorphism` each shipped with light's
|
|
152
|
+
brand scale untouched, and it passed every check that existed at the
|
|
153
|
+
time, because completeness and distinctness are different properties.
|
|
154
|
+
Rule 5b's fix (moving brand out of themes entirely) closes that
|
|
155
|
+
specific failure mode structurally — a theme literally cannot
|
|
156
|
+
reintroduce a brand color to forget to change, because the field
|
|
157
|
+
doesn't exist. `tests/theme-distinctness.test.ts` is the remaining
|
|
158
|
+
safety net, now checking radius/shadow/motion/border fingerprints
|
|
159
|
+
instead — run it (it's part of `npm test`) for every new theme, and
|
|
160
|
+
don't treat "it compiles and the completeness test passes" as
|
|
161
|
+
equivalent to "it's structurally different from the theme it was
|
|
162
|
+
derived from."
|
|
163
|
+
|
|
164
|
+
## 7b. Every new accent is a `.ts` file satisfying a full `ColorScale`
|
|
165
|
+
Same pattern as themes, deliberately: create
|
|
166
|
+
`src/tokens/accents/<name>.ts` exporting an `AccentDefinition`
|
|
167
|
+
(`name`, `label`, `scale`), register it via `registerAccent()` in
|
|
168
|
+
`accents.ts`. An incomplete scale throws immediately at registration
|
|
169
|
+
(`assertCompleteScale`), not silently at render time. Accents don't
|
|
170
|
+
need a distinctness test against every theme the way themes do against
|
|
171
|
+
each other — `tests/accent-distinctness.test.ts` only checks accents
|
|
172
|
+
against other accents (no two share a `500` step), since accents and
|
|
173
|
+
themes are different registries serving different purposes.
|
|
174
|
+
|
|
175
|
+
## 8. Every component ships with tests before it's considered done
|
|
176
|
+
This is a **floor, not a checklist to stop at**. The four tests below
|
|
177
|
+
are the minimum every component needs regardless of what it is; a
|
|
178
|
+
complex component (DataGrid, RichTextEditor, Kanban) needs many more
|
|
179
|
+
tests specific to its own behavior on top of these four, not instead
|
|
180
|
+
of them:
|
|
181
|
+
- One test proving the default (no-props) render is correct
|
|
182
|
+
- One test proving `theme` override scopes correctly (skip if the
|
|
183
|
+
component has no visual output, e.g. a hook)
|
|
184
|
+
- One test proving ref forwarding, if applicable
|
|
185
|
+
- If the component portals: one test proving `ThemedPortal` picked up
|
|
186
|
+
the right theme
|
|
187
|
+
(Old bug: zero tests existed anywhere in the 41-component, ~37k-line
|
|
188
|
+
library.)
|
|
189
|
+
|
|
190
|
+
## 9. Docs come last, and prefer an existing framework
|
|
191
|
+
Don't build a documentation site until there are components worth
|
|
192
|
+
documenting. Prefer an existing documentation framework (Storybook,
|
|
193
|
+
Nextra, Starlight, or whatever's current when you get there) unless
|
|
194
|
+
there's a specific, compelling reason to build your own — the rule is
|
|
195
|
+
"don't default to building a platform," not "you may never build one."
|
|
196
|
+
(Old bug: three separate documentation apps totaling ~83,500 lines —
|
|
197
|
+
more than twice the size of the entire component library.)
|
|
198
|
+
|
|
199
|
+
## 10. Public API stability
|
|
200
|
+
Once a prop ships in a released version, it's a promise. Don't rename
|
|
201
|
+
`variant` to `appearance`, don't change what a value means, don't
|
|
202
|
+
change a callback's signature — pick one:
|
|
203
|
+
- **alias** the old name to the new one and deprecate with a console
|
|
204
|
+
warning,
|
|
205
|
+
- or **bump the major version** and document the breaking change.
|
|
206
|
+
Internal architecture (core/, theme-engine/) can be refactored freely;
|
|
207
|
+
anything a consumer imports and passes props to cannot change shape
|
|
208
|
+
silently. This wasn't tested against the old codebase the way the
|
|
209
|
+
other rules were — Nuvox v1 never shipped a public version — but it's
|
|
210
|
+
the rule that protects everything else in this document from being
|
|
211
|
+
undermined the moment real consumers exist.
|
|
212
|
+
|
|
213
|
+
## 11. Every overlay component meets the same accessibility baseline
|
|
214
|
+
Any component that opens/closes, floats, or portals (Modal, Drawer,
|
|
215
|
+
Popover, Dropdown, Select, Combobox, ContextMenu, Tour, Toast,
|
|
216
|
+
Tooltip...) implements, without exception:
|
|
217
|
+
- `Escape` closes it (where closing is a valid action)
|
|
218
|
+
- Focus is trapped inside while open, and returns to the trigger on
|
|
219
|
+
close
|
|
220
|
+
- Correct ARIA role (`dialog`, `listbox`, `menu`, etc.) and state
|
|
221
|
+
attributes (`aria-expanded`, `aria-modal`, ...)
|
|
222
|
+
- Full keyboard navigation appropriate to the component (arrow keys
|
|
223
|
+
for a listbox, tab cycling in a modal)
|
|
224
|
+
This is the same failure shape as Rule 6 (ThemedPortal) waiting to
|
|
225
|
+
happen — a fix that's easy to duplicate 12 times and forget 3 times if
|
|
226
|
+
each component reimplements it locally. Build focus-trap/escape/ARIA
|
|
227
|
+
handling as a shared hook (e.g. `useOverlayBehavior`) the same way
|
|
228
|
+
`ThemedPortal` centralized the theme fix, rather than reimplementing it
|
|
229
|
+
per component.
|
|
230
|
+
|
|
231
|
+
## 12. Components compose primitives, they don't reimplement them
|
|
232
|
+
A `Select` doesn't hand-roll its own text field, its own floating
|
|
233
|
+
panel, and its own option-highlighting logic — it composes an `Input`,
|
|
234
|
+
a `Popover`/`ThemedPortal`, and a shared list/option primitive. If two
|
|
235
|
+
components need the same behavior (an editable text field, a floating
|
|
236
|
+
panel, a keyboard-navigable list), that behavior gets built once and
|
|
237
|
+
both components use it. This isn't backed by a specific bug the way
|
|
238
|
+
the other rules are — Nuvox v1 didn't get far enough architecturally
|
|
239
|
+
for this to bite yet — but it's the same principle as Rule 6 (portal
|
|
240
|
+
theming) and Rule 11 (overlay behavior) applied generally: the more
|
|
241
|
+
logic two components share by composition, the fewer places a fix has
|
|
242
|
+
to be duplicated or forgotten later.
|
|
243
|
+
|
|
244
|
+
---
|
|
245
|
+
|
|
246
|
+
## Adding a new rule to this document
|
|
247
|
+
Before adding a Rule 13, ask: **does this prevent a class of bugs, or
|
|
248
|
+
just one bug?** "Shared size scale," "theme contract," "ref policy" —
|
|
249
|
+
these prevent a class (N components each making the same kind of
|
|
250
|
+
decision independently). "Buttons should use 14px font" is a design
|
|
251
|
+
token, not an architectural invariant — it belongs in a theme file,
|
|
252
|
+
not here. A contract that grows past what people actually remember and
|
|
253
|
+
apply stops being a contract and becomes a document nobody rereads.
|
|
254
|
+
Keep it to rules that would still make sense five years from now,
|
|
255
|
+
regardless of what the component catalog looks like by then.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Nuvox
|
|
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.
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
This project includes icons adapted from Lucide (ISC License) and, via
|
|
26
|
+
Lucide, from Feather (MIT License). See THIRD-PARTY-NOTICES.md for the
|
|
27
|
+
full, required notices.
|
|
28
|
+
|
package/README.md
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# Nuvox
|
|
2
|
+
|
|
3
|
+
A framework-agnostic React component library with a themeable,
|
|
4
|
+
token-driven design system. 41 components, 11 visual themes, 9 named
|
|
5
|
+
accent colors (plus support for any raw CSS color), and 492 icons —
|
|
6
|
+
fully typed, accessible, and built on zero runtime dependencies.
|
|
7
|
+
|
|
8
|
+
## The core idea: theme is shape, color is color
|
|
9
|
+
|
|
10
|
+
A **theme** (`light`, `dark`, `cyberpunk`, `glass`, ...) controls
|
|
11
|
+
radius, shadow, motion, and border treatment. Your **accent color**
|
|
12
|
+
is a completely separate choice — pick a named accent or any raw CSS
|
|
13
|
+
color, and it works identically with every theme:
|
|
14
|
+
|
|
15
|
+
```tsx
|
|
16
|
+
<NuvoxProvider theme="cyberpunk" accent="teal">
|
|
17
|
+
<Button>Cyberpunk shape, teal brand — not tied to each other</Button>
|
|
18
|
+
</NuvoxProvider>
|
|
19
|
+
|
|
20
|
+
<Button color="#ff6600">A raw hex color works too, no setup</Button>
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Install
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
npm install nuvox
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
`react` and `react-dom` are peer dependencies (React 18+) — npm 7+
|
|
30
|
+
installs them automatically if they're missing.
|
|
31
|
+
|
|
32
|
+
## Quick start
|
|
33
|
+
|
|
34
|
+
```tsx
|
|
35
|
+
import { NuvoxProvider, Button } from "nuvox/react";
|
|
36
|
+
import "nuvox/themes.css"; // theme + accent CSS variables
|
|
37
|
+
import "nuvox/styles.css"; // component styles
|
|
38
|
+
|
|
39
|
+
function App() {
|
|
40
|
+
return (
|
|
41
|
+
<NuvoxProvider theme="dark" accent="teal">
|
|
42
|
+
<Button>Hello Nuvox</Button>
|
|
43
|
+
</NuvoxProvider>
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Both stylesheets are required — `themes.css` defines the CSS
|
|
49
|
+
variables, `styles.css` reads them into visible styles.
|
|
50
|
+
|
|
51
|
+
## What's in the box
|
|
52
|
+
|
|
53
|
+
- **41 components** across forms (Input, Select, Checkbox, Slider...),
|
|
54
|
+
overlays (Modal, Popover, Dropdown, Drawer...), composite widgets
|
|
55
|
+
(Combobox, DatePicker, CommandPalette...), and navigation
|
|
56
|
+
(Tabs, Accordion, Breadcrumb, Pagination...)
|
|
57
|
+
- **11 themes** (light, dark, glass, dark-glass, minimalism,
|
|
58
|
+
brutalism, neobrutalism, cyberpunk, gradient, material,
|
|
59
|
+
skeuomorphism) — switchable at runtime with no re-render logic
|
|
60
|
+
- **9 named accent colors**, or drop in any raw CSS color with zero
|
|
61
|
+
registration
|
|
62
|
+
- **492 icons**, tree-shakeable, each accepting `size` and `color`
|
|
63
|
+
- Full TypeScript support out of the box — no separate `@types`
|
|
64
|
+
package needed
|
|
65
|
+
- Accessible by default: every overlay component handles focus
|
|
66
|
+
trapping, Escape-to-close, and correct ARIA roles/states
|
|
67
|
+
|
|
68
|
+
## Switch themes and accents independently
|
|
69
|
+
|
|
70
|
+
```tsx
|
|
71
|
+
<NuvoxProvider theme="dark" accent="indigo">
|
|
72
|
+
<Button>Ambient dark + indigo</Button>
|
|
73
|
+
<Button theme="glass">Glass shape, still indigo accent</Button>
|
|
74
|
+
<Button color="teal">Dark shape, teal accent instead</Button>
|
|
75
|
+
</NuvoxProvider>
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
You can also register your own theme or accent from your own code —
|
|
79
|
+
see the [docs site](#docs--examples) for the full guide.
|
|
80
|
+
|
|
81
|
+
## Docs & examples
|
|
82
|
+
|
|
83
|
+
Every component has a live, interactive example alongside its API
|
|
84
|
+
reference in the docs site:
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
npm run docs:install # first time only
|
|
88
|
+
npm run docs:dev # → http://localhost:3000
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
- `/docs` — installation and setup
|
|
92
|
+
- `/docs/components` — every component, with live previews
|
|
93
|
+
- `/docs/themes` — the full theme/accent catalog, try them live
|
|
94
|
+
- `/docs/icons` — browse and search all 492 icons
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
## License
|
|
98
|
+
|
|
99
|
+
MIT
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# Third-Party Notices
|
|
2
|
+
|
|
3
|
+
Nuvox's own code is MIT licensed — see [`LICENSE`](./LICENSE). Some icons in
|
|
4
|
+
`src/icons/` are adapted from the [Lucide](https://lucide.dev) icon set,
|
|
5
|
+
which requires its own license notice to travel with the code. That notice
|
|
6
|
+
is reproduced below, unmodified, as required.
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
ISC License
|
|
11
|
+
|
|
12
|
+
Copyright (c) 2026 Lucide Icons and Contributors
|
|
13
|
+
|
|
14
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
|
15
|
+
purpose with or without fee is hereby granted, provided that the above
|
|
16
|
+
copyright notice and this permission notice appear in all copies.
|
|
17
|
+
|
|
18
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
19
|
+
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
20
|
+
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
21
|
+
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
22
|
+
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
23
|
+
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
24
|
+
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
The following Lucide icons are derived from the Feather project:
|
|
29
|
+
|
|
30
|
+
airplay, alert-circle, alert-octagon, alert-triangle, aperture, arrow-down-circle, arrow-down-left, arrow-down-right, arrow-down, arrow-left-circle, arrow-left, arrow-right-circle, arrow-right, arrow-up-circle, arrow-up-left, arrow-up-right, arrow-up, at-sign, calendar, cast, check, chevron-down, chevron-left, chevron-right, chevron-up, chevrons-down, chevrons-left, chevrons-right, chevrons-up, circle, clipboard, clock, code, columns, command, compass, corner-down-left, corner-down-right, corner-left-down, corner-left-up, corner-right-down, corner-right-up, corner-up-left, corner-up-right, crosshair, database, divide-circle, divide-square, dollar-sign, download, external-link, feather, frown, hash, headphones, help-circle, info, italic, key, layout, life-buoy, link-2, link, loader, lock, log-in, log-out, maximize, meh, minimize, minimize-2, minus-circle, minus-square, minus, monitor, moon, more-horizontal, more-vertical, move, music, navigation-2, navigation, octagon, pause-circle, percent, plus-circle, plus-square, plus, power, radio, rss, search, server, share, shopping-bag, sidebar, smartphone, smile, square, table-2, tablet, target, terminal, trash-2, trash, triangle, tv, type, upload, x-circle, x-octagon, x-square, x, zoom-in, zoom-out
|
|
31
|
+
|
|
32
|
+
The MIT License (MIT) (for the icons listed above)
|
|
33
|
+
|
|
34
|
+
Copyright (c) 2013-present Cole Bemis
|
|
35
|
+
|
|
36
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
37
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
38
|
+
in the Software without restriction, including without limitation the rights
|
|
39
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
40
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
41
|
+
furnished to do so, subject to the following conditions:
|
|
42
|
+
|
|
43
|
+
The above copyright notice and this permission notice shall be included in all
|
|
44
|
+
copies or substantial portions of the Software.
|
|
45
|
+
|
|
46
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
47
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
48
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
49
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
50
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
51
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
52
|
+
SOFTWARE.
|