@fgv/ts-app-shell 5.1.0-29 → 5.1.0-30
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 +159 -0
- package/dist/styles/theme.css +194 -0
- package/lib/styles/theme.css +194 -0
- package/package.json +11 -7
- package/tailwind-preset.cjs +102 -0
package/README.md
CHANGED
|
@@ -10,6 +10,7 @@ Shared React UI primitives for application shells in the `@fgv` monorepo.
|
|
|
10
10
|
- **Log Message Panel** — Collapsible bottom panel with severity-filtered message history
|
|
11
11
|
- **Command Palette** — Cmd+K quick navigation overlay
|
|
12
12
|
- **Keybinding Registry** — Infrastructure for registering and managing keyboard shortcuts
|
|
13
|
+
- **Default Theme** — CSS-variable tokens + Tailwind preset for all semantic colors (light + dark)
|
|
13
14
|
|
|
14
15
|
## Usage
|
|
15
16
|
|
|
@@ -17,6 +18,164 @@ Shared React UI primitives for application shells in the `@fgv` monorepo.
|
|
|
17
18
|
import { /* components */ } from '@fgv/ts-app-shell';
|
|
18
19
|
```
|
|
19
20
|
|
|
21
|
+
## Theming
|
|
22
|
+
|
|
23
|
+
All visual components use custom Tailwind semantic tokens (e.g. `bg-surface`, `text-primary`,
|
|
24
|
+
`bg-status-error-bg`). To make them render correctly in your application, add the Tailwind
|
|
25
|
+
preset and import the base CSS once.
|
|
26
|
+
|
|
27
|
+
### 1. Add the Tailwind preset
|
|
28
|
+
|
|
29
|
+
In your `tailwind.config.js` (or `tailwind.config.ts`):
|
|
30
|
+
|
|
31
|
+
```js
|
|
32
|
+
const appShellPreset = require('@fgv/ts-app-shell/tailwind-preset');
|
|
33
|
+
|
|
34
|
+
module.exports = {
|
|
35
|
+
presets: [appShellPreset],
|
|
36
|
+
// Make sure Tailwind scans the package's components for class names:
|
|
37
|
+
content: [
|
|
38
|
+
'./src/**/*.{ts,tsx}',
|
|
39
|
+
'./node_modules/@fgv/ts-app-shell/lib/**/*.{js,jsx}',
|
|
40
|
+
],
|
|
41
|
+
// ...rest of your config
|
|
42
|
+
};
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### 2. Import the base CSS
|
|
46
|
+
|
|
47
|
+
In your application entry point (e.g. `main.tsx` or `index.css`):
|
|
48
|
+
|
|
49
|
+
```ts
|
|
50
|
+
import '@fgv/ts-app-shell/theme.css';
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### 3. Toggle dark mode
|
|
54
|
+
|
|
55
|
+
The preset uses Tailwind's `darkMode: 'class'` strategy. Add the class `dark` to any ancestor
|
|
56
|
+
element (typically `<html>`) to activate dark mode. The `ThemeProvider` component does this
|
|
57
|
+
automatically when the user selects `'dark'` or `'system'` (if the OS prefers dark):
|
|
58
|
+
|
|
59
|
+
```tsx
|
|
60
|
+
import { ThemeProvider } from '@fgv/ts-app-shell';
|
|
61
|
+
|
|
62
|
+
<ThemeProvider initialTheme="system" onThemeChange={saveTheme}>
|
|
63
|
+
<App />
|
|
64
|
+
</ThemeProvider>
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### Overriding tokens
|
|
68
|
+
|
|
69
|
+
Every token is a CSS custom property under the `--fgv-` prefix. Override any subset in your
|
|
70
|
+
own CSS after importing `theme.css`:
|
|
71
|
+
|
|
72
|
+
```css
|
|
73
|
+
:root {
|
|
74
|
+
--fgv-brand-primary: #7c3aed; /* purple brand instead of blue */
|
|
75
|
+
--fgv-brand-accent: #a78bfa;
|
|
76
|
+
}
|
|
77
|
+
.dark {
|
|
78
|
+
--fgv-brand-primary: #8b5cf6;
|
|
79
|
+
--fgv-brand-accent: #c4b5fd;
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## Complete Token Reference
|
|
84
|
+
|
|
85
|
+
### Surfaces
|
|
86
|
+
|
|
87
|
+
| Token name | CSS variable | Light default | Dark default | Purpose |
|
|
88
|
+
|---|---|---|---|---|
|
|
89
|
+
| `surface` | `--fgv-surface` | `#ffffff` | `#1f2937` | Base page / panel background |
|
|
90
|
+
| `surface-raised` | `--fgv-surface-raised` | `#f3f4f6` | `#374151` | Elevated cards, filter toolbars |
|
|
91
|
+
| `surface-alt` | `--fgv-surface-alt` | `#f9fafb` | `#111827` | Alternate / zebra row background |
|
|
92
|
+
| `backdrop` | `--fgv-backdrop` | `rgba(0,0,0,0.45)` | `rgba(0,0,0,0.65)` | Modal/dialog overlay |
|
|
93
|
+
| `hover` | `--fgv-hover` | `#f3f4f6` | `#374151` | Row hover highlight |
|
|
94
|
+
| `selected` | `--fgv-selected` | `#eff6ff` | `#1e3a5f` | Selected list item background |
|
|
95
|
+
|
|
96
|
+
### Text
|
|
97
|
+
|
|
98
|
+
| Token name | CSS variable | Light | Dark | Purpose |
|
|
99
|
+
|---|---|---|---|---|
|
|
100
|
+
| `primary` | `--fgv-text-primary` | `#111827` | `#f9fafb` | High-emphasis body text (WCAG AA+) |
|
|
101
|
+
| `secondary` | `--fgv-text-secondary` | `#374151` | `#d1d5db` | Medium-emphasis labels |
|
|
102
|
+
| `muted` | `--fgv-text-muted` | `#6b7280` | `#9ca3af` | Supporting / helper text |
|
|
103
|
+
| `faint` | `--fgv-text-faint` | `#9ca3af` | `#6b7280` | Ghost / ornamental text |
|
|
104
|
+
| `star` | `--fgv-text-star` | `#d97706` | `#fbbf24` | Star / favorite icon color |
|
|
105
|
+
|
|
106
|
+
### Borders & Focus
|
|
107
|
+
|
|
108
|
+
| Token name | CSS variable | Light | Dark | Purpose |
|
|
109
|
+
|---|---|---|---|---|
|
|
110
|
+
| `border` | `--fgv-border` | `#d1d5db` | `#4b5563` | Default input / divider border |
|
|
111
|
+
| `border-subtle` | `--fgv-border-subtle` | `#e5e7eb` | `#374151` | Subtle row separator |
|
|
112
|
+
| `focus-ring` | `--fgv-focus-ring` | `#3b82f6` | `#60a5fa` | Keyboard focus ring |
|
|
113
|
+
| `selected-border` | `--fgv-selected-border` | `#2563eb` | `#3b82f6` | Selected item left-border accent |
|
|
114
|
+
|
|
115
|
+
### Brand
|
|
116
|
+
|
|
117
|
+
| Token name | CSS variable | Light | Dark | Purpose |
|
|
118
|
+
|---|---|---|---|---|
|
|
119
|
+
| `brand-primary` | `--fgv-brand-primary` | `#2563eb` | `#3b82f6` | Primary action / active nav |
|
|
120
|
+
| `brand-secondary` | `--fgv-brand-secondary` | `#1d4ed8` | `#1e3a5f` | Pressed / dark top-bar variant |
|
|
121
|
+
| `brand-accent` | `--fgv-brand-accent` | `#3b82f6` | `#60a5fa` | Hover text, checkmarks, accents |
|
|
122
|
+
|
|
123
|
+
### Status: Error
|
|
124
|
+
|
|
125
|
+
| Token name | CSS variable | Purpose |
|
|
126
|
+
|---|---|---|
|
|
127
|
+
| `status-error-bg` | `--fgv-status-error-bg` | Error row / badge background |
|
|
128
|
+
| `status-error-surface` | `--fgv-status-error-surface` | Hover background on error items |
|
|
129
|
+
| `status-error-btn` | `--fgv-status-error-btn` | Danger action button fill |
|
|
130
|
+
| `status-error-btn-hover` | `--fgv-status-error-btn-hover` | Danger button hover fill |
|
|
131
|
+
| `status-error-icon` | `--fgv-status-error-icon` | Error icon / dot color |
|
|
132
|
+
| `status-error-border` | `--fgv-status-error-border` | Error section border |
|
|
133
|
+
| `status-error-accent` | `--fgv-status-error-accent` | Error focus ring |
|
|
134
|
+
| `status-error-text` | `--fgv-status-error-text` | Error message text |
|
|
135
|
+
| `status-error-strong` | `--fgv-status-error-strong` | High-emphasis error text |
|
|
136
|
+
|
|
137
|
+
### Status: Warning
|
|
138
|
+
|
|
139
|
+
| Token name | CSS variable | Purpose |
|
|
140
|
+
|---|---|---|
|
|
141
|
+
| `status-warning-bg` | `--fgv-status-warning-bg` | Warning row / badge background |
|
|
142
|
+
| `status-warning-surface` | `--fgv-status-warning-surface` | Hover background on warning items |
|
|
143
|
+
| `status-warning-btn` | `--fgv-status-warning-btn` | Warning action button fill |
|
|
144
|
+
| `status-warning-btn-hover` | `--fgv-status-warning-btn-hover` | Warning button hover fill |
|
|
145
|
+
| `status-warning-icon` | `--fgv-status-warning-icon` | Warning icon / dot color |
|
|
146
|
+
| `status-warning-border` | `--fgv-status-warning-border` | Warning section border |
|
|
147
|
+
| `status-warning-accent` | `--fgv-status-warning-accent` | Warning focus ring |
|
|
148
|
+
| `status-warning-text` | `--fgv-status-warning-text` | Warning message text |
|
|
149
|
+
| `status-warning-strong` | `--fgv-status-warning-strong` | High-emphasis warning text |
|
|
150
|
+
|
|
151
|
+
### Status: Info
|
|
152
|
+
|
|
153
|
+
| Token name | CSS variable | Purpose |
|
|
154
|
+
|---|---|---|
|
|
155
|
+
| `status-info-bg` | `--fgv-status-info-bg` | Info row / badge background |
|
|
156
|
+
| `status-info-surface` | `--fgv-status-info-surface` | Hover background on info items |
|
|
157
|
+
| `status-info-btn` | `--fgv-status-info-btn` | Info action button fill |
|
|
158
|
+
| `status-info-btn-hover` | `--fgv-status-info-btn-hover` | Info button hover fill |
|
|
159
|
+
| `status-info-icon` | `--fgv-status-info-icon` | Info icon / dot color |
|
|
160
|
+
| `status-info-border` | `--fgv-status-info-border` | Info section border |
|
|
161
|
+
| `status-info-accent` | `--fgv-status-info-accent` | Info focus ring |
|
|
162
|
+
| `status-info-text` | `--fgv-status-info-text` | Info message text |
|
|
163
|
+
| `status-info-strong` | `--fgv-status-info-strong` | High-emphasis info text |
|
|
164
|
+
|
|
165
|
+
### Status: Success
|
|
166
|
+
|
|
167
|
+
| Token name | CSS variable | Purpose |
|
|
168
|
+
|---|---|---|
|
|
169
|
+
| `status-success-bg` | `--fgv-status-success-bg` | Success row / badge background |
|
|
170
|
+
| `status-success-surface` | `--fgv-status-success-surface` | Hover background on success items |
|
|
171
|
+
| `status-success-btn` | `--fgv-status-success-btn` | Success action button fill |
|
|
172
|
+
| `status-success-btn-hover` | `--fgv-status-success-btn-hover` | Success button hover fill |
|
|
173
|
+
| `status-success-icon` | `--fgv-status-success-icon` | Success icon / dot color |
|
|
174
|
+
| `status-success-border` | `--fgv-status-success-border` | Success section border |
|
|
175
|
+
| `status-success-accent` | `--fgv-status-success-accent` | Success focus ring |
|
|
176
|
+
| `status-success-text` | `--fgv-status-success-text` | Success message text |
|
|
177
|
+
| `status-success-strong` | `--fgv-status-success-strong` | High-emphasis success text |
|
|
178
|
+
|
|
20
179
|
## Development
|
|
21
180
|
|
|
22
181
|
```bash
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* @fgv/ts-app-shell — Default theme
|
|
3
|
+
*
|
|
4
|
+
* Defines all semantic design tokens as CSS custom properties.
|
|
5
|
+
* Import this file once at your application entry point:
|
|
6
|
+
*
|
|
7
|
+
* import '@fgv/ts-app-shell/theme.css';
|
|
8
|
+
*
|
|
9
|
+
* Then add the Tailwind preset in your tailwind.config.js:
|
|
10
|
+
*
|
|
11
|
+
* const appShellPreset = require('@fgv/ts-app-shell/tailwind-preset');
|
|
12
|
+
* module.exports = { presets: [appShellPreset], ... };
|
|
13
|
+
*
|
|
14
|
+
* Dark mode: put class="dark" on <html> (or any ancestor).
|
|
15
|
+
* The ThemeProvider component handles this automatically.
|
|
16
|
+
*
|
|
17
|
+
* All variables use the --fgv- prefix and can be overridden by the consumer.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
/* ============================================================
|
|
21
|
+
* LIGHT MODE (default / :root)
|
|
22
|
+
* ============================================================ */
|
|
23
|
+
:root {
|
|
24
|
+
/* --- Surfaces --- */
|
|
25
|
+
/* Base application surface (page background) */
|
|
26
|
+
--fgv-surface: #ffffff;
|
|
27
|
+
/* Slightly elevated surface (cards, panels) */
|
|
28
|
+
--fgv-surface-raised: #f3f4f6;
|
|
29
|
+
/* Alternate surface (zebra rows, secondary panels) */
|
|
30
|
+
--fgv-surface-alt: #f9fafb;
|
|
31
|
+
/* Modal / dialog backdrop overlay */
|
|
32
|
+
--fgv-backdrop: rgba(0, 0, 0, 0.45);
|
|
33
|
+
/* Hover highlight background (subtle, interactive rows) */
|
|
34
|
+
--fgv-hover: #f3f4f6;
|
|
35
|
+
/* Selected item background (listbox rows, picker items) */
|
|
36
|
+
--fgv-selected: #eff6ff;
|
|
37
|
+
|
|
38
|
+
/* --- Text --- */
|
|
39
|
+
/* High-emphasis body text — minimum 7:1 on --fgv-surface (WCAG AAA) */
|
|
40
|
+
--fgv-text-primary: #111827;
|
|
41
|
+
/* Medium-emphasis secondary text — ~5.4:1 on #fff (WCAG AA) */
|
|
42
|
+
--fgv-text-secondary: #374151;
|
|
43
|
+
/* De-emphasized muted text — ~3.5:1, use only for decorative / supporting copy */
|
|
44
|
+
--fgv-text-muted: #6b7280;
|
|
45
|
+
/* Faintest ghost text — intentionally low contrast, only for truly ornamental content */
|
|
46
|
+
--fgv-text-faint: #9ca3af;
|
|
47
|
+
/* Star / favorite accent (golden) */
|
|
48
|
+
--fgv-text-star: #d97706;
|
|
49
|
+
|
|
50
|
+
/* --- Borders --- */
|
|
51
|
+
/* Default divider / input border */
|
|
52
|
+
--fgv-border: #d1d5db;
|
|
53
|
+
/* Subtle divider (row separators, inner section borders) */
|
|
54
|
+
--fgv-border-subtle: #e5e7eb;
|
|
55
|
+
|
|
56
|
+
/* --- Brand colors --- */
|
|
57
|
+
/* Primary action color (buttons, active nav, links) */
|
|
58
|
+
--fgv-brand-primary: #2563eb;
|
|
59
|
+
/* Darker tonal variant (pressed state, dark top-bar) */
|
|
60
|
+
--fgv-brand-secondary: #1d4ed8;
|
|
61
|
+
/* Accent / highlight (hover text on dark surfaces, checkmarks) */
|
|
62
|
+
--fgv-brand-accent: #3b82f6;
|
|
63
|
+
|
|
64
|
+
/* --- Focus ring --- */
|
|
65
|
+
--fgv-focus-ring: #3b82f6;
|
|
66
|
+
/* Selected item left-border accent */
|
|
67
|
+
--fgv-selected-border: #2563eb;
|
|
68
|
+
|
|
69
|
+
/* --- Status: error / danger --- */
|
|
70
|
+
--fgv-status-error-bg: #fef2f2;
|
|
71
|
+
--fgv-status-error-surface: #fee2e2;
|
|
72
|
+
--fgv-status-error-btn: #dc2626;
|
|
73
|
+
--fgv-status-error-btn-hover: #b91c1c;
|
|
74
|
+
--fgv-status-error-icon: #dc2626;
|
|
75
|
+
--fgv-status-error-border: #fca5a5;
|
|
76
|
+
--fgv-status-error-accent: #ef4444;
|
|
77
|
+
--fgv-status-error-text: #b91c1c;
|
|
78
|
+
--fgv-status-error-strong: #991b1b;
|
|
79
|
+
|
|
80
|
+
/* --- Status: warning --- */
|
|
81
|
+
--fgv-status-warning-bg: #fffbeb;
|
|
82
|
+
--fgv-status-warning-surface: #fef3c7;
|
|
83
|
+
--fgv-status-warning-btn: #d97706;
|
|
84
|
+
--fgv-status-warning-btn-hover: #b45309;
|
|
85
|
+
--fgv-status-warning-icon: #d97706;
|
|
86
|
+
--fgv-status-warning-border: #fcd34d;
|
|
87
|
+
--fgv-status-warning-accent: #f59e0b;
|
|
88
|
+
--fgv-status-warning-text: #92400e;
|
|
89
|
+
--fgv-status-warning-strong: #78350f;
|
|
90
|
+
|
|
91
|
+
/* --- Status: info --- */
|
|
92
|
+
--fgv-status-info-bg: #eff6ff;
|
|
93
|
+
--fgv-status-info-surface: #dbeafe;
|
|
94
|
+
--fgv-status-info-btn: #2563eb;
|
|
95
|
+
--fgv-status-info-btn-hover: #1d4ed8;
|
|
96
|
+
--fgv-status-info-icon: #3b82f6;
|
|
97
|
+
--fgv-status-info-border: #93c5fd;
|
|
98
|
+
--fgv-status-info-accent: #60a5fa;
|
|
99
|
+
--fgv-status-info-text: #1e40af;
|
|
100
|
+
--fgv-status-info-strong: #1e3a8a;
|
|
101
|
+
|
|
102
|
+
/* --- Status: success --- */
|
|
103
|
+
--fgv-status-success-bg: #f0fdf4;
|
|
104
|
+
--fgv-status-success-surface: #dcfce7;
|
|
105
|
+
--fgv-status-success-btn: #16a34a;
|
|
106
|
+
--fgv-status-success-btn-hover: #15803d;
|
|
107
|
+
--fgv-status-success-icon: #16a34a;
|
|
108
|
+
--fgv-status-success-border: #86efac;
|
|
109
|
+
--fgv-status-success-accent: #4ade80;
|
|
110
|
+
--fgv-status-success-text: #166534;
|
|
111
|
+
--fgv-status-success-strong: #14532d;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/* ============================================================
|
|
115
|
+
* DARK MODE (.dark on any ancestor element)
|
|
116
|
+
* ============================================================ */
|
|
117
|
+
.dark {
|
|
118
|
+
/* --- Surfaces --- */
|
|
119
|
+
--fgv-surface: #1f2937;
|
|
120
|
+
--fgv-surface-raised: #374151;
|
|
121
|
+
--fgv-surface-alt: #111827;
|
|
122
|
+
--fgv-backdrop: rgba(0, 0, 0, 0.65);
|
|
123
|
+
--fgv-hover: #374151;
|
|
124
|
+
--fgv-selected: #1e3a5f;
|
|
125
|
+
|
|
126
|
+
/* --- Text --- */
|
|
127
|
+
/* ~14:1 on --fgv-surface dark (WCAG AAA) */
|
|
128
|
+
--fgv-text-primary: #f9fafb;
|
|
129
|
+
/* ~8:1 on dark surface (WCAG AA) */
|
|
130
|
+
--fgv-text-secondary: #d1d5db;
|
|
131
|
+
/* ~4.5:1 on dark surface (WCAG AA) */
|
|
132
|
+
--fgv-text-muted: #9ca3af;
|
|
133
|
+
/* Faint ghost text — intentionally low contrast */
|
|
134
|
+
--fgv-text-faint: #6b7280;
|
|
135
|
+
/* Star accent — brightened slightly for dark bg */
|
|
136
|
+
--fgv-text-star: #fbbf24;
|
|
137
|
+
|
|
138
|
+
/* --- Borders --- */
|
|
139
|
+
--fgv-border: #4b5563;
|
|
140
|
+
--fgv-border-subtle: #374151;
|
|
141
|
+
|
|
142
|
+
/* --- Brand colors --- */
|
|
143
|
+
--fgv-brand-primary: #3b82f6;
|
|
144
|
+
--fgv-brand-secondary: #1e3a5f;
|
|
145
|
+
--fgv-brand-accent: #60a5fa;
|
|
146
|
+
|
|
147
|
+
/* --- Focus ring --- */
|
|
148
|
+
--fgv-focus-ring: #60a5fa;
|
|
149
|
+
--fgv-selected-border: #3b82f6;
|
|
150
|
+
|
|
151
|
+
/* --- Status: error / danger --- */
|
|
152
|
+
--fgv-status-error-bg: #450a0a;
|
|
153
|
+
--fgv-status-error-surface: #7f1d1d;
|
|
154
|
+
--fgv-status-error-btn: #dc2626;
|
|
155
|
+
--fgv-status-error-btn-hover: #b91c1c;
|
|
156
|
+
--fgv-status-error-icon: #f87171;
|
|
157
|
+
--fgv-status-error-border: #7f1d1d;
|
|
158
|
+
--fgv-status-error-accent: #ef4444;
|
|
159
|
+
--fgv-status-error-text: #fca5a5;
|
|
160
|
+
--fgv-status-error-strong: #fecaca;
|
|
161
|
+
|
|
162
|
+
/* --- Status: warning --- */
|
|
163
|
+
--fgv-status-warning-bg: #451a03;
|
|
164
|
+
--fgv-status-warning-surface: #78350f;
|
|
165
|
+
--fgv-status-warning-btn: #d97706;
|
|
166
|
+
--fgv-status-warning-btn-hover: #b45309;
|
|
167
|
+
--fgv-status-warning-icon: #fbbf24;
|
|
168
|
+
--fgv-status-warning-border: #78350f;
|
|
169
|
+
--fgv-status-warning-accent: #f59e0b;
|
|
170
|
+
--fgv-status-warning-text: #fde68a;
|
|
171
|
+
--fgv-status-warning-strong: #fef3c7;
|
|
172
|
+
|
|
173
|
+
/* --- Status: info --- */
|
|
174
|
+
--fgv-status-info-bg: #0c1d3e;
|
|
175
|
+
--fgv-status-info-surface: #1e3a5f;
|
|
176
|
+
--fgv-status-info-btn: #3b82f6;
|
|
177
|
+
--fgv-status-info-btn-hover: #2563eb;
|
|
178
|
+
--fgv-status-info-icon: #60a5fa;
|
|
179
|
+
--fgv-status-info-border: #1e3a5f;
|
|
180
|
+
--fgv-status-info-accent: #93c5fd;
|
|
181
|
+
--fgv-status-info-text: #bfdbfe;
|
|
182
|
+
--fgv-status-info-strong: #dbeafe;
|
|
183
|
+
|
|
184
|
+
/* --- Status: success --- */
|
|
185
|
+
--fgv-status-success-bg: #052e16;
|
|
186
|
+
--fgv-status-success-surface: #14532d;
|
|
187
|
+
--fgv-status-success-btn: #16a34a;
|
|
188
|
+
--fgv-status-success-btn-hover: #15803d;
|
|
189
|
+
--fgv-status-success-icon: #4ade80;
|
|
190
|
+
--fgv-status-success-border: #14532d;
|
|
191
|
+
--fgv-status-success-accent: #86efac;
|
|
192
|
+
--fgv-status-success-text: #bbf7d0;
|
|
193
|
+
--fgv-status-success-strong: #dcfce7;
|
|
194
|
+
}
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* @fgv/ts-app-shell — Default theme
|
|
3
|
+
*
|
|
4
|
+
* Defines all semantic design tokens as CSS custom properties.
|
|
5
|
+
* Import this file once at your application entry point:
|
|
6
|
+
*
|
|
7
|
+
* import '@fgv/ts-app-shell/theme.css';
|
|
8
|
+
*
|
|
9
|
+
* Then add the Tailwind preset in your tailwind.config.js:
|
|
10
|
+
*
|
|
11
|
+
* const appShellPreset = require('@fgv/ts-app-shell/tailwind-preset');
|
|
12
|
+
* module.exports = { presets: [appShellPreset], ... };
|
|
13
|
+
*
|
|
14
|
+
* Dark mode: put class="dark" on <html> (or any ancestor).
|
|
15
|
+
* The ThemeProvider component handles this automatically.
|
|
16
|
+
*
|
|
17
|
+
* All variables use the --fgv- prefix and can be overridden by the consumer.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
/* ============================================================
|
|
21
|
+
* LIGHT MODE (default / :root)
|
|
22
|
+
* ============================================================ */
|
|
23
|
+
:root {
|
|
24
|
+
/* --- Surfaces --- */
|
|
25
|
+
/* Base application surface (page background) */
|
|
26
|
+
--fgv-surface: #ffffff;
|
|
27
|
+
/* Slightly elevated surface (cards, panels) */
|
|
28
|
+
--fgv-surface-raised: #f3f4f6;
|
|
29
|
+
/* Alternate surface (zebra rows, secondary panels) */
|
|
30
|
+
--fgv-surface-alt: #f9fafb;
|
|
31
|
+
/* Modal / dialog backdrop overlay */
|
|
32
|
+
--fgv-backdrop: rgba(0, 0, 0, 0.45);
|
|
33
|
+
/* Hover highlight background (subtle, interactive rows) */
|
|
34
|
+
--fgv-hover: #f3f4f6;
|
|
35
|
+
/* Selected item background (listbox rows, picker items) */
|
|
36
|
+
--fgv-selected: #eff6ff;
|
|
37
|
+
|
|
38
|
+
/* --- Text --- */
|
|
39
|
+
/* High-emphasis body text — minimum 7:1 on --fgv-surface (WCAG AAA) */
|
|
40
|
+
--fgv-text-primary: #111827;
|
|
41
|
+
/* Medium-emphasis secondary text — ~5.4:1 on #fff (WCAG AA) */
|
|
42
|
+
--fgv-text-secondary: #374151;
|
|
43
|
+
/* De-emphasized muted text — ~3.5:1, use only for decorative / supporting copy */
|
|
44
|
+
--fgv-text-muted: #6b7280;
|
|
45
|
+
/* Faintest ghost text — intentionally low contrast, only for truly ornamental content */
|
|
46
|
+
--fgv-text-faint: #9ca3af;
|
|
47
|
+
/* Star / favorite accent (golden) */
|
|
48
|
+
--fgv-text-star: #d97706;
|
|
49
|
+
|
|
50
|
+
/* --- Borders --- */
|
|
51
|
+
/* Default divider / input border */
|
|
52
|
+
--fgv-border: #d1d5db;
|
|
53
|
+
/* Subtle divider (row separators, inner section borders) */
|
|
54
|
+
--fgv-border-subtle: #e5e7eb;
|
|
55
|
+
|
|
56
|
+
/* --- Brand colors --- */
|
|
57
|
+
/* Primary action color (buttons, active nav, links) */
|
|
58
|
+
--fgv-brand-primary: #2563eb;
|
|
59
|
+
/* Darker tonal variant (pressed state, dark top-bar) */
|
|
60
|
+
--fgv-brand-secondary: #1d4ed8;
|
|
61
|
+
/* Accent / highlight (hover text on dark surfaces, checkmarks) */
|
|
62
|
+
--fgv-brand-accent: #3b82f6;
|
|
63
|
+
|
|
64
|
+
/* --- Focus ring --- */
|
|
65
|
+
--fgv-focus-ring: #3b82f6;
|
|
66
|
+
/* Selected item left-border accent */
|
|
67
|
+
--fgv-selected-border: #2563eb;
|
|
68
|
+
|
|
69
|
+
/* --- Status: error / danger --- */
|
|
70
|
+
--fgv-status-error-bg: #fef2f2;
|
|
71
|
+
--fgv-status-error-surface: #fee2e2;
|
|
72
|
+
--fgv-status-error-btn: #dc2626;
|
|
73
|
+
--fgv-status-error-btn-hover: #b91c1c;
|
|
74
|
+
--fgv-status-error-icon: #dc2626;
|
|
75
|
+
--fgv-status-error-border: #fca5a5;
|
|
76
|
+
--fgv-status-error-accent: #ef4444;
|
|
77
|
+
--fgv-status-error-text: #b91c1c;
|
|
78
|
+
--fgv-status-error-strong: #991b1b;
|
|
79
|
+
|
|
80
|
+
/* --- Status: warning --- */
|
|
81
|
+
--fgv-status-warning-bg: #fffbeb;
|
|
82
|
+
--fgv-status-warning-surface: #fef3c7;
|
|
83
|
+
--fgv-status-warning-btn: #d97706;
|
|
84
|
+
--fgv-status-warning-btn-hover: #b45309;
|
|
85
|
+
--fgv-status-warning-icon: #d97706;
|
|
86
|
+
--fgv-status-warning-border: #fcd34d;
|
|
87
|
+
--fgv-status-warning-accent: #f59e0b;
|
|
88
|
+
--fgv-status-warning-text: #92400e;
|
|
89
|
+
--fgv-status-warning-strong: #78350f;
|
|
90
|
+
|
|
91
|
+
/* --- Status: info --- */
|
|
92
|
+
--fgv-status-info-bg: #eff6ff;
|
|
93
|
+
--fgv-status-info-surface: #dbeafe;
|
|
94
|
+
--fgv-status-info-btn: #2563eb;
|
|
95
|
+
--fgv-status-info-btn-hover: #1d4ed8;
|
|
96
|
+
--fgv-status-info-icon: #3b82f6;
|
|
97
|
+
--fgv-status-info-border: #93c5fd;
|
|
98
|
+
--fgv-status-info-accent: #60a5fa;
|
|
99
|
+
--fgv-status-info-text: #1e40af;
|
|
100
|
+
--fgv-status-info-strong: #1e3a8a;
|
|
101
|
+
|
|
102
|
+
/* --- Status: success --- */
|
|
103
|
+
--fgv-status-success-bg: #f0fdf4;
|
|
104
|
+
--fgv-status-success-surface: #dcfce7;
|
|
105
|
+
--fgv-status-success-btn: #16a34a;
|
|
106
|
+
--fgv-status-success-btn-hover: #15803d;
|
|
107
|
+
--fgv-status-success-icon: #16a34a;
|
|
108
|
+
--fgv-status-success-border: #86efac;
|
|
109
|
+
--fgv-status-success-accent: #4ade80;
|
|
110
|
+
--fgv-status-success-text: #166534;
|
|
111
|
+
--fgv-status-success-strong: #14532d;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/* ============================================================
|
|
115
|
+
* DARK MODE (.dark on any ancestor element)
|
|
116
|
+
* ============================================================ */
|
|
117
|
+
.dark {
|
|
118
|
+
/* --- Surfaces --- */
|
|
119
|
+
--fgv-surface: #1f2937;
|
|
120
|
+
--fgv-surface-raised: #374151;
|
|
121
|
+
--fgv-surface-alt: #111827;
|
|
122
|
+
--fgv-backdrop: rgba(0, 0, 0, 0.65);
|
|
123
|
+
--fgv-hover: #374151;
|
|
124
|
+
--fgv-selected: #1e3a5f;
|
|
125
|
+
|
|
126
|
+
/* --- Text --- */
|
|
127
|
+
/* ~14:1 on --fgv-surface dark (WCAG AAA) */
|
|
128
|
+
--fgv-text-primary: #f9fafb;
|
|
129
|
+
/* ~8:1 on dark surface (WCAG AA) */
|
|
130
|
+
--fgv-text-secondary: #d1d5db;
|
|
131
|
+
/* ~4.5:1 on dark surface (WCAG AA) */
|
|
132
|
+
--fgv-text-muted: #9ca3af;
|
|
133
|
+
/* Faint ghost text — intentionally low contrast */
|
|
134
|
+
--fgv-text-faint: #6b7280;
|
|
135
|
+
/* Star accent — brightened slightly for dark bg */
|
|
136
|
+
--fgv-text-star: #fbbf24;
|
|
137
|
+
|
|
138
|
+
/* --- Borders --- */
|
|
139
|
+
--fgv-border: #4b5563;
|
|
140
|
+
--fgv-border-subtle: #374151;
|
|
141
|
+
|
|
142
|
+
/* --- Brand colors --- */
|
|
143
|
+
--fgv-brand-primary: #3b82f6;
|
|
144
|
+
--fgv-brand-secondary: #1e3a5f;
|
|
145
|
+
--fgv-brand-accent: #60a5fa;
|
|
146
|
+
|
|
147
|
+
/* --- Focus ring --- */
|
|
148
|
+
--fgv-focus-ring: #60a5fa;
|
|
149
|
+
--fgv-selected-border: #3b82f6;
|
|
150
|
+
|
|
151
|
+
/* --- Status: error / danger --- */
|
|
152
|
+
--fgv-status-error-bg: #450a0a;
|
|
153
|
+
--fgv-status-error-surface: #7f1d1d;
|
|
154
|
+
--fgv-status-error-btn: #dc2626;
|
|
155
|
+
--fgv-status-error-btn-hover: #b91c1c;
|
|
156
|
+
--fgv-status-error-icon: #f87171;
|
|
157
|
+
--fgv-status-error-border: #7f1d1d;
|
|
158
|
+
--fgv-status-error-accent: #ef4444;
|
|
159
|
+
--fgv-status-error-text: #fca5a5;
|
|
160
|
+
--fgv-status-error-strong: #fecaca;
|
|
161
|
+
|
|
162
|
+
/* --- Status: warning --- */
|
|
163
|
+
--fgv-status-warning-bg: #451a03;
|
|
164
|
+
--fgv-status-warning-surface: #78350f;
|
|
165
|
+
--fgv-status-warning-btn: #d97706;
|
|
166
|
+
--fgv-status-warning-btn-hover: #b45309;
|
|
167
|
+
--fgv-status-warning-icon: #fbbf24;
|
|
168
|
+
--fgv-status-warning-border: #78350f;
|
|
169
|
+
--fgv-status-warning-accent: #f59e0b;
|
|
170
|
+
--fgv-status-warning-text: #fde68a;
|
|
171
|
+
--fgv-status-warning-strong: #fef3c7;
|
|
172
|
+
|
|
173
|
+
/* --- Status: info --- */
|
|
174
|
+
--fgv-status-info-bg: #0c1d3e;
|
|
175
|
+
--fgv-status-info-surface: #1e3a5f;
|
|
176
|
+
--fgv-status-info-btn: #3b82f6;
|
|
177
|
+
--fgv-status-info-btn-hover: #2563eb;
|
|
178
|
+
--fgv-status-info-icon: #60a5fa;
|
|
179
|
+
--fgv-status-info-border: #1e3a5f;
|
|
180
|
+
--fgv-status-info-accent: #93c5fd;
|
|
181
|
+
--fgv-status-info-text: #bfdbfe;
|
|
182
|
+
--fgv-status-info-strong: #dbeafe;
|
|
183
|
+
|
|
184
|
+
/* --- Status: success --- */
|
|
185
|
+
--fgv-status-success-bg: #052e16;
|
|
186
|
+
--fgv-status-success-surface: #14532d;
|
|
187
|
+
--fgv-status-success-btn: #16a34a;
|
|
188
|
+
--fgv-status-success-btn-hover: #15803d;
|
|
189
|
+
--fgv-status-success-icon: #4ade80;
|
|
190
|
+
--fgv-status-success-border: #14532d;
|
|
191
|
+
--fgv-status-success-accent: #86efac;
|
|
192
|
+
--fgv-status-success-text: #bbf7d0;
|
|
193
|
+
--fgv-status-success-strong: #dcfce7;
|
|
194
|
+
}
|
package/package.json
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fgv/ts-app-shell",
|
|
3
|
-
"version": "5.1.0-
|
|
3
|
+
"version": "5.1.0-30",
|
|
4
4
|
"description": "Shared React UI primitives for application shells: column cascade, sidebar, toast/log messages, command palette, keybinding registry",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"types": "lib/index.d.ts",
|
|
7
|
-
"sideEffects":
|
|
7
|
+
"sideEffects": [
|
|
8
|
+
"**/*.css"
|
|
9
|
+
],
|
|
8
10
|
"keywords": [
|
|
9
11
|
"typescript",
|
|
10
12
|
"react",
|
|
@@ -17,8 +19,8 @@
|
|
|
17
19
|
"dependencies": {
|
|
18
20
|
"@heroicons/react": "~2.2.0",
|
|
19
21
|
"tslib": "^2.8.1",
|
|
20
|
-
"@fgv/ts-
|
|
21
|
-
"@fgv/ts-
|
|
22
|
+
"@fgv/ts-utils": "5.1.0-30",
|
|
23
|
+
"@fgv/ts-extras": "5.1.0-30"
|
|
22
24
|
},
|
|
23
25
|
"peerDependencies": {
|
|
24
26
|
"react": ">=18.0.0",
|
|
@@ -51,8 +53,8 @@
|
|
|
51
53
|
"@rushstack/heft-jest-plugin": "1.2.6",
|
|
52
54
|
"@testing-library/dom": "^10.4.0",
|
|
53
55
|
"@rushstack/heft-node-rig": "2.11.27",
|
|
54
|
-
"@fgv/
|
|
55
|
-
"@fgv/
|
|
56
|
+
"@fgv/heft-dual-rig": "5.1.0-30",
|
|
57
|
+
"@fgv/ts-utils-jest": "5.1.0-30"
|
|
56
58
|
},
|
|
57
59
|
"exports": {
|
|
58
60
|
".": {
|
|
@@ -65,7 +67,9 @@
|
|
|
65
67
|
"require": "./lib/index.browser.js"
|
|
66
68
|
},
|
|
67
69
|
"types": "lib/index.d.ts"
|
|
68
|
-
}
|
|
70
|
+
},
|
|
71
|
+
"./tailwind-preset": "./tailwind-preset.cjs",
|
|
72
|
+
"./theme.css": "./lib/styles/theme.css"
|
|
69
73
|
},
|
|
70
74
|
"repository": {
|
|
71
75
|
"type": "git",
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fgv/ts-app-shell — Tailwind CSS preset
|
|
3
|
+
*
|
|
4
|
+
* Maps the package's semantic design tokens to the CSS custom properties
|
|
5
|
+
* defined in theme.css. Add this preset to your tailwind.config.js and
|
|
6
|
+
* import theme.css once at your application entry point.
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
*
|
|
10
|
+
* // tailwind.config.js
|
|
11
|
+
* const appShellPreset = require('@fgv/ts-app-shell/tailwind-preset');
|
|
12
|
+
* module.exports = {
|
|
13
|
+
* presets: [appShellPreset],
|
|
14
|
+
* content: ['./src/**\/*.{ts,tsx}', './node_modules/@fgv/ts-app-shell/lib/**\/*.{js,jsx}'],
|
|
15
|
+
* // ...your config
|
|
16
|
+
* };
|
|
17
|
+
*
|
|
18
|
+
* // application entry (e.g. main.tsx or index.css)
|
|
19
|
+
* import '@fgv/ts-app-shell/theme.css';
|
|
20
|
+
*
|
|
21
|
+
* // dark mode: add class="dark" to <html> (ThemeProvider does this automatically)
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
/** @type {import('tailwindcss').Config} */
|
|
25
|
+
module.exports = {
|
|
26
|
+
darkMode: 'class',
|
|
27
|
+
theme: {
|
|
28
|
+
extend: {
|
|
29
|
+
colors: {
|
|
30
|
+
/* ----- Surfaces ----- */
|
|
31
|
+
surface: 'var(--fgv-surface)',
|
|
32
|
+
'surface-raised': 'var(--fgv-surface-raised)',
|
|
33
|
+
'surface-alt': 'var(--fgv-surface-alt)',
|
|
34
|
+
backdrop: 'var(--fgv-backdrop)',
|
|
35
|
+
hover: 'var(--fgv-hover)',
|
|
36
|
+
selected: 'var(--fgv-selected)',
|
|
37
|
+
|
|
38
|
+
/* ----- Text ----- */
|
|
39
|
+
primary: 'var(--fgv-text-primary)',
|
|
40
|
+
secondary: 'var(--fgv-text-secondary)',
|
|
41
|
+
muted: 'var(--fgv-text-muted)',
|
|
42
|
+
faint: 'var(--fgv-text-faint)',
|
|
43
|
+
star: 'var(--fgv-text-star)',
|
|
44
|
+
|
|
45
|
+
/* ----- Borders ----- */
|
|
46
|
+
border: 'var(--fgv-border)',
|
|
47
|
+
'border-subtle': 'var(--fgv-border-subtle)',
|
|
48
|
+
'focus-ring': 'var(--fgv-focus-ring)',
|
|
49
|
+
'selected-border': 'var(--fgv-selected-border)',
|
|
50
|
+
|
|
51
|
+
/* ----- Brand ----- */
|
|
52
|
+
'brand-primary': 'var(--fgv-brand-primary)',
|
|
53
|
+
'brand-secondary': 'var(--fgv-brand-secondary)',
|
|
54
|
+
'brand-accent': 'var(--fgv-brand-accent)',
|
|
55
|
+
|
|
56
|
+
/* ----- Status: error / danger ----- */
|
|
57
|
+
'status-error-bg': 'var(--fgv-status-error-bg)',
|
|
58
|
+
'status-error-surface': 'var(--fgv-status-error-surface)',
|
|
59
|
+
'status-error-btn': 'var(--fgv-status-error-btn)',
|
|
60
|
+
'status-error-btn-hover': 'var(--fgv-status-error-btn-hover)',
|
|
61
|
+
'status-error-icon': 'var(--fgv-status-error-icon)',
|
|
62
|
+
'status-error-border': 'var(--fgv-status-error-border)',
|
|
63
|
+
'status-error-accent': 'var(--fgv-status-error-accent)',
|
|
64
|
+
'status-error-text': 'var(--fgv-status-error-text)',
|
|
65
|
+
'status-error-strong': 'var(--fgv-status-error-strong)',
|
|
66
|
+
|
|
67
|
+
/* ----- Status: warning ----- */
|
|
68
|
+
'status-warning-bg': 'var(--fgv-status-warning-bg)',
|
|
69
|
+
'status-warning-surface': 'var(--fgv-status-warning-surface)',
|
|
70
|
+
'status-warning-btn': 'var(--fgv-status-warning-btn)',
|
|
71
|
+
'status-warning-btn-hover': 'var(--fgv-status-warning-btn-hover)',
|
|
72
|
+
'status-warning-icon': 'var(--fgv-status-warning-icon)',
|
|
73
|
+
'status-warning-border': 'var(--fgv-status-warning-border)',
|
|
74
|
+
'status-warning-accent': 'var(--fgv-status-warning-accent)',
|
|
75
|
+
'status-warning-text': 'var(--fgv-status-warning-text)',
|
|
76
|
+
'status-warning-strong': 'var(--fgv-status-warning-strong)',
|
|
77
|
+
|
|
78
|
+
/* ----- Status: info ----- */
|
|
79
|
+
'status-info-bg': 'var(--fgv-status-info-bg)',
|
|
80
|
+
'status-info-surface': 'var(--fgv-status-info-surface)',
|
|
81
|
+
'status-info-btn': 'var(--fgv-status-info-btn)',
|
|
82
|
+
'status-info-btn-hover': 'var(--fgv-status-info-btn-hover)',
|
|
83
|
+
'status-info-icon': 'var(--fgv-status-info-icon)',
|
|
84
|
+
'status-info-border': 'var(--fgv-status-info-border)',
|
|
85
|
+
'status-info-accent': 'var(--fgv-status-info-accent)',
|
|
86
|
+
'status-info-text': 'var(--fgv-status-info-text)',
|
|
87
|
+
'status-info-strong': 'var(--fgv-status-info-strong)',
|
|
88
|
+
|
|
89
|
+
/* ----- Status: success ----- */
|
|
90
|
+
'status-success-bg': 'var(--fgv-status-success-bg)',
|
|
91
|
+
'status-success-surface': 'var(--fgv-status-success-surface)',
|
|
92
|
+
'status-success-btn': 'var(--fgv-status-success-btn)',
|
|
93
|
+
'status-success-btn-hover': 'var(--fgv-status-success-btn-hover)',
|
|
94
|
+
'status-success-icon': 'var(--fgv-status-success-icon)',
|
|
95
|
+
'status-success-border': 'var(--fgv-status-success-border)',
|
|
96
|
+
'status-success-accent': 'var(--fgv-status-success-accent)',
|
|
97
|
+
'status-success-text': 'var(--fgv-status-success-text)',
|
|
98
|
+
'status-success-strong': 'var(--fgv-status-success-strong)'
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
};
|