@djangocfg/ui-core 2.1.443 → 2.1.444
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
CHANGED
|
@@ -91,12 +91,78 @@ Imports stay flat — group folders are organisational.
|
|
|
91
91
|
|
|
92
92
|
## Router adapters
|
|
93
93
|
|
|
94
|
-
The router-aware
|
|
94
|
+
The router-aware primitives (`Sidebar`, `Link`, `SSRPagination`, and the
|
|
95
|
+
`useRouter` / `useNavigate` / `useLocation` hooks) navigate through a **pluggable
|
|
96
|
+
adapter**, so the same components work under any host. ui-core itself is
|
|
97
|
+
router-agnostic and has **zero router dependency** — pick the adapter for your
|
|
98
|
+
host and mount it once near the root.
|
|
95
99
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
|
99
|
-
|
|
100
|
+
### Which adapter for which host
|
|
101
|
+
|
|
102
|
+
| Host | Mount this | Extra dependency you install |
|
|
103
|
+
|---|---|---|
|
|
104
|
+
| **Next.js** (App Router) | `NextRouterAdapter` from `@djangocfg/ui-core/adapters/nextjs` | `next` (you already have it) |
|
|
105
|
+
| **Vite / React-Router SPA** | `ReactRouterProvider` from `@djangocfg/ui-core/adapters/react-router` | `react-router` **≥7** — add to your app's `dependencies` |
|
|
106
|
+
| **Wails / Electron / plain React / Storybook** | **nothing** — the default History-API adapter is used automatically | none (zero deps) |
|
|
107
|
+
|
|
108
|
+
`next` and `react-router` are **optional peers**: the base package never imports
|
|
109
|
+
them. Only the matching sub-path entry does, so the dep is resolved *only* when
|
|
110
|
+
you import that adapter. Never add both — import the one sub-path for your host.
|
|
111
|
+
|
|
112
|
+
### Where to mount it
|
|
113
|
+
|
|
114
|
+
Both provider adapters wrap a subtree; every ui-core router call inside it flows
|
|
115
|
+
through your app's router. Mount **once**, near the root:
|
|
116
|
+
|
|
117
|
+
```tsx
|
|
118
|
+
// Next.js — inside the client provider stack (below your i18n provider)
|
|
119
|
+
import { NextRouterAdapter } from '@djangocfg/ui-core/adapters/nextjs';
|
|
120
|
+
<NextRouterAdapter><AppLayout>{children}</AppLayout></NextRouterAdapter>
|
|
121
|
+
|
|
122
|
+
// Vite / React-Router — INSIDE the router context (an element RouterProvider
|
|
123
|
+
// renders, e.g. a layout route wrapping <Outlet/>), NOT around <RouterProvider>.
|
|
124
|
+
import { ReactRouterProvider } from '@djangocfg/ui-core/adapters/react-router';
|
|
125
|
+
function Shell() {
|
|
126
|
+
return <ReactRouterProvider><Layout><Outlet /></Layout></ReactRouterProvider>;
|
|
127
|
+
}
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
> **React-Router gotcha:** mounting `ReactRouterProvider` *around*
|
|
131
|
+
> `<RouterProvider>` throws — that subtree has no router context, so
|
|
132
|
+
> `useNavigate` fails. Always mount it *inside*.
|
|
133
|
+
|
|
134
|
+
### What happens if you skip it
|
|
135
|
+
|
|
136
|
+
Without an adapter, hooks fall back to the **default History-API adapter**
|
|
137
|
+
(`window.history.pushState` + `window.location`). This is intentional and works
|
|
138
|
+
everywhere with zero deps — but in a *framework* host it silently loses the
|
|
139
|
+
framework's routing behavior:
|
|
140
|
+
|
|
141
|
+
- **Next.js without `NextRouterAdapter`:** no RSC refetch on navigation, no route
|
|
142
|
+
loader, no prefetch. Links still change the URL, but the App Router doesn't
|
|
143
|
+
react. → always mount it in Next apps.
|
|
144
|
+
- **React-Router without `ReactRouterProvider`:** programmatic `useNavigate` /
|
|
145
|
+
`<Link>` mutate `window.history` directly and bypass the `<Outlet/>` swap, so
|
|
146
|
+
navigation desyncs from the data router. → always mount it in RR SPAs.
|
|
147
|
+
|
|
148
|
+
For a plain SPA (Wails/Electron/CRA) the History-API default *is* the right
|
|
149
|
+
backend — mount nothing.
|
|
150
|
+
|
|
151
|
+
### Link adapters (optional, Next.js)
|
|
152
|
+
|
|
153
|
+
By default `<Link>` from `@djangocfg/ui-core/components` renders a plain `<a>`.
|
|
154
|
+
To route Next's prefetch / RSC handling through it, also mount `NextLinkProvider`
|
|
155
|
+
alongside `NextRouterAdapter`. For locale-prefixed hrefs with `next-intl`, pass
|
|
156
|
+
`createNextIntlLinkAdapter(IntlLink)` as `AppLayout`'s `linkAdapter` — both live
|
|
157
|
+
in `@djangocfg/ui-core/adapters/nextjs`.
|
|
158
|
+
|
|
159
|
+
> **Deeper dive:** full adapter reference, the `useRouter` / `useNavigate` /
|
|
160
|
+
> `useLocation` / `useQueryState` hook surface, and a ~20-line recipe for
|
|
161
|
+
> **custom** routers (TanStack Router, wouter, Remix, custom transports) live in
|
|
162
|
+
> [`src/hooks/router/README.md`](./src/hooks/router/README.md).
|
|
163
|
+
|
|
164
|
+
> Using `@djangocfg/layouts`? `BaseApp` already mounts the Next adapters for you
|
|
165
|
+
> — you don't wire them by hand.
|
|
100
166
|
|
|
101
167
|
## Theming (`/styles`)
|
|
102
168
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@djangocfg/ui-core",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.444",
|
|
4
4
|
"description": "Pure React UI component library without Next.js dependencies - for Electron, Vite, CRA apps",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ui-components",
|
|
@@ -116,7 +116,7 @@
|
|
|
116
116
|
"check": "tsc --noEmit"
|
|
117
117
|
},
|
|
118
118
|
"peerDependencies": {
|
|
119
|
-
"@djangocfg/i18n": "^2.1.
|
|
119
|
+
"@djangocfg/i18n": "^2.1.444",
|
|
120
120
|
"consola": "^3.4.2",
|
|
121
121
|
"lucide-react": "^0.545.0",
|
|
122
122
|
"moment": "^2.30.1",
|
|
@@ -194,8 +194,8 @@
|
|
|
194
194
|
"@chenglou/pretext": "*"
|
|
195
195
|
},
|
|
196
196
|
"devDependencies": {
|
|
197
|
-
"@djangocfg/i18n": "^2.1.
|
|
198
|
-
"@djangocfg/typescript-config": "^2.1.
|
|
197
|
+
"@djangocfg/i18n": "^2.1.444",
|
|
198
|
+
"@djangocfg/typescript-config": "^2.1.444",
|
|
199
199
|
"@types/node": "^25.2.3",
|
|
200
200
|
"@types/react": "^19.2.15",
|
|
201
201
|
"@types/react-dom": "^19.2.3",
|
|
@@ -18,11 +18,13 @@ export const iosPreset: ThemePreset = {
|
|
|
18
18
|
'accent-foreground': 'hsl(220 9% 12%)',
|
|
19
19
|
destructive: 'hsl(0 100% 58%)',
|
|
20
20
|
'destructive-foreground': 'hsl(0 0% 100%)',
|
|
21
|
-
border: 'hsl(220 9%
|
|
21
|
+
border: 'hsl(220 9% 85%)', // nudged darker so outlines read on white cards
|
|
22
22
|
input: 'hsl(220 9% 88%)',
|
|
23
23
|
ring: 'hsl(211 100% 50%)',
|
|
24
|
-
divider: 'hsl(220 9%
|
|
24
|
+
divider: 'hsl(220 9% 88%)', // hairline, a touch lighter than border
|
|
25
25
|
radius: '0.75rem',
|
|
26
|
+
// Modal scrim — light dim, iOS sheets keep the backdrop visible.
|
|
27
|
+
overlay: 'hsl(220 9% 10% / 0.32)',
|
|
26
28
|
'sidebar-background': 'hsl(0 0% 99%)',
|
|
27
29
|
'sidebar-foreground': 'hsl(220 9% 12%)',
|
|
28
30
|
'sidebar-primary': 'hsl(211 100% 50%)',
|
|
@@ -55,25 +57,31 @@ export const iosPreset: ThemePreset = {
|
|
|
55
57
|
dark: {
|
|
56
58
|
background: 'hsl(240 6% 10%)',
|
|
57
59
|
foreground: 'hsl(0 0% 96%)',
|
|
58
|
-
|
|
60
|
+
// Lifted 14→16 for a clearer float off the dark page (see macos rationale).
|
|
61
|
+
card: 'hsl(240 6% 16%)',
|
|
59
62
|
'card-foreground': 'hsl(0 0% 96%)',
|
|
60
|
-
|
|
63
|
+
// Kept a distinct step above card.
|
|
64
|
+
popover: 'hsl(240 6% 20%)',
|
|
61
65
|
'popover-foreground': 'hsl(0 0% 96%)',
|
|
62
66
|
primary: 'hsl(211 100% 55%)',
|
|
63
67
|
'primary-foreground': 'hsl(0 0% 100%)',
|
|
64
68
|
secondary: 'hsl(240 5% 26%)',
|
|
65
69
|
'secondary-foreground': 'hsl(0 0% 96%)',
|
|
66
|
-
|
|
70
|
+
// Recessed surface — below the page (bg 10%) and below card (16%), so
|
|
71
|
+
// bg-muted rails/chips read as sunk, not level with the panel.
|
|
72
|
+
muted: 'hsl(240 5% 13%)',
|
|
67
73
|
'muted-foreground': 'hsl(240 5% 64%)',
|
|
68
74
|
accent: 'hsl(240 5% 18%)',
|
|
69
75
|
'accent-foreground': 'hsl(0 0% 96%)',
|
|
70
76
|
destructive: 'hsl(0 100% 67%)',
|
|
71
77
|
'destructive-foreground': 'hsl(0 0% 100%)',
|
|
72
|
-
border: 'hsl(240 5%
|
|
78
|
+
border: 'hsl(240 5% 27%)', // raised so outlines read on the dark page
|
|
73
79
|
input: 'hsl(240 5% 22%)',
|
|
74
80
|
ring: 'hsl(211 100% 55%)',
|
|
75
|
-
divider: 'hsl(240 5%
|
|
81
|
+
divider: 'hsl(240 5% 21%)', // hairline, softer than border
|
|
76
82
|
radius: '0.75rem',
|
|
83
|
+
// Modal scrim — lighter than base 0.7 so the dark page still reads behind.
|
|
84
|
+
overlay: 'hsl(0 0% 0% / 0.55)',
|
|
77
85
|
'sidebar-background': 'hsl(240 6% 8%)',
|
|
78
86
|
'sidebar-foreground': 'hsl(0 0% 96%)',
|
|
79
87
|
'sidebar-primary': 'hsl(211 100% 55%)',
|
|
@@ -52,14 +52,18 @@ export const macosPreset: ThemePreset = {
|
|
|
52
52
|
// systemRed #FF3B30
|
|
53
53
|
destructive: 'hsl(2 100% 59%)',
|
|
54
54
|
'destructive-foreground': 'hsl(0 0% 100%)',
|
|
55
|
-
// Apple opaque separator: rgba(60,60,67,0.29) on white ≈ #C6C6C8 → HSL 240 3% 78
|
|
56
|
-
//
|
|
57
|
-
|
|
55
|
+
// Apple opaque separator: rgba(60,60,67,0.29) on white ≈ #C6C6C8 → HSL 240 3% 78%.
|
|
56
|
+
// Nudged a touch darker (78→74) so control/panel outlines actually read on
|
|
57
|
+
// white cards — the pure hairline vanished on elevated surfaces.
|
|
58
|
+
border: 'hsl(240 3% 74%)',
|
|
58
59
|
input: 'hsl(240 8% 93%)',
|
|
59
60
|
// Row hairline — a touch lighter than border so it reads on white cards
|
|
60
|
-
divider: 'hsl(240 4%
|
|
61
|
+
divider: 'hsl(240 4% 83%)',
|
|
61
62
|
ring: 'hsl(211 100% 50%)',
|
|
62
63
|
radius: '0.625rem',
|
|
64
|
+
// Modal scrim — lighter than the base 0.6 so the light grouped canvas still
|
|
65
|
+
// reads behind the dialog (Apple sheets dim, they don't black out).
|
|
66
|
+
overlay: 'hsl(240 6% 10% / 0.32)',
|
|
63
67
|
// Sidebar: slighly darker than page, matches macOS sidebar material
|
|
64
68
|
'sidebar-background': 'hsl(240 12% 94%)',
|
|
65
69
|
'sidebar-foreground': 'hsl(0 0% 7%)',
|
|
@@ -97,11 +101,13 @@ export const macosPreset: ThemePreset = {
|
|
|
97
101
|
// Deeper than stock #1C1C1E — matches Sequoia dark finder/mail chrome (#141414)
|
|
98
102
|
background: 'hsl(240 5% 8%)',
|
|
99
103
|
foreground: 'hsl(0 0% 95%)',
|
|
100
|
-
// secondarySystemBackground #1E1E20 — cards
|
|
101
|
-
|
|
104
|
+
// secondarySystemBackground #1E1E20 — cards lifted above the #141414 page.
|
|
105
|
+
// Bumped 12→15 so panels (dialog body, settings cards) visibly float off the
|
|
106
|
+
// near-black canvas; on L≈8 a +4 lift is too subtle for the eye (Weber).
|
|
107
|
+
card: 'hsl(240 3% 15%)',
|
|
102
108
|
'card-foreground': 'hsl(0 0% 95%)',
|
|
103
|
-
// #2C2C2E — popovers, menus, dropdowns
|
|
104
|
-
popover: 'hsl(240 3%
|
|
109
|
+
// #2C2C2E — popovers, menus, dropdowns; kept a clear step above card.
|
|
110
|
+
popover: 'hsl(240 3% 20%)',
|
|
105
111
|
'popover-foreground': 'hsl(0 0% 95%)',
|
|
106
112
|
// systemBlue dark #0A84FF
|
|
107
113
|
primary: 'hsl(211 100% 58%)',
|
|
@@ -109,8 +115,12 @@ export const macosPreset: ThemePreset = {
|
|
|
109
115
|
// Elevated neutral surface — slightly lighter than card
|
|
110
116
|
secondary: 'hsl(240 3% 20%)',
|
|
111
117
|
'secondary-foreground': 'hsl(0 0% 88%)',
|
|
112
|
-
//
|
|
113
|
-
muted
|
|
118
|
+
// Recessed surface — a notch BELOW the page (bg 8%), like default's muted.
|
|
119
|
+
// `--muted` is the "sunk" surface (input rest, nav rails, chips): it must
|
|
120
|
+
// read as recessed vs `--card` (15%), not raised. Apple's #3A3A3C
|
|
121
|
+
// tertiary fill (23%) sat *above* card and inverted the depth — the
|
|
122
|
+
// SettingsDialog nav rail (bg-muted) then looked lighter than its panel.
|
|
123
|
+
muted: 'hsl(240 3% 11%)',
|
|
114
124
|
'muted-foreground': 'hsl(240 4% 58%)',
|
|
115
125
|
// Blue-tinted hover (Apple active state in dark)
|
|
116
126
|
accent: 'hsl(211 25% 19%)',
|
|
@@ -118,13 +128,20 @@ export const macosPreset: ThemePreset = {
|
|
|
118
128
|
// systemRed dark #FF453A
|
|
119
129
|
destructive: 'hsl(3 100% 62%)',
|
|
120
130
|
'destructive-foreground': 'hsl(0 0% 100%)',
|
|
121
|
-
// Separator dark: rgba(84,84,88,0.36) —
|
|
122
|
-
|
|
123
|
-
|
|
131
|
+
// Separator dark: rgba(84,84,88,0.36). Raised 22→28 — Apple's hairline is
|
|
132
|
+
// authored for content-on-content, but on the near-black #141414 page it was
|
|
133
|
+
// invisible, so control/panel outlines had no edge. 28 keeps it a hairline
|
|
134
|
+
// while actually drawing against the dark canvas.
|
|
135
|
+
border: 'hsl(240 3% 28%)',
|
|
136
|
+
input: 'hsl(240 3% 22%)',
|
|
124
137
|
// Row hairline — softer than border so dark rows don't read as a hard rule
|
|
125
|
-
divider: 'hsl(240 3%
|
|
138
|
+
divider: 'hsl(240 3% 22%)',
|
|
126
139
|
ring: 'hsl(211 100% 58%)',
|
|
127
140
|
radius: '0.625rem',
|
|
141
|
+
// Modal scrim — a touch lighter than the base 0.7. The page is already near
|
|
142
|
+
// black (L≈8); 0.55 dims it enough to separate the dialog without crushing
|
|
143
|
+
// the backdrop into a flat black slab the panel can't lift off of.
|
|
144
|
+
overlay: 'hsl(0 0% 0% / 0.55)',
|
|
128
145
|
// Sidebar: near-black floor — #0D0D0F
|
|
129
146
|
'sidebar-background': 'hsl(240 5% 5%)',
|
|
130
147
|
'sidebar-foreground': 'hsl(0 0% 90%)',
|
|
@@ -132,7 +149,9 @@ export const macosPreset: ThemePreset = {
|
|
|
132
149
|
'sidebar-primary-foreground': 'hsl(0 0% 100%)',
|
|
133
150
|
'sidebar-accent': 'hsl(211 22% 16%)',
|
|
134
151
|
'sidebar-accent-foreground': 'hsl(211 100% 72%)',
|
|
135
|
-
|
|
152
|
+
// Raised 15→22 to match --border: the sidebar edge was lost against the
|
|
153
|
+
// near-black rail + page.
|
|
154
|
+
'sidebar-border': 'hsl(240 3% 22%)',
|
|
136
155
|
'sidebar-ring': 'hsl(211 100% 58%)',
|
|
137
156
|
// Chart: brighter variants for contrast on dark
|
|
138
157
|
'chart-1': 'hsl(211 100% 58%)',
|
|
@@ -47,6 +47,8 @@ export const windowsPreset: ThemePreset = {
|
|
|
47
47
|
// Hairline between rows — slightly lighter than border
|
|
48
48
|
divider: 'hsl(0 0% 84%)',
|
|
49
49
|
ring: 'hsl(210 100% 45%)',
|
|
50
|
+
// Fluent SmokeFillColorDefault — content dialogs dim the layer beneath.
|
|
51
|
+
overlay: 'hsl(0 0% 0% / 0.4)',
|
|
50
52
|
// WinUI 3: 4px controls, 8px cards/dialogs
|
|
51
53
|
radius: '0.375rem',
|
|
52
54
|
'sidebar-background': 'hsl(0 0% 92%)',
|
|
@@ -101,7 +103,9 @@ export const windowsPreset: ThemePreset = {
|
|
|
101
103
|
'primary-foreground': 'hsl(0 0% 9%)',
|
|
102
104
|
secondary: 'hsl(0 0% 22%)',
|
|
103
105
|
'secondary-foreground': 'hsl(0 0% 90%)',
|
|
104
|
-
|
|
106
|
+
// Recessed surface — below the page (bg 13%) and below card (17%), so
|
|
107
|
+
// bg-muted rails/chips read as sunk, not raised above the panel.
|
|
108
|
+
muted: 'hsl(0 0% 15%)',
|
|
105
109
|
'muted-foreground': 'hsl(0 0% 62%)',
|
|
106
110
|
accent: 'hsl(210 25% 20%)',
|
|
107
111
|
'accent-foreground': 'hsl(200 100% 75%)',
|
|
@@ -113,6 +117,8 @@ export const windowsPreset: ThemePreset = {
|
|
|
113
117
|
// Hairline between rows — softer than border
|
|
114
118
|
divider: 'hsl(0 0% 22%)',
|
|
115
119
|
ring: 'hsl(200 100% 69%)',
|
|
120
|
+
// Fluent SmokeFillColorDefault dark — dims the dark page behind the dialog.
|
|
121
|
+
overlay: 'hsl(0 0% 0% / 0.55)',
|
|
116
122
|
radius: '0.375rem',
|
|
117
123
|
// NavigationView pane background
|
|
118
124
|
'sidebar-background': 'hsl(0 0% 10%)',
|
|
@@ -35,8 +35,12 @@ export type ThemeCssVarColorKey =
|
|
|
35
35
|
* Layout / focus tokens — `radius` is a CSS length, the rest are wrapped colors.
|
|
36
36
|
* `divider` is the hairline-between-rows token (deliberately *lighter* than
|
|
37
37
|
* `--card` so it stays visible on elevated surfaces — see styles README).
|
|
38
|
+
* `overlay` is the modal scrim behind dialogs/drawers/sheets — a wrapped color
|
|
39
|
+
* that OWNS its own opacity (`hsl(0 0% 0% / 0.55)`). Brand presets that set a
|
|
40
|
+
* custom `background` should set `overlay` too: on a near-black canvas the base
|
|
41
|
+
* 0.6/0.7 scrim crushes the backdrop flat and the dialog can't lift off it.
|
|
38
42
|
*/
|
|
39
|
-
export type ThemeCssVarChromeKey = 'border' | 'input' | 'divider' | 'ring' | 'radius';
|
|
43
|
+
export type ThemeCssVarChromeKey = 'border' | 'input' | 'divider' | 'overlay' | 'ring' | 'radius';
|
|
40
44
|
|
|
41
45
|
/**
|
|
42
46
|
* Status surfaces — each role has a 4-token set (icon/accent, banner background,
|