@djangocfg/ui-core 2.1.209 → 2.1.212
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 +38 -15
- package/package.json +4 -4
- package/src/styles/README.md +50 -0
- package/src/styles/palette/index.ts +2 -0
- package/src/styles/palette/useThemePalette.ts +90 -35
package/README.md
CHANGED
|
@@ -65,35 +65,58 @@ pnpm add @djangocfg/ui-core
|
|
|
65
65
|
|
|
66
66
|
## Theme Palette Hooks
|
|
67
67
|
|
|
68
|
-
Hooks for accessing theme colors from CSS variables (useful for charts, diagrams, etc.):
|
|
68
|
+
Hooks for accessing theme colors from CSS variables (useful for Canvas, SVG, charts, diagrams, etc.):
|
|
69
69
|
|
|
70
|
-
| Hook | Description |
|
|
71
|
-
|
|
72
|
-
| `useThemePalette()` | Full color palette from CSS variables |
|
|
73
|
-
| `
|
|
74
|
-
| `
|
|
70
|
+
| Hook / Util | Description |
|
|
71
|
+
|-------------|-------------|
|
|
72
|
+
| `useThemePalette()` | Full hex color palette from CSS variables |
|
|
73
|
+
| `useThemeColor(var, opacity?)` | Single color by CSS var name — lighter alternative |
|
|
74
|
+
| `useStylePresets()` | Pre-built `{ fill, stroke, color }` configs for diagrams |
|
|
75
|
+
| `useBoxColors()` | Semi-transparent RGBA colors for boxes/containers |
|
|
76
|
+
| `alpha(hex, opacity)` | Convert hex color to `rgba()` string |
|
|
75
77
|
|
|
76
78
|
```tsx
|
|
77
|
-
import {
|
|
79
|
+
import {
|
|
80
|
+
useThemePalette,
|
|
81
|
+
useThemeColor,
|
|
82
|
+
useStylePresets,
|
|
83
|
+
useBoxColors,
|
|
84
|
+
alpha,
|
|
85
|
+
} from '@djangocfg/ui-core/styles/palette';
|
|
86
|
+
|
|
87
|
+
function MyCanvas() {
|
|
88
|
+
const palette = useThemePalette();
|
|
89
|
+
|
|
90
|
+
// Use in Canvas / inline styles
|
|
91
|
+
ctx.fillStyle = palette.primary; // '#a855f7' (theme-aware)
|
|
92
|
+
ctx.fillStyle = alpha(palette.primary, 0.3); // 'rgba(168, 85, 247, 0.3)'
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function MyWaveform() {
|
|
96
|
+
// Lighter: only subscribes to 'primary', not the entire palette
|
|
97
|
+
const primary = useThemeColor('primary'); // '#a855f7'
|
|
98
|
+
const primaryFaded = useThemeColor('primary', 0.3); // 'rgba(168, 85, 247, 0.3)'
|
|
99
|
+
const errorBackground = useThemeColor('destructive', 0.1);
|
|
100
|
+
}
|
|
78
101
|
|
|
79
102
|
function MyChart() {
|
|
80
103
|
const presets = useStylePresets();
|
|
81
|
-
// presets.
|
|
82
|
-
// presets.
|
|
83
|
-
// presets.danger
|
|
84
|
-
// presets.
|
|
85
|
-
// presets.
|
|
104
|
+
// presets.primary = { fill: '#a855f7', stroke: '#a855f7', color: '#fff' }
|
|
105
|
+
// presets.success = { fill: '#22c55e', stroke: '#22c55e', color: '#fff' }
|
|
106
|
+
// presets.danger = { fill: '#ef4444', stroke: '#ef4444', color: '#fff' }
|
|
107
|
+
// presets.warning = { fill: '#f59e0b', stroke: '#f59e0b', color: '#fff' }
|
|
108
|
+
// presets.info = { fill: '#3b82f6', stroke: '#3b82f6', color: '#fff' }
|
|
86
109
|
|
|
87
110
|
const boxes = useBoxColors();
|
|
88
|
-
// boxes.primary = 'rgba(
|
|
89
|
-
// boxes.success = 'rgba(34, 197, 94, 0.
|
|
111
|
+
// boxes.primary = 'rgba(168, 85, 247, 0.15)' (0.3 in dark mode)
|
|
112
|
+
// boxes.success = 'rgba(34, 197, 94, 0.15)'
|
|
90
113
|
// etc.
|
|
91
114
|
|
|
92
115
|
return <Chart colors={[presets.success.fill, presets.warning.fill]} />;
|
|
93
116
|
}
|
|
94
117
|
```
|
|
95
118
|
|
|
96
|
-
### Color Utilities
|
|
119
|
+
### Color Utilities (HSL conversion)
|
|
97
120
|
|
|
98
121
|
```tsx
|
|
99
122
|
import { hslToHex, hslToRgbString, hslToRgba } from '@djangocfg/ui-core/styles/palette';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@djangocfg/ui-core",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.212",
|
|
4
4
|
"description": "Pure React UI component library without Next.js dependencies - for Electron, Vite, CRA apps",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ui-components",
|
|
@@ -81,7 +81,7 @@
|
|
|
81
81
|
"playground": "playground dev"
|
|
82
82
|
},
|
|
83
83
|
"peerDependencies": {
|
|
84
|
-
"@djangocfg/i18n": "^2.1.
|
|
84
|
+
"@djangocfg/i18n": "^2.1.212",
|
|
85
85
|
"react-device-detect": "^2.2.3",
|
|
86
86
|
"consola": "^3.4.2",
|
|
87
87
|
"lucide-react": "^0.545.0",
|
|
@@ -143,9 +143,9 @@
|
|
|
143
143
|
"vaul": "1.1.2"
|
|
144
144
|
},
|
|
145
145
|
"devDependencies": {
|
|
146
|
-
"@djangocfg/i18n": "^2.1.
|
|
146
|
+
"@djangocfg/i18n": "^2.1.212",
|
|
147
147
|
"@djangocfg/playground": "workspace:*",
|
|
148
|
-
"@djangocfg/typescript-config": "^2.1.
|
|
148
|
+
"@djangocfg/typescript-config": "^2.1.212",
|
|
149
149
|
"@types/node": "^24.7.2",
|
|
150
150
|
"@types/react": "^19.1.0",
|
|
151
151
|
"@types/react-dom": "^19.1.0",
|
package/src/styles/README.md
CHANGED
|
@@ -11,6 +11,11 @@ styles/
|
|
|
11
11
|
├── base.css # Base element styles
|
|
12
12
|
├── utilities.css # Custom utility classes
|
|
13
13
|
├── sources.css # Source detection for Tailwind v4 monorepo
|
|
14
|
+
├── palette/ # Theme palette hooks & utilities
|
|
15
|
+
│ ├── index.ts # Exports
|
|
16
|
+
│ ├── types.ts # ThemePalette, StylePresets, BoxColors interfaces
|
|
17
|
+
│ ├── useThemePalette.ts # useThemePalette, useStylePresets, useBoxColors, alpha
|
|
18
|
+
│ └── utils.ts # hslToHex, hslToRgbString, hslToRgba
|
|
14
19
|
└── theme/
|
|
15
20
|
├── tokens.css # @theme block with CSS custom properties
|
|
16
21
|
├── light.css # Light theme variables
|
|
@@ -204,6 +209,51 @@ Z-index utilities (`z-50`, `z-100`) require `--z-index-*` variables:
|
|
|
204
209
|
<div className="aspect-square rounded-full overflow-hidden w-10 h-10">
|
|
205
210
|
```
|
|
206
211
|
|
|
212
|
+
## Theme Palette (Programmatic Color Access)
|
|
213
|
+
|
|
214
|
+
For Canvas, SVG, Mermaid, and other contexts that don't support CSS variables, use the palette hooks:
|
|
215
|
+
|
|
216
|
+
```tsx
|
|
217
|
+
import {
|
|
218
|
+
useThemePalette,
|
|
219
|
+
useStylePresets,
|
|
220
|
+
useBoxColors,
|
|
221
|
+
alpha,
|
|
222
|
+
} from '@djangocfg/ui-core/styles/palette';
|
|
223
|
+
|
|
224
|
+
// Hex colors from CSS variables (updates on theme switch)
|
|
225
|
+
const palette = useThemePalette();
|
|
226
|
+
ctx.fillStyle = palette.primary; // '#a855f7'
|
|
227
|
+
ctx.fillStyle = alpha(palette.primary, 0.3); // 'rgba(168, 85, 247, 0.3)'
|
|
228
|
+
|
|
229
|
+
// Pre-built { fill, stroke, color } for diagrams
|
|
230
|
+
const presets = useStylePresets();
|
|
231
|
+
presets.success // { fill: '#22c55e', stroke: '#22c55e', color: '#fff' }
|
|
232
|
+
presets.danger // { fill: '#ef4444', stroke: '#ef4444', color: '#fff' }
|
|
233
|
+
|
|
234
|
+
// Semi-transparent backgrounds (adapts opacity to light/dark)
|
|
235
|
+
const boxes = useBoxColors();
|
|
236
|
+
boxes.primary // 'rgba(168, 85, 247, 0.15)' in light, 0.3 in dark
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
### `alpha(hex, opacity)`
|
|
240
|
+
|
|
241
|
+
Convert any hex color to `rgba()` — works with any value from `ThemePalette`:
|
|
242
|
+
|
|
243
|
+
```tsx
|
|
244
|
+
alpha(palette.primary, 0.3) // 'rgba(168, 85, 247, 0.3)'
|
|
245
|
+
alpha(palette.destructive, 0.1) // useful for error backgrounds
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
### Raw HSL Utilities
|
|
249
|
+
|
|
250
|
+
```tsx
|
|
251
|
+
import { hslToHex, hslToRgbString, hslToRgba } from '@djangocfg/ui-core/styles/palette';
|
|
252
|
+
|
|
253
|
+
hslToHex('265 89% 78%') // '#c084fc'
|
|
254
|
+
hslToRgba('265 89% 78%', 0.2) // 'rgba(192, 132, 252, 0.2)'
|
|
255
|
+
```
|
|
256
|
+
|
|
207
257
|
## What to Avoid
|
|
208
258
|
|
|
209
259
|
- Custom utilities like: `section-padding`, `animate-*`, `shadow-brand`
|
|
@@ -11,19 +11,53 @@
|
|
|
11
11
|
|
|
12
12
|
import { useMemo } from 'react';
|
|
13
13
|
import { useResolvedTheme } from '../../hooks/useResolvedTheme';
|
|
14
|
-
import { hslToHex
|
|
14
|
+
import { hslToHex } from './utils';
|
|
15
15
|
import type { ThemePalette, StylePresets, BoxColors } from './types';
|
|
16
16
|
|
|
17
17
|
/**
|
|
18
|
-
*
|
|
18
|
+
* SSR-safe fallback palette (light theme defaults).
|
|
19
|
+
* Used when document is not available (server-side rendering).
|
|
20
|
+
*/
|
|
21
|
+
const SSR_FALLBACK: Record<string, string> = {
|
|
22
|
+
'background': '#f5f5f5',
|
|
23
|
+
'foreground': '#171717',
|
|
24
|
+
'card': '#ffffff',
|
|
25
|
+
'card-foreground': '#171717',
|
|
26
|
+
'popover': '#ffffff',
|
|
27
|
+
'popover-foreground': '#171717',
|
|
28
|
+
'muted': '#e5e5e5',
|
|
29
|
+
'muted-foreground': '#737373',
|
|
30
|
+
'border': '#e5e5e5',
|
|
31
|
+
'input': '#e5e5e5',
|
|
32
|
+
'ring': '#3b82f6',
|
|
33
|
+
'primary': '#3b82f6',
|
|
34
|
+
'primary-foreground': '#ffffff',
|
|
35
|
+
'secondary': '#f5f5f5',
|
|
36
|
+
'secondary-foreground': '#171717',
|
|
37
|
+
'accent': '#f5f5f5',
|
|
38
|
+
'accent-foreground': '#171717',
|
|
39
|
+
'destructive': '#ef4444',
|
|
40
|
+
'destructive-foreground': '#ffffff',
|
|
41
|
+
'chart-1': '#3b82f6',
|
|
42
|
+
'chart-2': '#22c55e',
|
|
43
|
+
'chart-3': '#8b5cf6',
|
|
44
|
+
'chart-4': '#f59e0b',
|
|
45
|
+
'chart-5': '#ef4444',
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Read a CSS variable from document and convert to hex.
|
|
50
|
+
* Falls back to SSR_FALLBACK on the server.
|
|
19
51
|
*/
|
|
20
52
|
function getCssColorAsHex(varName: string): string {
|
|
21
|
-
if (typeof document === 'undefined')
|
|
53
|
+
if (typeof document === 'undefined') {
|
|
54
|
+
return SSR_FALLBACK[varName] ?? '#000000';
|
|
55
|
+
}
|
|
22
56
|
|
|
23
57
|
const style = getComputedStyle(document.documentElement);
|
|
24
58
|
const value = style.getPropertyValue(`--${varName}`).trim();
|
|
25
59
|
|
|
26
|
-
if (!value) return '#000000';
|
|
60
|
+
if (!value) return SSR_FALLBACK[varName] ?? '#000000';
|
|
27
61
|
|
|
28
62
|
// If already hex, return as-is
|
|
29
63
|
if (value.startsWith('#')) return value;
|
|
@@ -32,35 +66,31 @@ function getCssColorAsHex(varName: string): string {
|
|
|
32
66
|
return hslToHex(value);
|
|
33
67
|
}
|
|
34
68
|
|
|
69
|
+
|
|
35
70
|
/**
|
|
36
|
-
*
|
|
71
|
+
* Convert a hex color to rgba string with given opacity.
|
|
72
|
+
* Works with any hex returned from ThemePalette.
|
|
73
|
+
*
|
|
74
|
+
* @example
|
|
75
|
+
* const palette = useThemePalette();
|
|
76
|
+
* ctx.fillStyle = alpha(palette.primary, 0.3);
|
|
37
77
|
*/
|
|
38
|
-
function
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
const
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
if (!value) return `rgba(0, 0, 0, ${alpha})`;
|
|
45
|
-
|
|
46
|
-
return hslToRgba(value, alpha);
|
|
78
|
+
export function alpha(hexColor: string, opacity: number): string {
|
|
79
|
+
const hex = hexColor.replace('#', '');
|
|
80
|
+
const r = parseInt(hex.substring(0, 2), 16);
|
|
81
|
+
const g = parseInt(hex.substring(2, 4), 16);
|
|
82
|
+
const b = parseInt(hex.substring(4, 6), 16);
|
|
83
|
+
return `rgba(${r}, ${g}, ${b}, ${opacity})`;
|
|
47
84
|
}
|
|
48
85
|
|
|
49
86
|
/**
|
|
50
|
-
* Hook to get the full theme palette from CSS variables
|
|
87
|
+
* Hook to get the full theme palette from CSS variables.
|
|
88
|
+
* Returns hex colors for use in Canvas, SVG, Mermaid, and inline styles.
|
|
51
89
|
*
|
|
52
90
|
* @example
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
* // Use in canvas
|
|
58
|
-
* ctx.fillStyle = palette.primary;
|
|
59
|
-
*
|
|
60
|
-
* // Use in inline styles
|
|
61
|
-
* <div style={{ backgroundColor: palette.success }} />
|
|
62
|
-
* }
|
|
63
|
-
* ```
|
|
91
|
+
* const palette = useThemePalette();
|
|
92
|
+
* ctx.fillStyle = palette.primary;
|
|
93
|
+
* ctx.fillStyle = alpha(palette.primary, 0.3);
|
|
64
94
|
*/
|
|
65
95
|
export function useThemePalette(): ThemePalette {
|
|
66
96
|
const theme = useResolvedTheme();
|
|
@@ -216,18 +246,43 @@ export function useStylePresets(): StylePresets {
|
|
|
216
246
|
*/
|
|
217
247
|
export function useBoxColors(): BoxColors {
|
|
218
248
|
const theme = useResolvedTheme();
|
|
249
|
+
const palette = useThemePalette();
|
|
219
250
|
|
|
220
251
|
return useMemo(() => {
|
|
221
|
-
|
|
222
|
-
const alpha = theme === 'dark' ? 0.3 : 0.15;
|
|
252
|
+
const opacity = theme === 'dark' ? 0.3 : 0.15;
|
|
223
253
|
|
|
224
254
|
return {
|
|
225
|
-
primary:
|
|
226
|
-
success:
|
|
227
|
-
warning:
|
|
228
|
-
danger:
|
|
229
|
-
info:
|
|
230
|
-
muted:
|
|
255
|
+
primary: alpha(palette.primary, opacity),
|
|
256
|
+
success: alpha(palette.success, opacity),
|
|
257
|
+
warning: alpha(palette.warning, opacity),
|
|
258
|
+
danger: alpha(palette.destructive, opacity),
|
|
259
|
+
info: alpha(palette.info, opacity),
|
|
260
|
+
muted: alpha(palette.muted, opacity),
|
|
231
261
|
};
|
|
232
|
-
}, [theme]);
|
|
262
|
+
}, [theme, palette]);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* Hook to get a single theme color by CSS variable name.
|
|
267
|
+
* Lighter alternative to useThemePalette() when you only need 1-3 colors.
|
|
268
|
+
*
|
|
269
|
+
* @param varName - CSS variable name without `--` prefix (e.g. 'primary', 'card-foreground')
|
|
270
|
+
* @param opacity - Optional opacity 0-1. If provided, returns rgba() string instead of hex.
|
|
271
|
+
*
|
|
272
|
+
* @example
|
|
273
|
+
* // Hex color
|
|
274
|
+
* const primary = useThemeColor('primary'); // '#a855f7'
|
|
275
|
+
*
|
|
276
|
+
* // With opacity
|
|
277
|
+
* const wave = useThemeColor('primary', 0.3); // 'rgba(168, 85, 247, 0.3)'
|
|
278
|
+
* const bg = useThemeColor('destructive', 0.1); // useful for error backgrounds
|
|
279
|
+
*/
|
|
280
|
+
export function useThemeColor(varName: string, opacity?: number): string {
|
|
281
|
+
const theme = useResolvedTheme();
|
|
282
|
+
|
|
283
|
+
return useMemo(() => {
|
|
284
|
+
const hex = getCssColorAsHex(varName);
|
|
285
|
+
return opacity !== undefined ? alpha(hex, opacity) : hex;
|
|
286
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
287
|
+
}, [theme, varName, opacity]);
|
|
233
288
|
}
|