@djangocfg/ui-core 2.1.364 → 2.1.365
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 +53 -2
- package/package.json +4 -4
- package/src/styles/README.md +122 -190
- package/src/styles/theme/dark.css +28 -0
- package/src/styles/theme/light.css +29 -0
- package/src/styles/theme/theme-tokens.story.tsx +157 -0
- package/src/styles/theme/tokens.css +17 -0
package/README.md
CHANGED
|
@@ -101,10 +101,61 @@ const log = createLogger('MyComponent');
|
|
|
101
101
|
log.info('user logged in', { userId: 123 });
|
|
102
102
|
```
|
|
103
103
|
|
|
104
|
-
## Styles
|
|
104
|
+
## Styles & Theming
|
|
105
105
|
|
|
106
106
|
```css
|
|
107
|
-
|
|
107
|
+
/* In your app's globals.css — import BEFORE @import "tailwindcss" */
|
|
108
|
+
@import '@djangocfg/ui-nextjs/styles'; /* re-exports ui-core styles */
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### Semantic color tokens
|
|
112
|
+
|
|
113
|
+
All colors are CSS custom properties registered in `@theme` so Tailwind generates utility classes.
|
|
114
|
+
**Never use raw Tailwind color-scale classes** (`amber-500`, `green-100`) in components — use
|
|
115
|
+
semantic tokens that adapt to both light and dark themes automatically.
|
|
116
|
+
|
|
117
|
+
#### Status surfaces — banners and alerts
|
|
118
|
+
|
|
119
|
+
```tsx
|
|
120
|
+
{/* Warning */}
|
|
121
|
+
<div className="flex gap-2 rounded-md border border-warning-border/40
|
|
122
|
+
bg-warning-background px-3 py-2 text-xs text-warning-foreground">
|
|
123
|
+
<Icon className="text-warning" />
|
|
124
|
+
<span>You're on a preview plan.</span>
|
|
125
|
+
</div>
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
Each status (`warning` · `success` · `destructive` · `info`) exposes four classes:
|
|
129
|
+
|
|
130
|
+
| Class | Role |
|
|
131
|
+
|---|---|
|
|
132
|
+
| `bg-*-background` | Banner fill |
|
|
133
|
+
| `text-*-foreground` | Readable body text |
|
|
134
|
+
| `border-*-border` | Border ring |
|
|
135
|
+
| `text-*` / `bg-*` | Icon / accent color |
|
|
136
|
+
|
|
137
|
+
#### Code surface
|
|
138
|
+
|
|
139
|
+
`bg-code` · `text-code-foreground` · `border-code-border` — used by PrettyCode, MarkdownMessage code fences.
|
|
140
|
+
`bg-code-inline` · `text-code-inline-foreground` — for inline `<code>` chips.
|
|
141
|
+
|
|
142
|
+
### Playground
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
pnpm playground # opens component explorer with live theme preview
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
The **Theme / Tokens** story shows every semantic token in both themes — use it to validate
|
|
149
|
+
changes before syncing CSS to consumers. See `src/styles/README.md` for the full token reference
|
|
150
|
+
and the manual sync workflow for local package development.
|
|
151
|
+
|
|
152
|
+
### Programmatic colors (Canvas / SVG)
|
|
153
|
+
|
|
154
|
+
```tsx
|
|
155
|
+
import { useThemePalette, alpha } from '@djangocfg/ui-core/styles/palette';
|
|
156
|
+
|
|
157
|
+
const palette = useThemePalette();
|
|
158
|
+
ctx.fillStyle = alpha(palette.primary, 0.3); // hex → rgba, updates on theme switch
|
|
108
159
|
```
|
|
109
160
|
|
|
110
161
|
## Requirements
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@djangocfg/ui-core",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.365",
|
|
4
4
|
"description": "Pure React UI component library without Next.js dependencies - for Electron, Vite, CRA apps",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ui-components",
|
|
@@ -96,7 +96,7 @@
|
|
|
96
96
|
"playground": "playground dev"
|
|
97
97
|
},
|
|
98
98
|
"peerDependencies": {
|
|
99
|
-
"@djangocfg/i18n": "^2.1.
|
|
99
|
+
"@djangocfg/i18n": "^2.1.365",
|
|
100
100
|
"consola": "^3.4.2",
|
|
101
101
|
"lucide-react": "^0.545.0",
|
|
102
102
|
"moment": "^2.30.1",
|
|
@@ -166,9 +166,9 @@
|
|
|
166
166
|
"vaul": "1.1.2"
|
|
167
167
|
},
|
|
168
168
|
"devDependencies": {
|
|
169
|
-
"@djangocfg/i18n": "^2.1.
|
|
169
|
+
"@djangocfg/i18n": "^2.1.365",
|
|
170
170
|
"@djangocfg/playground": "workspace:*",
|
|
171
|
-
"@djangocfg/typescript-config": "^2.1.
|
|
171
|
+
"@djangocfg/typescript-config": "^2.1.365",
|
|
172
172
|
"@types/node": "^24.7.2",
|
|
173
173
|
"@types/react": "^19.1.0",
|
|
174
174
|
"@types/react-dom": "^19.1.0",
|
package/src/styles/README.md
CHANGED
|
@@ -6,80 +6,134 @@ This directory contains the CSS architecture for `@djangocfg/ui-core` using **Ta
|
|
|
6
6
|
|
|
7
7
|
```
|
|
8
8
|
styles/
|
|
9
|
-
├── index.css
|
|
10
|
-
├── theme.css
|
|
11
|
-
├── base.css
|
|
12
|
-
├── utilities.css
|
|
13
|
-
├── sources.css
|
|
14
|
-
├── palette/
|
|
15
|
-
│ ├── index.ts
|
|
16
|
-
│ ├── types.ts
|
|
17
|
-
│ ├── useThemePalette.ts
|
|
18
|
-
│ └── utils.ts
|
|
9
|
+
├── index.css # Main entry point (import order is critical!)
|
|
10
|
+
├── theme.css # Theme imports
|
|
11
|
+
├── base.css # Base element styles
|
|
12
|
+
├── utilities.css # Custom utility classes
|
|
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
|
|
19
19
|
└── theme/
|
|
20
|
-
├── tokens.css
|
|
21
|
-
├── light.css
|
|
22
|
-
├── dark.css
|
|
23
|
-
|
|
20
|
+
├── tokens.css # @theme block — registers all CSS custom properties as Tailwind utilities
|
|
21
|
+
├── light.css # :root — light theme HSL values
|
|
22
|
+
├── dark.css # .dark — dark theme HSL values
|
|
23
|
+
├── animations.css # Animation keyframes
|
|
24
|
+
└── theme-tokens.story.tsx # Playground story — visual reference for all tokens
|
|
24
25
|
```
|
|
25
26
|
|
|
26
|
-
##
|
|
27
|
+
## Semantic Color Token System
|
|
27
28
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
3. **Simplified PostCSS Setup**: Use `@tailwindcss/postcss` plugin
|
|
31
|
-
4. **Performance Improvements**: 10x faster build times, significantly smaller CSS bundles
|
|
32
|
-
5. **Modern Browser Support**: Optimized for Safari 16.4+, Chrome 111+, Firefox 128+
|
|
33
|
-
6. **Source Detection Required**: Tailwind v4 requires explicit `@source` directives to scan files in monorepo packages
|
|
29
|
+
All colors are defined as HSL CSS custom properties in `light.css` / `dark.css` and
|
|
30
|
+
registered in `tokens.css` so Tailwind generates utility classes.
|
|
34
31
|
|
|
35
|
-
|
|
32
|
+
**Rule: never use raw Tailwind color-scale classes** (`amber-500`, `green-100`, etc.) in
|
|
33
|
+
components. Always use the semantic token classes — they adapt to both themes automatically.
|
|
34
|
+
|
|
35
|
+
### Base tokens
|
|
36
|
+
|
|
37
|
+
| Tailwind class | Token | Description |
|
|
38
|
+
|---|---|---|
|
|
39
|
+
| `bg-background` | `--background` | Page background |
|
|
40
|
+
| `bg-foreground` | `--foreground` | Inverted fill (e.g. avatar bg) |
|
|
41
|
+
| `text-foreground` | `--foreground` | Primary body text |
|
|
42
|
+
| `text-muted-foreground` | `--muted-foreground` | Secondary / hint text |
|
|
43
|
+
| `bg-muted` | `--muted` | Subtle surface (inputs, chips) |
|
|
44
|
+
| `bg-card` | `--card` | Card surface |
|
|
45
|
+
| `bg-accent` | `--accent` | Hover surface |
|
|
46
|
+
| `bg-border` / `border-border` | `--border` | Dividers & rings |
|
|
47
|
+
| `bg-primary` / `text-primary` | `--primary` | Brand CTA color |
|
|
48
|
+
| `bg-destructive` / `text-destructive` | `--destructive` | Error / delete |
|
|
49
|
+
|
|
50
|
+
### Status surface tokens
|
|
51
|
+
|
|
52
|
+
Each status has **4 tokens** for building banners and alerts:
|
|
53
|
+
|
|
54
|
+
| Role | Class | Token |
|
|
55
|
+
|---|---|---|
|
|
56
|
+
| Icon / accent | `text-warning` | `--warning` |
|
|
57
|
+
| Banner background | `bg-warning-background` | `--warning-background` |
|
|
58
|
+
| Readable text | `text-warning-foreground` | `--warning-foreground` |
|
|
59
|
+
| Border ring | `border-warning-border` | `--warning-border` |
|
|
60
|
+
|
|
61
|
+
Available statuses: **`warning`** · **`success`** · **`destructive`** · **`info`**
|
|
62
|
+
|
|
63
|
+
#### Banner pattern
|
|
64
|
+
|
|
65
|
+
```tsx
|
|
66
|
+
<div className="flex items-center gap-3 rounded-md border border-warning-border/40
|
|
67
|
+
bg-warning-background px-3 py-2 text-xs text-warning-foreground">
|
|
68
|
+
<Icon className="h-4 w-4 text-warning" />
|
|
69
|
+
<span>You're on a preview plan.</span>
|
|
70
|
+
</div>
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Change `warning` → `success` / `destructive` / `info` for other states. No raw color classes needed.
|
|
74
|
+
|
|
75
|
+
### Code surface tokens
|
|
76
|
+
|
|
77
|
+
| Class | Description |
|
|
78
|
+
|---|---|
|
|
79
|
+
| `bg-code` / `text-code-foreground` | Code block panel |
|
|
80
|
+
| `border-code-border` | Code block border |
|
|
81
|
+
| `bg-code-inline` / `text-code-inline-foreground` | Inline `<code>` chips |
|
|
82
|
+
|
|
83
|
+
### Sidebar tokens
|
|
84
|
+
|
|
85
|
+
`bg-sidebar`, `text-sidebar-foreground`, `bg-sidebar-accent`, `text-sidebar-accent-foreground`,
|
|
86
|
+
`border-sidebar-border` — used by the app sidebar chrome, defined per-theme.
|
|
87
|
+
|
|
88
|
+
## Playground Story
|
|
89
|
+
|
|
90
|
+
Run `pnpm playground` in the `ui-core` package to open the component explorer.
|
|
91
|
+
The **Theme / Tokens** story (`theme-tokens.story.tsx`) shows a live visual reference of every
|
|
92
|
+
semantic token — base colors, status surfaces, code surface, sidebar tokens, and typography —
|
|
93
|
+
in both light and dark mode. Use it to validate token changes before syncing to consumers.
|
|
94
|
+
|
|
95
|
+
## App Setup (globals.css) — CRITICAL!
|
|
36
96
|
|
|
37
97
|
When creating a new app that uses UI packages, your `globals.css` MUST follow this pattern:
|
|
38
98
|
|
|
39
99
|
```css
|
|
40
|
-
/**
|
|
41
|
-
* CRITICAL: Import order matters for Tailwind CSS v4!
|
|
42
|
-
* 1. Theme variables MUST come BEFORE @import "tailwindcss"
|
|
43
|
-
* 2. @source directives MUST be added for each package with Tailwind classes
|
|
44
|
-
*/
|
|
45
|
-
|
|
46
100
|
/* 1. Import UI package styles FIRST (contains @theme variables) */
|
|
47
101
|
@import "@djangocfg/ui-nextjs/styles";
|
|
48
102
|
|
|
49
|
-
/* 2. Import other package styles (layouts, etc.) */
|
|
103
|
+
/* 2. Import other package styles (layouts, tools, etc.) */
|
|
50
104
|
@import "@djangocfg/layouts/styles";
|
|
105
|
+
@import "@djangocfg/ui-tools/styles";
|
|
51
106
|
|
|
52
107
|
/* 3. Import Tailwind CSS v4 (AFTER theme variables!) */
|
|
53
108
|
@import "tailwindcss";
|
|
54
109
|
|
|
55
110
|
/* 4. Load plugins */
|
|
56
111
|
@plugin "tailwindcss-animate";
|
|
57
|
-
|
|
58
|
-
/* 5. Source detection for packages NOT included in ui-nextjs/styles */
|
|
59
|
-
/* This tells Tailwind where to scan for utility classes */
|
|
60
|
-
@source "../../../packages/your-package/src/**/*.{ts,tsx}";
|
|
61
112
|
```
|
|
62
113
|
|
|
63
114
|
### Why @source is Required
|
|
64
115
|
|
|
65
|
-
In Tailwind v4 monorepo, automatic class detection doesn't work across packages.
|
|
116
|
+
In a Tailwind v4 monorepo, automatic class detection doesn't work across packages.
|
|
117
|
+
Each package with Tailwind classes needs either:
|
|
66
118
|
|
|
67
|
-
1.
|
|
68
|
-
2.
|
|
119
|
+
1. A `@source` directive in the consuming app's `globals.css`
|
|
120
|
+
2. Or a `sources.css` file in the package imported via the styles entry point
|
|
69
121
|
|
|
70
|
-
|
|
122
|
+
## Syncing Local Packages to Consumer
|
|
71
123
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
@source "../../../packages/playground-ui/src/**/*.{ts,tsx}";
|
|
75
|
-
```
|
|
124
|
+
These packages are consumed via pinned npm versions — edits to `src/` don't automatically
|
|
125
|
+
reach the consumer. After changing theme files, copy them manually:
|
|
76
126
|
|
|
77
|
-
|
|
127
|
+
```bash
|
|
128
|
+
CONSUMER=/path/to/carapis/solution/projects/frontend
|
|
129
|
+
SRC=./src/styles/theme
|
|
130
|
+
|
|
131
|
+
cp "$SRC/light.css" "$CONSUMER/node_modules/@djangocfg/ui-core/src/styles/theme/light.css"
|
|
132
|
+
cp "$SRC/dark.css" "$CONSUMER/node_modules/@djangocfg/ui-core/src/styles/theme/dark.css"
|
|
133
|
+
cp "$SRC/tokens.css" "$CONSUMER/node_modules/@djangocfg/ui-core/src/styles/theme/tokens.css"
|
|
134
|
+
```
|
|
78
135
|
|
|
79
|
-
|
|
80
|
-
- Grid classes like `grid-cols-3`, `grid-cols-5` don't work
|
|
81
|
-
- Spacing classes like `pb-20`, `mt-10` have no effect
|
|
82
|
-
- Classes work in some packages but not others
|
|
136
|
+
Then restart the consumer's dev server (Next.js caches module resolution).
|
|
83
137
|
|
|
84
138
|
## Import Order (Critical!)
|
|
85
139
|
|
|
@@ -97,121 +151,45 @@ In Tailwind v4 monorepo, automatic class detection doesn't work across packages.
|
|
|
97
151
|
@import "./utilities.css";
|
|
98
152
|
```
|
|
99
153
|
|
|
100
|
-
##
|
|
154
|
+
## Gotchas
|
|
101
155
|
|
|
102
|
-
###
|
|
156
|
+
### Opacity with HSL colors
|
|
103
157
|
|
|
104
|
-
-
|
|
105
|
-
- Responsive patterns: `px-4 sm:px-6 lg:px-8`
|
|
106
|
-
- Container pattern: `container max-w-7xl mx-auto`
|
|
107
|
-
|
|
108
|
-
### Arbitrary Values
|
|
109
|
-
|
|
110
|
-
**IMPORTANT**: Arbitrary values like `h-[80px]`, `z-[100]` may NOT work in v4!
|
|
158
|
+
`bg-background/80` does **not** work with HSL tokens in Tailwind v4. Use inline styles:
|
|
111
159
|
|
|
112
160
|
```tsx
|
|
113
|
-
// ❌
|
|
114
|
-
<
|
|
115
|
-
|
|
116
|
-
// ✅ Define tokens in @theme instead
|
|
117
|
-
@theme {
|
|
118
|
-
--spacing-80: 20rem;
|
|
119
|
-
--z-index-100: 100;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
// ✅ Or use inline styles (always reliable)
|
|
123
|
-
<div style={{ height: '80px', zIndex: 100 }}>
|
|
124
|
-
```
|
|
125
|
-
|
|
126
|
-
### Opacity with HSL Colors
|
|
161
|
+
// ❌ Broken
|
|
162
|
+
<nav className="bg-background/80">
|
|
127
163
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
```tsx
|
|
131
|
-
// ❌ BROKEN - does not work in Tailwind v4
|
|
132
|
-
<nav className="bg-background/80 border-border/30">
|
|
133
|
-
|
|
134
|
-
// ✅ WORKING - use inline styles for opacity
|
|
135
|
-
<nav
|
|
136
|
-
className="sticky top-0 z-100 backdrop-blur-xl"
|
|
137
|
-
style={{
|
|
138
|
-
backgroundColor: 'hsl(var(--background) / 0.8)',
|
|
139
|
-
borderColor: 'hsl(var(--border) / 0.3)'
|
|
140
|
-
}}
|
|
141
|
-
>
|
|
164
|
+
// ✅ Works
|
|
165
|
+
<nav style={{ backgroundColor: 'hsl(var(--background) / 0.8)' }}>
|
|
142
166
|
```
|
|
143
167
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
```tsx
|
|
147
|
-
// ✅ Inline styles for fixed sizes (always reliable)
|
|
148
|
-
<Avatar
|
|
149
|
-
className="aspect-square rounded-full overflow-hidden"
|
|
150
|
-
style={{ width: '80px', height: '80px' }}
|
|
151
|
-
>
|
|
152
|
-
<AvatarImage src={avatar} alt="User" />
|
|
153
|
-
</Avatar>
|
|
154
|
-
```
|
|
168
|
+
**Exception**: `border-warning-border/40` works because `--color-warning-border` is resolved
|
|
169
|
+
to a full `hsl()` value, making the `/` opacity modifier valid.
|
|
155
170
|
|
|
156
|
-
###
|
|
171
|
+
### Arbitrary values
|
|
157
172
|
|
|
158
|
-
|
|
173
|
+
Arbitrary values like `h-[80px]`, `z-[100]` may not work in v4. Prefer tokens:
|
|
159
174
|
|
|
160
175
|
```css
|
|
161
176
|
@theme {
|
|
162
|
-
--spacing:
|
|
163
|
-
--spacing-0: 0;
|
|
164
|
-
--spacing-1: 0.25rem; /* 4px */
|
|
165
|
-
--spacing-2: 0.5rem; /* 8px */
|
|
166
|
-
--spacing-4: 1rem; /* 16px */
|
|
167
|
-
--spacing-8: 2rem; /* 32px */
|
|
168
|
-
--spacing-13: 3.25rem; /* 52px - for min-h-13 */
|
|
169
|
-
/* ... */
|
|
170
|
-
}
|
|
171
|
-
```
|
|
172
|
-
|
|
173
|
-
### Z-Index Requirements
|
|
174
|
-
|
|
175
|
-
Z-index utilities (`z-50`, `z-100`) require `--z-index-*` variables:
|
|
176
|
-
|
|
177
|
-
```css
|
|
178
|
-
@theme {
|
|
179
|
-
--z-index-50: 50;
|
|
177
|
+
--spacing-80: 20rem;
|
|
180
178
|
--z-index-100: 100;
|
|
181
|
-
--z-index-9999: 9999;
|
|
182
179
|
}
|
|
183
180
|
```
|
|
184
181
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
### Responsive Design (Mobile-First)
|
|
188
|
-
|
|
189
|
-
```tsx
|
|
190
|
-
<div className="px-4 sm:px-6 lg:px-8">
|
|
191
|
-
<div className="py-16 sm:py-20 md:py-24 lg:py-32">
|
|
192
|
-
Content
|
|
193
|
-
</div>
|
|
194
|
-
</div>
|
|
195
|
-
```
|
|
196
|
-
|
|
197
|
-
### Breakpoints
|
|
198
|
-
|
|
199
|
-
- `sm:` - 640px (40rem)
|
|
200
|
-
- `md:` - 768px (48rem)
|
|
201
|
-
- `lg:` - 1024px (64rem)
|
|
202
|
-
- `xl:` - 1280px (80rem)
|
|
203
|
-
- `2xl:` - 1536px (96rem)
|
|
182
|
+
Or use inline styles for one-off fixed sizes.
|
|
204
183
|
|
|
205
|
-
###
|
|
184
|
+
### Spacing & z-index
|
|
206
185
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
```
|
|
186
|
+
Spacing utilities (`h-20`, `p-4`) require `--spacing-*` in `@theme`.
|
|
187
|
+
Z-index utilities (`z-50`, `z-100`) require `--z-index-*` in `@theme`.
|
|
188
|
+
Both are already defined in `tokens.css`.
|
|
211
189
|
|
|
212
190
|
## Theme Palette (Programmatic Color Access)
|
|
213
191
|
|
|
214
|
-
For Canvas, SVG, Mermaid, and other contexts that don't support CSS variables
|
|
192
|
+
For Canvas, SVG, Mermaid, and other contexts that don't support CSS variables:
|
|
215
193
|
|
|
216
194
|
```tsx
|
|
217
195
|
import {
|
|
@@ -221,59 +199,13 @@ import {
|
|
|
221
199
|
alpha,
|
|
222
200
|
} from '@djangocfg/ui-core/styles/palette';
|
|
223
201
|
|
|
224
|
-
// Hex colors from CSS variables (updates on theme switch)
|
|
225
202
|
const palette = useThemePalette();
|
|
226
|
-
ctx.fillStyle = palette.primary;
|
|
227
|
-
ctx.fillStyle = alpha(palette.
|
|
203
|
+
ctx.fillStyle = palette.primary; // '#0989aa'
|
|
204
|
+
ctx.fillStyle = alpha(palette.warning, 0.15); // 'rgba(..., 0.15)'
|
|
228
205
|
|
|
229
|
-
// Pre-built { fill, stroke, color } for diagrams
|
|
230
206
|
const presets = useStylePresets();
|
|
231
|
-
presets.success // { fill: '
|
|
232
|
-
presets.danger // { fill: '#ef4444', stroke: '#ef4444', color: '#fff' }
|
|
207
|
+
presets.success // { fill: '#...', stroke: '#...', color: '#fff' }
|
|
233
208
|
|
|
234
|
-
// Semi-transparent backgrounds (adapts opacity to light/dark)
|
|
235
209
|
const boxes = useBoxColors();
|
|
236
|
-
boxes.primary
|
|
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
|
-
|
|
257
|
-
## What to Avoid
|
|
258
|
-
|
|
259
|
-
- Custom utilities like: `section-padding`, `animate-*`, `shadow-brand`
|
|
260
|
-
- Arbitrary values without checking if they work: `h-[53px]`, `min-h-[100px]`
|
|
261
|
-
- Opacity modifiers with HSL colors: `bg-background/80`
|
|
262
|
-
- Assuming spacing values exist without defining them in `@theme`
|
|
263
|
-
|
|
264
|
-
## CSS Variables
|
|
265
|
-
|
|
266
|
-
Use CSS variables for dynamic values:
|
|
267
|
-
|
|
268
|
-
```tsx
|
|
269
|
-
<div style={{ color: 'var(--color-primary)' }}>
|
|
270
|
-
```
|
|
271
|
-
|
|
272
|
-
Or in CSS:
|
|
273
|
-
|
|
274
|
-
```css
|
|
275
|
-
.my-class {
|
|
276
|
-
background-color: hsl(var(--background));
|
|
277
|
-
color: hsl(var(--foreground));
|
|
278
|
-
}
|
|
210
|
+
boxes.primary // semi-transparent brand, adapts to light/dark
|
|
279
211
|
```
|
|
@@ -60,6 +60,34 @@
|
|
|
60
60
|
--sidebar-border: 0 0% 15%;
|
|
61
61
|
--sidebar-ring: 189 100% 50%;
|
|
62
62
|
|
|
63
|
+
/* ── Status surfaces (dark) ──────────────────────────────────────
|
|
64
|
+
* Dark variants: dim backgrounds (~8-12% lightness) so banners
|
|
65
|
+
* don't blow out against the near-black page. Foreground raised
|
|
66
|
+
* to ~70-80% lightness for contrast. Borders kept muted.
|
|
67
|
+
* ─────────────────────────────────────────────────────────────── */
|
|
68
|
+
|
|
69
|
+
/* Warning — amber */
|
|
70
|
+
--warning: 38 92% 50%;
|
|
71
|
+
--warning-background: 38 80% 10%;
|
|
72
|
+
--warning-foreground: 38 90% 75%;
|
|
73
|
+
--warning-border: 38 60% 30%;
|
|
74
|
+
|
|
75
|
+
/* Success — green */
|
|
76
|
+
--success: 142 60% 45%;
|
|
77
|
+
--success-background: 142 60% 8%;
|
|
78
|
+
--success-foreground: 142 70% 70%;
|
|
79
|
+
--success-border: 142 50% 25%;
|
|
80
|
+
|
|
81
|
+
/* Destructive surface */
|
|
82
|
+
--destructive-background: 0 80% 10%;
|
|
83
|
+
--destructive-border: 0 70% 30%;
|
|
84
|
+
|
|
85
|
+
/* Info — blue-cyan */
|
|
86
|
+
--info: 200 80% 55%;
|
|
87
|
+
--info-background: 200 80% 8%;
|
|
88
|
+
--info-foreground: 200 80% 75%;
|
|
89
|
+
--info-border: 200 60% 25%;
|
|
90
|
+
|
|
63
91
|
/* Surface gradient dark */
|
|
64
92
|
--surface-gradient: linear-gradient(to bottom, hsl(var(--background)), hsl(var(--background) / 0.8));
|
|
65
93
|
|
|
@@ -60,6 +60,35 @@
|
|
|
60
60
|
--sidebar-border: 0 0% 90%;
|
|
61
61
|
--sidebar-ring: 192 90% 35%;
|
|
62
62
|
|
|
63
|
+
/* ── Status surfaces ─────────────────────────────────────────────
|
|
64
|
+
* Each status has 4 tokens: icon/accent color, page background,
|
|
65
|
+
* readable foreground text, and border ring.
|
|
66
|
+
* Use bg-warning-background / text-warning-foreground / border-warning-border
|
|
67
|
+
* in components — never raw amber-* or green-* classes.
|
|
68
|
+
* ─────────────────────────────────────────────────────────────── */
|
|
69
|
+
|
|
70
|
+
/* Warning — amber */
|
|
71
|
+
--warning: 38 92% 50%;
|
|
72
|
+
--warning-background: 48 100% 96%;
|
|
73
|
+
--warning-foreground: 26 90% 25%;
|
|
74
|
+
--warning-border: 38 70% 65%;
|
|
75
|
+
|
|
76
|
+
/* Success — green */
|
|
77
|
+
--success: 142 71% 45%;
|
|
78
|
+
--success-background: 138 76% 97%;
|
|
79
|
+
--success-foreground: 140 60% 20%;
|
|
80
|
+
--success-border: 142 60% 65%;
|
|
81
|
+
|
|
82
|
+
/* Destructive surface (separate from the filled button token) */
|
|
83
|
+
--destructive-background: 0 100% 97%;
|
|
84
|
+
--destructive-border: 0 84% 70%;
|
|
85
|
+
|
|
86
|
+
/* Info — blue-cyan (brand-adjacent) */
|
|
87
|
+
--info: 200 90% 40%;
|
|
88
|
+
--info-background: 200 100% 97%;
|
|
89
|
+
--info-foreground: 200 80% 20%;
|
|
90
|
+
--info-border: 200 80% 65%;
|
|
91
|
+
|
|
63
92
|
/* Surface gradient */
|
|
64
93
|
--surface-gradient: linear-gradient(to bottom, hsl(var(--background)), hsl(var(--background) / 0.8));
|
|
65
94
|
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import { defineStory } from '@djangocfg/playground';
|
|
2
|
+
|
|
3
|
+
export default defineStory({
|
|
4
|
+
title: 'Theme/Tokens',
|
|
5
|
+
description: 'Visual reference for all semantic CSS color tokens. Use these classes — never raw Tailwind color-scale classes (amber-500 etc).',
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
// ─── Helpers ──────────────────────────────────────────────────────────────────
|
|
9
|
+
|
|
10
|
+
function TokenRow({ label, bg, text, border }: { label: string; bg: string; text: string; border: string }) {
|
|
11
|
+
return (
|
|
12
|
+
<div className={`flex items-center justify-between rounded-md border px-4 py-3 ${bg} ${border}`}>
|
|
13
|
+
<span className={`text-sm font-medium ${text}`}>{label}</span>
|
|
14
|
+
<div className="flex gap-2 font-mono text-xs opacity-70">
|
|
15
|
+
<code className={text}>{bg}</code>
|
|
16
|
+
<code className={text}>{text}</code>
|
|
17
|
+
<code className={text}>{border}</code>
|
|
18
|
+
</div>
|
|
19
|
+
</div>
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function Section({ title, children }: { title: string; children: React.ReactNode }) {
|
|
24
|
+
return (
|
|
25
|
+
<div className="space-y-2">
|
|
26
|
+
<h3 className="text-xs font-semibold uppercase tracking-widest text-muted-foreground">{title}</h3>
|
|
27
|
+
<div className="space-y-2">{children}</div>
|
|
28
|
+
</div>
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function SwatchRow({ name, bgClass }: { name: string; bgClass: string }) {
|
|
33
|
+
return (
|
|
34
|
+
<div className="flex items-center gap-3">
|
|
35
|
+
<div className={`h-8 w-16 rounded border border-border ${bgClass}`} />
|
|
36
|
+
<code className="text-xs text-foreground">{name}</code>
|
|
37
|
+
</div>
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// ─── Stories ──────────────────────────────────────────────────────────────────
|
|
42
|
+
|
|
43
|
+
export const StatusSurfaces = () => (
|
|
44
|
+
<div className="space-y-6 max-w-2xl">
|
|
45
|
+
<p className="text-sm text-muted-foreground">
|
|
46
|
+
Status banners — use <code className="text-xs bg-code text-code-foreground rounded px-1">bg-*-background</code>,{' '}
|
|
47
|
+
<code className="text-xs bg-code text-code-foreground rounded px-1">text-*-foreground</code>,{' '}
|
|
48
|
+
<code className="text-xs bg-code text-code-foreground rounded px-1">border-*-border</code>
|
|
49
|
+
</p>
|
|
50
|
+
|
|
51
|
+
<Section title="Warning">
|
|
52
|
+
<TokenRow
|
|
53
|
+
label="⚠ You're seeing a preview. Subscribe for full access."
|
|
54
|
+
bg="bg-warning-background"
|
|
55
|
+
text="text-warning-foreground"
|
|
56
|
+
border="border border-warning-border/50"
|
|
57
|
+
/>
|
|
58
|
+
</Section>
|
|
59
|
+
|
|
60
|
+
<Section title="Success">
|
|
61
|
+
<TokenRow
|
|
62
|
+
label="✓ Operation completed successfully."
|
|
63
|
+
bg="bg-success-background"
|
|
64
|
+
text="text-success-foreground"
|
|
65
|
+
border="border border-success-border/50"
|
|
66
|
+
/>
|
|
67
|
+
</Section>
|
|
68
|
+
|
|
69
|
+
<Section title="Destructive">
|
|
70
|
+
<TokenRow
|
|
71
|
+
label="✕ Something went wrong. Please try again."
|
|
72
|
+
bg="bg-destructive-background"
|
|
73
|
+
text="text-destructive"
|
|
74
|
+
border="border border-destructive-border/50"
|
|
75
|
+
/>
|
|
76
|
+
</Section>
|
|
77
|
+
|
|
78
|
+
<Section title="Info">
|
|
79
|
+
<TokenRow
|
|
80
|
+
label="ℹ New features are available in your plan."
|
|
81
|
+
bg="bg-info-background"
|
|
82
|
+
text="text-info-foreground"
|
|
83
|
+
border="border border-info-border/50"
|
|
84
|
+
/>
|
|
85
|
+
</Section>
|
|
86
|
+
</div>
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
export const BaseColors = () => (
|
|
90
|
+
<div className="space-y-6 max-w-xl">
|
|
91
|
+
<Section title="Base">
|
|
92
|
+
<div className="grid grid-cols-2 gap-3">
|
|
93
|
+
<SwatchRow name="bg-background" bgClass="bg-background" />
|
|
94
|
+
<SwatchRow name="bg-foreground" bgClass="bg-foreground" />
|
|
95
|
+
<SwatchRow name="bg-card" bgClass="bg-card" />
|
|
96
|
+
<SwatchRow name="bg-muted" bgClass="bg-muted" />
|
|
97
|
+
<SwatchRow name="bg-accent" bgClass="bg-accent" />
|
|
98
|
+
<SwatchRow name="bg-border" bgClass="bg-border" />
|
|
99
|
+
</div>
|
|
100
|
+
</Section>
|
|
101
|
+
|
|
102
|
+
<Section title="Brand">
|
|
103
|
+
<div className="grid grid-cols-2 gap-3">
|
|
104
|
+
<SwatchRow name="bg-primary" bgClass="bg-primary" />
|
|
105
|
+
<SwatchRow name="bg-secondary" bgClass="bg-secondary" />
|
|
106
|
+
</div>
|
|
107
|
+
</Section>
|
|
108
|
+
|
|
109
|
+
<Section title="Status accents (icon color)">
|
|
110
|
+
<div className="grid grid-cols-2 gap-3">
|
|
111
|
+
<SwatchRow name="bg-warning" bgClass="bg-warning" />
|
|
112
|
+
<SwatchRow name="bg-success" bgClass="bg-success" />
|
|
113
|
+
<SwatchRow name="bg-destructive" bgClass="bg-destructive" />
|
|
114
|
+
<SwatchRow name="bg-info" bgClass="bg-info" />
|
|
115
|
+
</div>
|
|
116
|
+
</Section>
|
|
117
|
+
</div>
|
|
118
|
+
);
|
|
119
|
+
|
|
120
|
+
export const CodeSurface = () => (
|
|
121
|
+
<div className="space-y-4 max-w-xl">
|
|
122
|
+
<Section title="Code tokens">
|
|
123
|
+
<div className="rounded-md border border-code-border bg-code text-code-foreground p-4 font-mono text-sm">
|
|
124
|
+
<div>bg-code / text-code-foreground / border-code-border</div>
|
|
125
|
+
<div className="mt-2 text-xs opacity-60">Used by PrettyCode, MarkdownMessage code fences</div>
|
|
126
|
+
</div>
|
|
127
|
+
<div className="text-sm">
|
|
128
|
+
Inline:{' '}
|
|
129
|
+
<code className="rounded bg-code-inline px-1.5 py-0.5 text-code-inline-foreground text-xs">
|
|
130
|
+
const x = 42
|
|
131
|
+
</code>
|
|
132
|
+
</div>
|
|
133
|
+
</Section>
|
|
134
|
+
|
|
135
|
+
<Section title="Sidebar tokens">
|
|
136
|
+
<div className="rounded-md border border-sidebar-border bg-sidebar p-4 space-y-1 text-sidebar-foreground text-sm">
|
|
137
|
+
<div className="font-semibold">bg-sidebar / text-sidebar-foreground</div>
|
|
138
|
+
<div className="text-xs opacity-60">Used by the app sidebar chrome</div>
|
|
139
|
+
<div className="mt-2 rounded bg-sidebar-accent px-2 py-1 text-sidebar-accent-foreground text-xs">
|
|
140
|
+
bg-sidebar-accent — hover state
|
|
141
|
+
</div>
|
|
142
|
+
</div>
|
|
143
|
+
</Section>
|
|
144
|
+
</div>
|
|
145
|
+
);
|
|
146
|
+
|
|
147
|
+
export const TypographyTokens = () => (
|
|
148
|
+
<div className="space-y-3 max-w-md">
|
|
149
|
+
<p className="text-foreground text-sm font-medium">text-foreground — primary body</p>
|
|
150
|
+
<p className="text-muted-foreground text-sm">text-muted-foreground — secondary / hints</p>
|
|
151
|
+
<p className="text-primary text-sm">text-primary — brand links / accents</p>
|
|
152
|
+
<p className="text-destructive text-sm">text-destructive — error messages</p>
|
|
153
|
+
<p className="text-warning text-sm">text-warning — warning labels</p>
|
|
154
|
+
<p className="text-success text-sm">text-success — success labels</p>
|
|
155
|
+
<p className="text-info text-sm">text-info — info labels</p>
|
|
156
|
+
</div>
|
|
157
|
+
);
|
|
@@ -35,7 +35,24 @@
|
|
|
35
35
|
--color-ring: hsl(var(--ring));
|
|
36
36
|
--color-black: #000000;
|
|
37
37
|
--color-white: #ffffff;
|
|
38
|
+
/* Status surfaces — warning / success / destructive / info */
|
|
38
39
|
--color-warning: hsl(var(--warning));
|
|
40
|
+
--color-warning-background: hsl(var(--warning-background));
|
|
41
|
+
--color-warning-foreground: hsl(var(--warning-foreground));
|
|
42
|
+
--color-warning-border: hsl(var(--warning-border));
|
|
43
|
+
|
|
44
|
+
--color-success: hsl(var(--success));
|
|
45
|
+
--color-success-background: hsl(var(--success-background));
|
|
46
|
+
--color-success-foreground: hsl(var(--success-foreground));
|
|
47
|
+
--color-success-border: hsl(var(--success-border));
|
|
48
|
+
|
|
49
|
+
--color-destructive-background: hsl(var(--destructive-background));
|
|
50
|
+
--color-destructive-border: hsl(var(--destructive-border));
|
|
51
|
+
|
|
52
|
+
--color-info: hsl(var(--info));
|
|
53
|
+
--color-info-background: hsl(var(--info-background));
|
|
54
|
+
--color-info-foreground: hsl(var(--info-foreground));
|
|
55
|
+
--color-info-border: hsl(var(--info-border));
|
|
39
56
|
|
|
40
57
|
/* Code surface tokens — single source of truth for any "this is
|
|
41
58
|
* code, not prose" panel (markdown fences, inline code, terminal
|