@fiestaboard/ui 1.6.3
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/LICENSE +21 -0
- package/README.md +153 -0
- package/dist/components/board/board-display.d.ts +35 -0
- package/dist/components/board/board-display.js +514 -0
- package/dist/components/board/board-display.js.map +1 -0
- package/dist/components/board/board-teaser.d.ts +10 -0
- package/dist/components/board/board-teaser.js +110 -0
- package/dist/components/board/board-teaser.js.map +1 -0
- package/dist/components/board/scaled-board-display.d.ts +37 -0
- package/dist/components/board/scaled-board-display.js +118 -0
- package/dist/components/board/scaled-board-display.js.map +1 -0
- package/dist/components/board/static-board-display.d.ts +17 -0
- package/dist/components/board/static-board-display.js +140 -0
- package/dist/components/board/static-board-display.js.map +1 -0
- package/dist/components/chrome/board-icon.d.ts +11 -0
- package/dist/components/chrome/board-icon.js +31 -0
- package/dist/components/chrome/board-icon.js.map +1 -0
- package/dist/components/chrome/board-selector.d.ts +30 -0
- package/dist/components/chrome/board-selector.js +37 -0
- package/dist/components/chrome/board-selector.js.map +1 -0
- package/dist/components/chrome/fiesta-icon.d.ts +28 -0
- package/dist/components/chrome/fiesta-icon.js +129 -0
- package/dist/components/chrome/fiesta-icon.js.map +1 -0
- package/dist/components/chrome/fiesta-logo.d.ts +6 -0
- package/dist/components/chrome/fiesta-logo.js +20 -0
- package/dist/components/chrome/fiesta-logo.js.map +1 -0
- package/dist/components/chrome/language-selector.d.ts +21 -0
- package/dist/components/chrome/language-selector.js +29 -0
- package/dist/components/chrome/language-selector.js.map +1 -0
- package/dist/components/chrome/main-content.d.ts +28 -0
- package/dist/components/chrome/main-content.js +33 -0
- package/dist/components/chrome/main-content.js.map +1 -0
- package/dist/components/chrome/page-header.d.ts +23 -0
- package/dist/components/chrome/page-header.js +69 -0
- package/dist/components/chrome/page-header.js.map +1 -0
- package/dist/components/chrome/page-layout.d.ts +10 -0
- package/dist/components/chrome/page-layout.js +17 -0
- package/dist/components/chrome/page-layout.js.map +1 -0
- package/dist/components/chrome/page-toolbar.d.ts +7 -0
- package/dist/components/chrome/page-toolbar.js +21 -0
- package/dist/components/chrome/page-toolbar.js.map +1 -0
- package/dist/components/chrome/sidebar.d.ts +83 -0
- package/dist/components/chrome/sidebar.js +318 -0
- package/dist/components/chrome/sidebar.js.map +1 -0
- package/dist/components/chrome/skip-to-content.d.ts +8 -0
- package/dist/components/chrome/skip-to-content.js +15 -0
- package/dist/components/chrome/skip-to-content.js.map +1 -0
- package/dist/components/chrome/theme-toggle.d.ts +22 -0
- package/dist/components/chrome/theme-toggle.js +28 -0
- package/dist/components/chrome/theme-toggle.js.map +1 -0
- package/dist/components/seasons/aurora-canvas.d.ts +22 -0
- package/dist/components/seasons/aurora-canvas.js +88 -0
- package/dist/components/seasons/aurora-canvas.js.map +1 -0
- package/dist/components/seasons/sidebar-aurora-horizontal.d.ts +3 -0
- package/dist/components/seasons/sidebar-aurora-horizontal.js +47 -0
- package/dist/components/seasons/sidebar-aurora-horizontal.js.map +1 -0
- package/dist/components/seasons/sidebar-aurora.d.ts +3 -0
- package/dist/components/seasons/sidebar-aurora.js +57 -0
- package/dist/components/seasons/sidebar-aurora.js.map +1 -0
- package/dist/components/ui/accordion.d.ts +16 -0
- package/dist/components/ui/accordion.js +55 -0
- package/dist/components/ui/accordion.js.map +1 -0
- package/dist/components/ui/alert-dialog.d.ts +16 -0
- package/dist/components/ui/alert-dialog.js +78 -0
- package/dist/components/ui/alert-dialog.js.map +1 -0
- package/dist/components/ui/alert.d.ts +9 -0
- package/dist/components/ui/alert.js +41 -0
- package/dist/components/ui/alert.js.map +1 -0
- package/dist/components/ui/aurora.d.ts +9 -0
- package/dist/components/ui/aurora.js +105 -0
- package/dist/components/ui/aurora.js.map +1 -0
- package/dist/components/ui/badge.d.ts +9 -0
- package/dist/components/ui/badge.js +35 -0
- package/dist/components/ui/badge.js.map +1 -0
- package/dist/components/ui/box.d.ts +20 -0
- package/dist/components/ui/box.js +14 -0
- package/dist/components/ui/box.js.map +1 -0
- package/dist/components/ui/button.d.ts +10 -0
- package/dist/components/ui/button.js +52 -0
- package/dist/components/ui/button.js.map +1 -0
- package/dist/components/ui/card.d.ts +11 -0
- package/dist/components/ui/card.js +57 -0
- package/dist/components/ui/card.js.map +1 -0
- package/dist/components/ui/checkbox.d.ts +4 -0
- package/dist/components/ui/checkbox.js +16 -0
- package/dist/components/ui/checkbox.js.map +1 -0
- package/dist/components/ui/code.d.ts +8 -0
- package/dist/components/ui/code.js +15 -0
- package/dist/components/ui/code.js.map +1 -0
- package/dist/components/ui/collapsible.d.ts +8 -0
- package/dist/components/ui/collapsible.js +28 -0
- package/dist/components/ui/collapsible.js.map +1 -0
- package/dist/components/ui/dialog.d.ts +17 -0
- package/dist/components/ui/dialog.js +77 -0
- package/dist/components/ui/dialog.js.map +1 -0
- package/dist/components/ui/dropdown-menu.d.ts +32 -0
- package/dist/components/ui/dropdown-menu.js +129 -0
- package/dist/components/ui/dropdown-menu.js.map +1 -0
- package/dist/components/ui/empty-state.d.ts +23 -0
- package/dist/components/ui/empty-state.js +49 -0
- package/dist/components/ui/empty-state.js.map +1 -0
- package/dist/components/ui/flex.d.ts +24 -0
- package/dist/components/ui/flex.js +61 -0
- package/dist/components/ui/flex.js.map +1 -0
- package/dist/components/ui/grid.d.ts +20 -0
- package/dist/components/ui/grid.js +77 -0
- package/dist/components/ui/grid.js.map +1 -0
- package/dist/components/ui/heading.d.ts +18 -0
- package/dist/components/ui/heading.js +26 -0
- package/dist/components/ui/heading.js.map +1 -0
- package/dist/components/ui/input.d.ts +4 -0
- package/dist/components/ui/input.js +16 -0
- package/dist/components/ui/input.js.map +1 -0
- package/dist/components/ui/label.d.ts +4 -0
- package/dist/components/ui/label.js +15 -0
- package/dist/components/ui/label.js.map +1 -0
- package/dist/components/ui/list.d.ts +25 -0
- package/dist/components/ui/list.js +46 -0
- package/dist/components/ui/list.js.map +1 -0
- package/dist/components/ui/react-bits/decrypted-text.d.ts +14 -0
- package/dist/components/ui/react-bits/decrypted-text.js +88 -0
- package/dist/components/ui/react-bits/decrypted-text.js.map +1 -0
- package/dist/components/ui/react-bits/fade-content.d.ts +11 -0
- package/dist/components/ui/react-bits/fade-content.js +48 -0
- package/dist/components/ui/react-bits/fade-content.js.map +1 -0
- package/dist/components/ui/scroll-area.d.ts +5 -0
- package/dist/components/ui/scroll-area.js +39 -0
- package/dist/components/ui/scroll-area.js.map +1 -0
- package/dist/components/ui/select.d.ts +26 -0
- package/dist/components/ui/select.js +132 -0
- package/dist/components/ui/select.js.map +1 -0
- package/dist/components/ui/sheet.d.ts +29 -0
- package/dist/components/ui/sheet.js +114 -0
- package/dist/components/ui/sheet.js.map +1 -0
- package/dist/components/ui/skeleton.d.ts +2 -0
- package/dist/components/ui/skeleton.js +14 -0
- package/dist/components/ui/skeleton.js.map +1 -0
- package/dist/components/ui/slider.d.ts +9 -0
- package/dist/components/ui/slider.js +39 -0
- package/dist/components/ui/slider.js.map +1 -0
- package/dist/components/ui/stack.d.ts +16 -0
- package/dist/components/ui/stack.js +44 -0
- package/dist/components/ui/stack.js.map +1 -0
- package/dist/components/ui/switch.d.ts +4 -0
- package/dist/components/ui/switch.js +24 -0
- package/dist/components/ui/switch.js.map +1 -0
- package/dist/components/ui/table.d.ts +13 -0
- package/dist/components/ui/table.js +54 -0
- package/dist/components/ui/table.js.map +1 -0
- package/dist/components/ui/tabs.d.ts +7 -0
- package/dist/components/ui/tabs.js +37 -0
- package/dist/components/ui/tabs.js.map +1 -0
- package/dist/components/ui/text-link.d.ts +9 -0
- package/dist/components/ui/text-link.js +15 -0
- package/dist/components/ui/text-link.js.map +1 -0
- package/dist/components/ui/text.d.ts +22 -0
- package/dist/components/ui/text.js +48 -0
- package/dist/components/ui/text.js.map +1 -0
- package/dist/components/ui/textarea.d.ts +3 -0
- package/dist/components/ui/textarea.js +15 -0
- package/dist/components/ui/textarea.js.map +1 -0
- package/dist/components/ui/tooltip.d.ts +16 -0
- package/dist/components/ui/tooltip.js +38 -0
- package/dist/components/ui/tooltip.js.map +1 -0
- package/dist/fonts.css +16 -0
- package/dist/index.d.ts +66 -0
- package/dist/index.js +59 -0
- package/dist/lib/board-characters.d.ts +55 -0
- package/dist/lib/board-characters.js +151 -0
- package/dist/lib/board-characters.js.map +1 -0
- package/dist/lib/board-colors.d.ts +48 -0
- package/dist/lib/board-colors.js +93 -0
- package/dist/lib/board-colors.js.map +1 -0
- package/dist/lib/board-dimensions.d.ts +41 -0
- package/dist/lib/board-dimensions.js +38 -0
- package/dist/lib/board-dimensions.js.map +1 -0
- package/dist/lib/board-metrics.d.ts +19 -0
- package/dist/lib/board-metrics.js +22 -0
- package/dist/lib/board-metrics.js.map +1 -0
- package/dist/lib/seasons-drafts.d.ts +26 -0
- package/dist/lib/seasons-drafts.js +106 -0
- package/dist/lib/seasons-drafts.js.map +1 -0
- package/dist/lib/seasons.d.ts +52 -0
- package/dist/lib/seasons.js +59 -0
- package/dist/lib/seasons.js.map +1 -0
- package/dist/lib/select-items.d.ts +29 -0
- package/dist/lib/select-items.js +35 -0
- package/dist/lib/select-items.js.map +1 -0
- package/dist/lib/utils.d.ts +2 -0
- package/dist/lib/utils.js +11 -0
- package/dist/lib/utils.js.map +1 -0
- package/dist/seasons/christmas.css +64 -0
- package/dist/seasons/easter.css +62 -0
- package/dist/seasons/fathers-day.css +62 -0
- package/dist/seasons/halloween.css +69 -0
- package/dist/seasons/mothers-day.css +63 -0
- package/dist/seasons/new-year.css +66 -0
- package/dist/seasons/pride.css +68 -0
- package/dist/seasons/thanksgiving.css +64 -0
- package/dist/theme.css +1174 -0
- package/package.json +108 -0
package/dist/theme.css
ADDED
|
@@ -0,0 +1,1174 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* FiestaUI design tokens + base layer.
|
|
3
|
+
*
|
|
4
|
+
* This file is the design-system half of what used to be FiestaBoard's
|
|
5
|
+
* app/globals.css. It must be imported into a Tailwind v4 pipeline:
|
|
6
|
+
*
|
|
7
|
+
* @import "tailwindcss";
|
|
8
|
+
* @import "@fiestaboard/ui/fonts.css"; <- opt-in Geist @font-face registrations;
|
|
9
|
+
* @import "@fiestaboard/ui/theme.css"; skip fonts.css if you self-host Geist
|
|
10
|
+
* @source "../node_modules/@fiestaboard/ui/dist";
|
|
11
|
+
*
|
|
12
|
+
* Dark mode is class-based: toggle the `dark` class on <html>.
|
|
13
|
+
* Values are copied verbatim from FiestaBoard app/globals.css — visual
|
|
14
|
+
* parity with the app is the contract; do not reformat or "improve"
|
|
15
|
+
* token values here without a parity check on the consumer side.
|
|
16
|
+
*/
|
|
17
|
+
@import "tw-animate-css";
|
|
18
|
+
/* Seasonal themes — one file per season, inert unless the season class
|
|
19
|
+
is stamped on <html>. Only the LIVE season (pride) is inlined here so
|
|
20
|
+
consumers get it automatically; the design drafts are NOT imported —
|
|
21
|
+
they'd ship ~18 KB of rule families keyed on classes the app never
|
|
22
|
+
stamps. A consumer that promotes a draft opts in with:
|
|
23
|
+
@import "@fiestaboard/ui/seasons/<id>.css";
|
|
24
|
+
(see README "Seasonal theming"). Storybook imports every draft
|
|
25
|
+
directly in storybook.css so the Season toolbar can preview them. */
|
|
26
|
+
@import "./seasons/pride.css";
|
|
27
|
+
|
|
28
|
+
@custom-variant dark (&:is(.dark *));
|
|
29
|
+
|
|
30
|
+
@theme inline {
|
|
31
|
+
--color-background: var(--background);
|
|
32
|
+
--color-foreground: var(--foreground);
|
|
33
|
+
--font-sans: var(--font-geist-sans);
|
|
34
|
+
--font-mono: var(--font-geist-mono);
|
|
35
|
+
--color-sidebar-ring: var(--sidebar-ring);
|
|
36
|
+
--color-sidebar-border: var(--sidebar-border);
|
|
37
|
+
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
|
|
38
|
+
--color-sidebar-accent: var(--sidebar-accent);
|
|
39
|
+
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
|
|
40
|
+
--color-sidebar-primary: var(--sidebar-primary);
|
|
41
|
+
--color-sidebar-foreground: var(--sidebar-foreground);
|
|
42
|
+
--color-sidebar: var(--sidebar);
|
|
43
|
+
--color-chart-5: var(--chart-5);
|
|
44
|
+
--color-chart-4: var(--chart-4);
|
|
45
|
+
--color-chart-3: var(--chart-3);
|
|
46
|
+
--color-chart-2: var(--chart-2);
|
|
47
|
+
--color-chart-1: var(--chart-1);
|
|
48
|
+
--color-ring: var(--ring);
|
|
49
|
+
--color-input: var(--input);
|
|
50
|
+
--color-border: var(--border);
|
|
51
|
+
--color-destructive: var(--destructive);
|
|
52
|
+
--color-destructive-foreground: var(--destructive-foreground);
|
|
53
|
+
--color-accent-foreground: var(--accent-foreground);
|
|
54
|
+
--color-accent: var(--accent);
|
|
55
|
+
--color-muted-foreground: var(--muted-foreground);
|
|
56
|
+
--color-muted: var(--muted);
|
|
57
|
+
--color-secondary-foreground: var(--secondary-foreground);
|
|
58
|
+
--color-secondary: var(--secondary);
|
|
59
|
+
--color-primary-foreground: var(--primary-foreground);
|
|
60
|
+
--color-primary: var(--primary);
|
|
61
|
+
--color-popover-foreground: var(--popover-foreground);
|
|
62
|
+
--color-popover: var(--popover);
|
|
63
|
+
--color-card-foreground: var(--card-foreground);
|
|
64
|
+
--color-card: var(--card);
|
|
65
|
+
--radius-sm: calc(var(--radius) - 4px);
|
|
66
|
+
--radius-md: calc(var(--radius) - 2px);
|
|
67
|
+
--radius-lg: var(--radius);
|
|
68
|
+
--radius-xl: calc(var(--radius) + 4px);
|
|
69
|
+
--radius-2xl: calc(var(--radius) + 8px);
|
|
70
|
+
--radius-3xl: calc(var(--radius) + 12px);
|
|
71
|
+
--radius-4xl: calc(var(--radius) + 16px);
|
|
72
|
+
|
|
73
|
+
/* Status/intent semantic colors */
|
|
74
|
+
--color-info: var(--info);
|
|
75
|
+
--color-info-foreground: var(--info-foreground);
|
|
76
|
+
--color-success: var(--success);
|
|
77
|
+
--color-success-foreground: var(--success-foreground);
|
|
78
|
+
--color-warning: var(--warning);
|
|
79
|
+
--color-warning-foreground: var(--warning-foreground);
|
|
80
|
+
|
|
81
|
+
/* Tag/badge accent colors */
|
|
82
|
+
--color-tag-variable: var(--tag-variable);
|
|
83
|
+
--color-tag-variable-foreground: var(--tag-variable-foreground);
|
|
84
|
+
--color-tag-success: var(--tag-success);
|
|
85
|
+
--color-tag-success-foreground: var(--tag-success-foreground);
|
|
86
|
+
--color-tag-formula: var(--tag-formula);
|
|
87
|
+
--color-tag-formula-foreground: var(--tag-formula-foreground);
|
|
88
|
+
|
|
89
|
+
/* Overlay */
|
|
90
|
+
--color-overlay: var(--overlay);
|
|
91
|
+
|
|
92
|
+
/* Brand accent (sidebar active, links, key UI) - FiestaBoard orange from taco icon #f5a623 */
|
|
93
|
+
--color-brand: var(--brand);
|
|
94
|
+
--color-brand-foreground: var(--brand-foreground);
|
|
95
|
+
--color-brand-emphasis: var(--brand-emphasis);
|
|
96
|
+
|
|
97
|
+
/* Navigation active state (indigo) */
|
|
98
|
+
--color-nav-active: var(--nav-active);
|
|
99
|
+
--color-nav-active-foreground: var(--nav-active-foreground);
|
|
100
|
+
--color-nav-active-hover: var(--nav-active-hover);
|
|
101
|
+
|
|
102
|
+
/* Board Official Colors - use these for all status/tile colors */
|
|
103
|
+
--color-board-red: #eb4034;
|
|
104
|
+
--color-board-orange: #f5a623;
|
|
105
|
+
--color-board-yellow: #f8e71c;
|
|
106
|
+
--color-board-green: #7ed321;
|
|
107
|
+
--color-board-blue: #4a90d9;
|
|
108
|
+
--color-board-violet: #9b59b6;
|
|
109
|
+
--color-board-white: #ffffff;
|
|
110
|
+
--color-board-black: #1a1a1a;
|
|
111
|
+
|
|
112
|
+
/* Board hardware surface colors (independent of UI theme) */
|
|
113
|
+
--color-board-surface-light: #fafafa;
|
|
114
|
+
--color-board-surface-dark: #0d0d0d;
|
|
115
|
+
--color-board-text-on-light: #0d0d0d;
|
|
116
|
+
--color-board-text-on-dark: #f0f0e8;
|
|
117
|
+
--color-board-bezel-light: #f8f8f8;
|
|
118
|
+
--color-board-bezel-dark: #050505;
|
|
119
|
+
--color-board-bezel-border-light: #d8d8d8;
|
|
120
|
+
--color-board-bezel-border-dark: #1a1a1a;
|
|
121
|
+
|
|
122
|
+
/* Backward compatibility aliases */
|
|
123
|
+
--color-fiesta-red: var(--color-board-red);
|
|
124
|
+
--color-fiesta-orange: var(--color-board-orange);
|
|
125
|
+
--color-fiesta-yellow: var(--color-board-yellow);
|
|
126
|
+
--color-fiesta-green: var(--color-board-green);
|
|
127
|
+
--color-fiesta-blue: var(--color-board-blue);
|
|
128
|
+
--color-fiesta-violet: var(--color-board-violet);
|
|
129
|
+
--color-fiesta-white: var(--color-board-white);
|
|
130
|
+
--color-fiesta-black: var(--color-board-black);
|
|
131
|
+
|
|
132
|
+
/* Elevation shadows (theme-aware via CSS vars in :root / .dark) */
|
|
133
|
+
--shadow-card: var(--elevation-card);
|
|
134
|
+
--shadow-card-hover: var(--elevation-card-hover);
|
|
135
|
+
--shadow-modal: var(--elevation-modal);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
:root {
|
|
139
|
+
/* Font family tokens. The "Geist Variable" / "Geist Mono Variable"
|
|
140
|
+
names match the @font-face declarations registered by the opt-in
|
|
141
|
+
@fiestaboard/ui/fonts.css subpath (which wraps
|
|
142
|
+
@fontsource-variable/geist and @fontsource-variable/geist-mono).
|
|
143
|
+
Consumers that self-host Geist (next/font, CDN, subset) skip
|
|
144
|
+
fonts.css; without it these tokens degrade gracefully to the
|
|
145
|
+
system stacks below. Previously these CSS vars were set by
|
|
146
|
+
Next.js's next/font wrapper in layout.tsx — moved here so they're
|
|
147
|
+
owned by the stylesheet, not a build-time JS wrapper. */
|
|
148
|
+
--font-geist-sans: "Geist Variable", system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
|
149
|
+
--font-geist-mono: "Geist Mono Variable", ui-monospace, SFMono-Regular, "SF Mono", Monaco, Menlo, monospace;
|
|
150
|
+
--radius: 0.625rem;
|
|
151
|
+
--background: oklch(0.985 0 0);
|
|
152
|
+
--foreground: oklch(0.145 0 0);
|
|
153
|
+
--card: oklch(1 0 0);
|
|
154
|
+
--card-foreground: oklch(0.145 0 0);
|
|
155
|
+
--popover: oklch(1 0 0);
|
|
156
|
+
--popover-foreground: oklch(0.145 0 0);
|
|
157
|
+
--primary: oklch(0.52 0.16 60);
|
|
158
|
+
--primary-foreground: oklch(0.985 0 0);
|
|
159
|
+
--secondary: oklch(0.96 0 0);
|
|
160
|
+
--secondary-foreground: oklch(0.205 0 0);
|
|
161
|
+
--muted: oklch(0.955 0 0);
|
|
162
|
+
/* Muted text needs >=4.5:1 contrast vs --background to meet WCAG AA. */
|
|
163
|
+
--muted-foreground: oklch(0.4 0 0);
|
|
164
|
+
--accent: oklch(0.95 0 0);
|
|
165
|
+
--accent-foreground: oklch(0.205 0 0);
|
|
166
|
+
--destructive: oklch(0.43 0.245 27.325);
|
|
167
|
+
--destructive-foreground: oklch(0.985 0 0);
|
|
168
|
+
--border: oklch(0.905 0 0);
|
|
169
|
+
--input: oklch(0.885 0 0);
|
|
170
|
+
--ring: oklch(0.52 0.16 60);
|
|
171
|
+
--chart-1: oklch(0.646 0.222 41.116);
|
|
172
|
+
--chart-2: oklch(0.6 0.118 184.704);
|
|
173
|
+
--chart-3: oklch(0.398 0.07 227.392);
|
|
174
|
+
--chart-4: oklch(0.65 0.189 84.429);
|
|
175
|
+
--chart-5: oklch(0.65 0.188 70.08);
|
|
176
|
+
--sidebar: oklch(0.975 0 0);
|
|
177
|
+
--sidebar-foreground: oklch(0.145 0 0);
|
|
178
|
+
--sidebar-primary: oklch(0.52 0.16 60);
|
|
179
|
+
--sidebar-primary-foreground: oklch(0.985 0 0);
|
|
180
|
+
--sidebar-accent: oklch(0.92 0 0);
|
|
181
|
+
--sidebar-accent-foreground: oklch(0.205 0 0);
|
|
182
|
+
--sidebar-border: oklch(0.915 0 0);
|
|
183
|
+
--sidebar-ring: oklch(0.52 0.16 60);
|
|
184
|
+
--info: oklch(0.45 0.15 250);
|
|
185
|
+
--info-foreground: oklch(0.985 0 0);
|
|
186
|
+
--success: oklch(0.4 0.16 150);
|
|
187
|
+
--success-foreground: oklch(0.985 0 0);
|
|
188
|
+
--warning: oklch(0.45 0.16 70);
|
|
189
|
+
--warning-foreground: oklch(0.985 0 0);
|
|
190
|
+
--tag-variable: oklch(0.45 0.2 280);
|
|
191
|
+
--tag-variable-foreground: oklch(0.35 0.1 280);
|
|
192
|
+
--tag-success: oklch(0.45 0.2 160);
|
|
193
|
+
--tag-success-foreground: oklch(0.35 0.1 160);
|
|
194
|
+
--tag-formula: oklch(0.45 0.15 80);
|
|
195
|
+
--tag-formula-foreground: oklch(0.35 0.1 80);
|
|
196
|
+
--overlay: oklch(0 0 0 / 80%);
|
|
197
|
+
/* Brand accent - darkened for WCAG 2.2 AA (≥4.5:1 on light bg) */
|
|
198
|
+
--brand: oklch(0.56 0.15 75);
|
|
199
|
+
--brand-emphasis: oklch(0.52 0.16 60);
|
|
200
|
+
--brand-foreground: oklch(1 0 0);
|
|
201
|
+
/* Navigation active state — indigo palette */
|
|
202
|
+
--nav-active: oklch(0.44 0.19 265); /* indigo-600 */
|
|
203
|
+
--nav-active-foreground: oklch(1 0 0); /* white */
|
|
204
|
+
--nav-active-hover: oklch(0.5 0.17 265 / 15%); /* indigo tint hover */
|
|
205
|
+
/* Fiesta gradient palette — used by sidebar and page icon gradient */
|
|
206
|
+
--fiesta-red: #c97a72;
|
|
207
|
+
--fiesta-orange: #c99662;
|
|
208
|
+
--fiesta-purple: #9b7bb0;
|
|
209
|
+
/* Page icon gradient stops — 6 vars so pride month can expand to a full rainbow */
|
|
210
|
+
--icon-g1: var(--fiesta-red);
|
|
211
|
+
--icon-g2: var(--fiesta-orange);
|
|
212
|
+
--icon-g3: var(--fiesta-orange);
|
|
213
|
+
--icon-g4: var(--fiesta-orange);
|
|
214
|
+
--icon-g5: var(--fiesta-purple);
|
|
215
|
+
--icon-g6: var(--fiesta-purple);
|
|
216
|
+
/* Elevation - subtle card and modal shadows */
|
|
217
|
+
--elevation-card: 0 1px 2px 0 rgb(0 0 0 / 0.04), 0 2px 8px -2px rgb(0 0 0 / 0.06);
|
|
218
|
+
--elevation-card-hover: 0 2px 8px -2px rgb(0 0 0 / 0.08), 0 4px 16px -4px rgb(0 0 0 / 0.06);
|
|
219
|
+
--elevation-modal: 0 4px 12px -2px rgb(0 0 0 / 0.08), 0 4px 6px -4px rgb(0 0 0 / 0.04);
|
|
220
|
+
|
|
221
|
+
/* ============================================================
|
|
222
|
+
* Motion tokens (B4) — one duration scale + named easings.
|
|
223
|
+
* Values are lifted verbatim from the pre-token declarations;
|
|
224
|
+
* visual parity is the contract (see file header).
|
|
225
|
+
*
|
|
226
|
+
* Durations:
|
|
227
|
+
* fast 100ms micro fades (collapsed-sidebar labels; = Tailwind duration-100)
|
|
228
|
+
* control 150ms control feedback: button/switch/scrollbar (= Tailwind duration-150)
|
|
229
|
+
* base 200ms standard fades, accordion, dialog (= Tailwind duration-200)
|
|
230
|
+
* exit 250ms surface exit slides + companion fades
|
|
231
|
+
* slow 300ms surface & page entrances, sidebar collapse
|
|
232
|
+
* slower 350ms large-surface entrances (slide-up page, mobile nav/menu)
|
|
233
|
+
* slowest 400ms sheet/drawer slide-in, desktop sidebar entrance
|
|
234
|
+
*
|
|
235
|
+
* Easings:
|
|
236
|
+
* out ease-out standard decelerate (fades, entrances)
|
|
237
|
+
* spring cubic-bezier(0.16, 1, 0.3, 1) out-expo "spring" (modal/page/sheet entrances)
|
|
238
|
+
* in cubic-bezier(0.4, 0, 1, 1) accelerate (exits; = Tailwind ease-in)
|
|
239
|
+
* out-cubic cubic-bezier(0.33, 1, 0.68, 1) gentle decelerate (sidebar collapse/expand)
|
|
240
|
+
* standard cubic-bezier(0.25, 0.1, 0.25, 1) = CSS `ease` keyword (sheet slides)
|
|
241
|
+
*
|
|
242
|
+
* Deliberately left inline (true one-offs): 117s sidebar gradient
|
|
243
|
+
* flow, 220ms board-switch exits, 0.8s pride-burst, 1s card-fade-in,
|
|
244
|
+
* 50ms page-toolbar stagger delay.
|
|
245
|
+
* ============================================================ */
|
|
246
|
+
--motion-duration-fast: 100ms;
|
|
247
|
+
--motion-duration-control: 150ms;
|
|
248
|
+
--motion-duration-base: 200ms;
|
|
249
|
+
--motion-duration-exit: 250ms;
|
|
250
|
+
--motion-duration-slow: 300ms;
|
|
251
|
+
--motion-duration-slower: 350ms;
|
|
252
|
+
--motion-duration-slowest: 400ms;
|
|
253
|
+
--motion-ease-out: ease-out;
|
|
254
|
+
--motion-ease-spring: cubic-bezier(0.16, 1, 0.3, 1);
|
|
255
|
+
--motion-ease-in: cubic-bezier(0.4, 0, 1, 1);
|
|
256
|
+
--motion-ease-out-cubic: cubic-bezier(0.33, 1, 0.68, 1);
|
|
257
|
+
--motion-ease-standard: cubic-bezier(0.25, 0.1, 0.25, 1);
|
|
258
|
+
|
|
259
|
+
/* ============================================================
|
|
260
|
+
* Radius role scale (B3) — which UI role gets which corner.
|
|
261
|
+
* Roles are derived from what shipped components already use;
|
|
262
|
+
* every value equals the exact pre-token computed value (visual
|
|
263
|
+
* parity is the contract — see file header).
|
|
264
|
+
*
|
|
265
|
+
* Role | token | = class | value | used by
|
|
266
|
+
* control-inset | --radius-control-inset | rounded-sm | 6px | items nested inside a rounded control/popover: dropdown & select items, tabs trigger, dialog close
|
|
267
|
+
* control | --radius-control | rounded-md | 8px | buttons, input, textarea, select trigger, tabs list, skeleton, skip-link, and popover surfaces (dropdown/select/tooltip popups)
|
|
268
|
+
* surface | --radius-surface | rounded-lg | 10px | modal surfaces (dialog, alert-dialog), alert, sidebar nav pills
|
|
269
|
+
* card | --radius-card | rounded-xl | 12px | card, page-header icon tile
|
|
270
|
+
* pill | --radius-pill | rounded-full | ∞ | badge, switch, slider, sheet close, scroll-area thumb, sidebar edge toggle, empty-state icon
|
|
271
|
+
* chrome | --radius-chrome | (css/inline) | 14px | desktop sidebar shell + its aurora inset
|
|
272
|
+
* chrome-mobile | --radius-chrome-mobile | (css/inline) | 16px | mobile header/menu shell, its aurora inset, menu clip-path reveal
|
|
273
|
+
*
|
|
274
|
+
* Components KEEP their Tailwind rounded-* classes — those already
|
|
275
|
+
* resolve through the --radius calc chain, so each role token below
|
|
276
|
+
* is defined with the same math and stays in lockstep with them.
|
|
277
|
+
* New components pick a ROLE from this table instead of a raw size.
|
|
278
|
+
* The chrome radii are deliberately fixed px (not on the rem-based
|
|
279
|
+
* --radius chain): they match the aurora canvas insets exactly.
|
|
280
|
+
*
|
|
281
|
+
* Deliberately off-scale (left as-is): BoardIcon bare `rounded`
|
|
282
|
+
* (4px micro tile), pride-burst particle 2px, scroll-area viewport
|
|
283
|
+
* rounded-[inherit].
|
|
284
|
+
* ============================================================ */
|
|
285
|
+
--radius-control-inset: calc(var(--radius) - 4px);
|
|
286
|
+
--radius-control: calc(var(--radius) - 2px);
|
|
287
|
+
--radius-surface: var(--radius);
|
|
288
|
+
--radius-card: calc(var(--radius) + 4px);
|
|
289
|
+
--radius-pill: calc(infinity * 1px);
|
|
290
|
+
--radius-chrome: 14px;
|
|
291
|
+
--radius-chrome-mobile: 16px;
|
|
292
|
+
|
|
293
|
+
/* ============================================================
|
|
294
|
+
* Z-index layer scale (B3) — the app's stacking story, bottom to
|
|
295
|
+
* top. Values are exactly the pre-token numbers (parity contract).
|
|
296
|
+
*
|
|
297
|
+
* --z-popover 50 dropdown-menu popovers (see gotcha below)
|
|
298
|
+
* --z-sidebar 50 desktop sidebar rail
|
|
299
|
+
* --z-sidebar-toggle 51 edge collapse button — LOCAL value: the
|
|
300
|
+
* fixed z-50 aside is its own stacking
|
|
301
|
+
* context, so 51 only beats the aurora
|
|
302
|
+
* canvas / z-[1] content column inside it
|
|
303
|
+
* --z-mobile-backdrop 90 dimming layer behind the mobile menu
|
|
304
|
+
* --z-mobile-menu 95 mobile slide-down menu panel
|
|
305
|
+
* --z-mobile-header 100 mobile header — beats the open menu (95)
|
|
306
|
+
* and backdrop (90) so the hamburger/close
|
|
307
|
+
* button stays clickable
|
|
308
|
+
* --z-sheet 110 sheet/drawer + its backdrop — covers all
|
|
309
|
+
* chrome including the mobile header
|
|
310
|
+
* --z-select 120 Select popup — MUST stay above
|
|
311
|
+
* --z-mobile-header: the header hosts the
|
|
312
|
+
* mobile board selector, and its portalled
|
|
313
|
+
* popup wins by VALUE (Base UI portals into
|
|
314
|
+
* <body>; all these layers are fixed in the
|
|
315
|
+
* root stacking context)
|
|
316
|
+
* --z-modal 130 dialog + alert-dialog (and backdrops) —
|
|
317
|
+
* above sheets, so dialogs can launch from
|
|
318
|
+
* them
|
|
319
|
+
* --z-tooltip 140 tooltip — topmost overlay, readable over
|
|
320
|
+
* modals
|
|
321
|
+
* --z-skip-link 200 skip-to-content link when focused
|
|
322
|
+
* --z-confetti 9999 season burst particles — above everything
|
|
323
|
+
*
|
|
324
|
+
* Popover gotcha: --z-popover (50) ties with the desktop sidebar.
|
|
325
|
+
* Dropdowns still stack above it because Base UI portals append to
|
|
326
|
+
* <body> AFTER the sidebar — DOM order breaks the tie. A dropdown
|
|
327
|
+
* opened FROM the mobile header (100) would lose; nothing does that
|
|
328
|
+
* today (the header's board selector is a Select at 120). Don't put
|
|
329
|
+
* DropdownMenus in mobile chrome without revisiting this.
|
|
330
|
+
*
|
|
331
|
+
* Not on the scale (local stacking / one-offs): select scroll
|
|
332
|
+
* buttons z-10 (sticky inside the popup), sidebar/header content
|
|
333
|
+
* z-[1] (above the aurora canvas), board-switch view-transition
|
|
334
|
+
* groups 0/1 (::view-transition pseudo-tree only).
|
|
335
|
+
* ============================================================ */
|
|
336
|
+
--z-popover: 50;
|
|
337
|
+
--z-sidebar: 50;
|
|
338
|
+
--z-sidebar-toggle: 51;
|
|
339
|
+
--z-mobile-backdrop: 90;
|
|
340
|
+
--z-mobile-menu: 95;
|
|
341
|
+
--z-mobile-header: 100;
|
|
342
|
+
--z-sheet: 110;
|
|
343
|
+
--z-select: 120;
|
|
344
|
+
--z-modal: 130;
|
|
345
|
+
--z-tooltip: 140;
|
|
346
|
+
--z-skip-link: 200;
|
|
347
|
+
--z-confetti: 9999;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
.dark {
|
|
351
|
+
--background: oklch(0.13 0 0);
|
|
352
|
+
--foreground: oklch(0.985 0 0);
|
|
353
|
+
--card: oklch(0.16 0 0);
|
|
354
|
+
--card-foreground: oklch(0.985 0 0);
|
|
355
|
+
--popover: oklch(0.16 0 0);
|
|
356
|
+
--popover-foreground: oklch(0.985 0 0);
|
|
357
|
+
--primary: oklch(0.922 0 0);
|
|
358
|
+
--primary-foreground: oklch(0.205 0 0);
|
|
359
|
+
--secondary: oklch(0.215 0 0);
|
|
360
|
+
--secondary-foreground: oklch(0.985 0 0);
|
|
361
|
+
--muted: oklch(0.2 0 0);
|
|
362
|
+
--muted-foreground: oklch(0.83 0 0);
|
|
363
|
+
--accent: oklch(0.19 0 0);
|
|
364
|
+
--accent-foreground: oklch(0.985 0 0);
|
|
365
|
+
--destructive: oklch(0.8 0.191 22.216);
|
|
366
|
+
--destructive-foreground: oklch(0.15 0 0);
|
|
367
|
+
--border: oklch(1 0 0 / 10%);
|
|
368
|
+
--input: oklch(1 0 0 / 14%);
|
|
369
|
+
--ring: oklch(0.55 0 0);
|
|
370
|
+
--chart-1: oklch(0.55 0.243 264.376);
|
|
371
|
+
--chart-2: oklch(0.696 0.17 162.48);
|
|
372
|
+
--chart-3: oklch(0.769 0.188 70.08);
|
|
373
|
+
--chart-4: oklch(0.627 0.265 303.9);
|
|
374
|
+
--chart-5: oklch(0.645 0.246 16.439);
|
|
375
|
+
--sidebar: oklch(0.145 0 0);
|
|
376
|
+
--sidebar-foreground: oklch(0.985 0 0);
|
|
377
|
+
--sidebar-primary: oklch(0.488 0.243 264.376);
|
|
378
|
+
--sidebar-primary-foreground: oklch(0.985 0 0);
|
|
379
|
+
--sidebar-accent: oklch(0.28 0 0);
|
|
380
|
+
--sidebar-accent-foreground: oklch(0.985 0 0);
|
|
381
|
+
--sidebar-border: oklch(1 0 0 / 8%);
|
|
382
|
+
--sidebar-ring: oklch(0.55 0 0);
|
|
383
|
+
--info: oklch(0.75 0.18 250);
|
|
384
|
+
--info-foreground: oklch(0.15 0 0);
|
|
385
|
+
--success: oklch(0.68 0.18 145);
|
|
386
|
+
--success-foreground: oklch(0.15 0 0);
|
|
387
|
+
--warning: oklch(0.78 0.14 75);
|
|
388
|
+
--warning-foreground: oklch(0.15 0 0);
|
|
389
|
+
--tag-variable: oklch(0.65 0.15 280);
|
|
390
|
+
--tag-variable-foreground: oklch(0.85 0.08 280);
|
|
391
|
+
--tag-success: oklch(0.65 0.15 160);
|
|
392
|
+
--tag-success-foreground: oklch(0.85 0.08 160);
|
|
393
|
+
--tag-formula: oklch(0.65 0.15 80);
|
|
394
|
+
--tag-formula-foreground: oklch(0.85 0.08 80);
|
|
395
|
+
--overlay: oklch(0 0 0 / 80%);
|
|
396
|
+
--brand: oklch(0.78 0.14 75);
|
|
397
|
+
--brand-emphasis: oklch(0.55 0.16 60);
|
|
398
|
+
--brand-foreground: oklch(1 0 0);
|
|
399
|
+
/* Navigation active state — indigo palette (lighter for dark mode) */
|
|
400
|
+
--nav-active: oklch(0.59 0.19 265); /* indigo-500 */
|
|
401
|
+
--nav-active-foreground: oklch(1 0 0); /* white */
|
|
402
|
+
--nav-active-hover: oklch(0.59 0.19 265 / 20%); /* indigo tint hover */
|
|
403
|
+
/* Fiesta gradient palette — lighter for visibility on dark card backgrounds */
|
|
404
|
+
--fiesta-red: #e08a82;
|
|
405
|
+
--fiesta-orange: #d4a870;
|
|
406
|
+
--fiesta-purple: #b08ec8;
|
|
407
|
+
--elevation-card: 0 1px 3px 0 rgb(0 0 0 / 0.2), 0 0 0 1px oklch(1 0 0 / 6%);
|
|
408
|
+
--elevation-card-hover: 0 2px 8px -2px rgb(0 0 0 / 0.3), 0 0 0 1px oklch(1 0 0 / 10%);
|
|
409
|
+
--elevation-modal: 0 8px 24px -4px rgb(0 0 0 / 0.25), 0 4px 8px -4px rgb(0 0 0 / 0.15);
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
@layer base {
|
|
413
|
+
* {
|
|
414
|
+
@apply border-border outline-ring;
|
|
415
|
+
}
|
|
416
|
+
html {
|
|
417
|
+
/* Reserve space for scrollbar to prevent layout shift */
|
|
418
|
+
scrollbar-gutter: stable;
|
|
419
|
+
}
|
|
420
|
+
body {
|
|
421
|
+
@apply bg-background text-foreground;
|
|
422
|
+
font-feature-settings: "ss01";
|
|
423
|
+
/* Clip horizontal overflow at the document root so the body itself stays the
|
|
424
|
+
* vertical scroll container. Applying overflow-x to <main> would force its
|
|
425
|
+
* computed overflow-y to `auto`, recreating a nested scrollbar. */
|
|
426
|
+
overflow-x: hidden;
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
/* Accordion animation classes.
|
|
431
|
+
*
|
|
432
|
+
* These resolve to tw-animate-css's `accordion-down`/`accordion-up`
|
|
433
|
+
* keyframes (registered via its @theme import at the top of this file),
|
|
434
|
+
* which animate `height: 0 <-> var(--radix-accordion-content-height,
|
|
435
|
+
* ... auto)`. That is the ONLY version of these keyframes that has ever
|
|
436
|
+
* shipped: a same-named grid-template-rows pair that used to live here was
|
|
437
|
+
* silently discarded by the Tailwind build in favor of tw-animate's
|
|
438
|
+
* (issue #71). Restoring the grid technique means renaming the keyframes
|
|
439
|
+
* (fui- prefix) and re-wiring these classes — a behavior change that needs
|
|
440
|
+
* visual review, tracked separately from the dead-CSS cleanup.
|
|
441
|
+
*
|
|
442
|
+
* `display: grid` is kept deliberately: it shipped with these classes all
|
|
443
|
+
* along, and removing it is a rendered-layout change (grid vs block
|
|
444
|
+
* formatting context) outside the pixel-identical scope of the cleanup.
|
|
445
|
+
*/
|
|
446
|
+
.animate-accordion-down {
|
|
447
|
+
display: grid;
|
|
448
|
+
animation: accordion-down var(--motion-duration-base) var(--motion-ease-out);
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
.animate-accordion-up {
|
|
452
|
+
display: grid;
|
|
453
|
+
animation: accordion-up var(--motion-duration-base) var(--motion-ease-out);
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
/* ===== 60FPS SHEET/DRAWER ANIMATIONS ===== */
|
|
457
|
+
/* Direct keyframe animations for smooth sheet transitions */
|
|
458
|
+
|
|
459
|
+
@keyframes sheet-overlay-in {
|
|
460
|
+
from {
|
|
461
|
+
opacity: 0;
|
|
462
|
+
}
|
|
463
|
+
to {
|
|
464
|
+
opacity: 1;
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
@keyframes sheet-slide-in-right {
|
|
469
|
+
from {
|
|
470
|
+
transform: translate3d(100%, 0, 0);
|
|
471
|
+
}
|
|
472
|
+
to {
|
|
473
|
+
transform: translate3d(0, 0, 0);
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
@keyframes sheet-slide-in-left {
|
|
478
|
+
from {
|
|
479
|
+
transform: translate3d(-100%, 0, 0);
|
|
480
|
+
}
|
|
481
|
+
to {
|
|
482
|
+
transform: translate3d(0, 0, 0);
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
@keyframes sheet-slide-in-top {
|
|
487
|
+
from {
|
|
488
|
+
transform: translate3d(0, -100%, 0);
|
|
489
|
+
}
|
|
490
|
+
to {
|
|
491
|
+
transform: translate3d(0, 0, 0);
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
@keyframes sheet-slide-in-bottom {
|
|
496
|
+
from {
|
|
497
|
+
transform: translate3d(0, 100%, 0);
|
|
498
|
+
}
|
|
499
|
+
to {
|
|
500
|
+
transform: translate3d(0, 0, 0);
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
/* Prevent iOS Safari from zooming on input focus */
|
|
505
|
+
/* iOS Safari zooms when input font-size is < 16px */
|
|
506
|
+
@supports (-webkit-touch-callout: none) {
|
|
507
|
+
input,
|
|
508
|
+
textarea,
|
|
509
|
+
select {
|
|
510
|
+
font-size: 16px !important;
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
/* ============================================================
|
|
515
|
+
* App chrome — sidebar, navigation, page transitions, festive.
|
|
516
|
+
* Extracted verbatim from FiestaBoard app/globals.css (phase 2).
|
|
517
|
+
* Consumed by the chrome components (Sidebar, PageHeader, …).
|
|
518
|
+
* ============================================================ */
|
|
519
|
+
|
|
520
|
+
/* Pride styling lives in seasons/pride.css alongside the draft seasons. */
|
|
521
|
+
|
|
522
|
+
/* Season easter egg — confetti burst particles spawned by JS on logo click
|
|
523
|
+
(class name kept as pride-burst-* for app compatibility; every season's
|
|
524
|
+
fireSeasonBurst() uses it) */
|
|
525
|
+
@keyframes pride-burst {
|
|
526
|
+
0% {
|
|
527
|
+
transform: translate(0, 0) rotate(0deg) scale(1);
|
|
528
|
+
opacity: 1;
|
|
529
|
+
}
|
|
530
|
+
100% {
|
|
531
|
+
transform: translate(var(--tx), var(--ty)) rotate(var(--rot)) scale(0.2);
|
|
532
|
+
opacity: 0;
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
/* Wrapper so the 48 particles append to the live tree in a single mutation
|
|
536
|
+
and share one animationend listener. Fixed (out of flow) so the empty
|
|
537
|
+
container never affects body layout; no transform/filter/contain here, so
|
|
538
|
+
the fixed particles still resolve against the viewport. */
|
|
539
|
+
.pride-burst {
|
|
540
|
+
position: fixed;
|
|
541
|
+
top: 0;
|
|
542
|
+
left: 0;
|
|
543
|
+
/* The fixed container forms a stacking context, trapping the particles'
|
|
544
|
+
z-index inside it — so the container itself must sit at confetti level
|
|
545
|
+
or the burst paints under sheets/modals/tooltips. */
|
|
546
|
+
z-index: var(--z-confetti);
|
|
547
|
+
pointer-events: none;
|
|
548
|
+
}
|
|
549
|
+
.pride-burst-particle {
|
|
550
|
+
position: fixed;
|
|
551
|
+
width: 7px;
|
|
552
|
+
height: 7px;
|
|
553
|
+
border-radius: 2px;
|
|
554
|
+
pointer-events: none;
|
|
555
|
+
z-index: var(--z-confetti);
|
|
556
|
+
/* 0.8s default is a one-off (JS overrides via --dur), so it stays inline */
|
|
557
|
+
animation: pride-burst var(--dur, 0.8s) var(--motion-ease-out) forwards;
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
/* fireSeasonBurst() already skips the burst under reduced motion, but mirror
|
|
561
|
+
every other motion surface in this file with a CSS gate as a backstop. */
|
|
562
|
+
@media (prefers-reduced-motion: reduce) {
|
|
563
|
+
.pride-burst-particle {
|
|
564
|
+
animation: none !important;
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
/* Board switch transition — the app-wide board selector wraps its state
|
|
569
|
+
update in document.startViewTransition() (see current-board-context.tsx),
|
|
570
|
+
stamping html[data-board-switch="forward"|"backward"]. Only the page
|
|
571
|
+
content participates (view-transition-name on <main>): the old page slides
|
|
572
|
+
out one way while the new page slides in from the other, so changing boards
|
|
573
|
+
reads as a SWITCH instead of the page re-entering from the bottom. */
|
|
574
|
+
#main-content {
|
|
575
|
+
view-transition-name: board-page;
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
/* The sidebar and mobile header get their own (static) snapshot groups so
|
|
579
|
+
they can be stacked ABOVE the sliding page — the page slides under the
|
|
580
|
+
app chrome, never over it. */
|
|
581
|
+
aside.sidebar-gradient {
|
|
582
|
+
view-transition-name: app-sidebar;
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
header.sidebar-gradient-horizontal {
|
|
586
|
+
view-transition-name: app-mobile-header;
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
html[data-board-switch]::view-transition-group(board-page) {
|
|
590
|
+
z-index: 0;
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
html[data-board-switch]::view-transition-group(app-sidebar),
|
|
594
|
+
html[data-board-switch]::view-transition-group(app-mobile-header) {
|
|
595
|
+
z-index: 1;
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
@keyframes board-switch-out-left {
|
|
599
|
+
to {
|
|
600
|
+
opacity: 0;
|
|
601
|
+
transform: translateX(-20%);
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
@keyframes board-switch-in-right {
|
|
606
|
+
from {
|
|
607
|
+
opacity: 0;
|
|
608
|
+
transform: translateX(20%);
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
@keyframes board-switch-out-right {
|
|
613
|
+
to {
|
|
614
|
+
opacity: 0;
|
|
615
|
+
transform: translateX(20%);
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
@keyframes board-switch-in-left {
|
|
620
|
+
from {
|
|
621
|
+
opacity: 0;
|
|
622
|
+
transform: translateX(-20%);
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
/* 220ms outgoing-page exit is a deliberate one-off (slightly quicker than the
|
|
627
|
+
250ms exit tier so the incoming page reads as leading the switch) */
|
|
628
|
+
html[data-board-switch="forward"]::view-transition-old(board-page) {
|
|
629
|
+
animation: board-switch-out-left 220ms var(--motion-ease-in) both;
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
html[data-board-switch="forward"]::view-transition-new(board-page) {
|
|
633
|
+
animation: board-switch-in-right var(--motion-duration-slow) var(--motion-ease-spring) both;
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
html[data-board-switch="backward"]::view-transition-old(board-page) {
|
|
637
|
+
animation: board-switch-out-right 220ms var(--motion-ease-in) both;
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
html[data-board-switch="backward"]::view-transition-new(board-page) {
|
|
641
|
+
animation: board-switch-in-left var(--motion-duration-slow) var(--motion-ease-spring) both;
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
@media (prefers-reduced-motion: reduce) {
|
|
645
|
+
html[data-board-switch]::view-transition-old(board-page),
|
|
646
|
+
html[data-board-switch]::view-transition-new(board-page) {
|
|
647
|
+
animation: none !important;
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
/* Page Transitions - CSS-based (works in all browsers) */
|
|
652
|
+
@layer base {
|
|
653
|
+
/* Animate page content on route changes */
|
|
654
|
+
main {
|
|
655
|
+
animation: page-enter var(--motion-duration-slow) var(--motion-ease-out);
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
/* Slide-up animation for modal-like pages (edit/new) */
|
|
659
|
+
html[data-transition="slide-up"] main {
|
|
660
|
+
animation: slide-up-enter var(--motion-duration-slower) var(--motion-ease-spring);
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
/* Slide-down animation for returning from modals */
|
|
664
|
+
html[data-transition="slide-down"] main {
|
|
665
|
+
animation: slide-down-enter var(--motion-duration-slow) var(--motion-ease-spring);
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
/* Scale-fade for content areas */
|
|
669
|
+
html[data-transition="scale-fade"] main {
|
|
670
|
+
animation: scale-fade-enter var(--motion-duration-slow) var(--motion-ease-spring);
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
/* Respect user's motion preferences */
|
|
674
|
+
@media (prefers-reduced-motion: reduce) {
|
|
675
|
+
main {
|
|
676
|
+
animation: none !important;
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
.reduce-motion main {
|
|
680
|
+
animation: none !important;
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
/* Page Transition Keyframes */
|
|
685
|
+
@keyframes page-enter {
|
|
686
|
+
from {
|
|
687
|
+
opacity: 0;
|
|
688
|
+
transform: translateY(8px);
|
|
689
|
+
}
|
|
690
|
+
to {
|
|
691
|
+
opacity: 1;
|
|
692
|
+
transform: translateY(0);
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
@keyframes slide-up-enter {
|
|
697
|
+
from {
|
|
698
|
+
opacity: 0;
|
|
699
|
+
transform: translateY(20px) scale(0.98);
|
|
700
|
+
}
|
|
701
|
+
to {
|
|
702
|
+
opacity: 1;
|
|
703
|
+
transform: translateY(0) scale(1);
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
@keyframes slide-down-enter {
|
|
708
|
+
from {
|
|
709
|
+
opacity: 0;
|
|
710
|
+
transform: translateY(-12px);
|
|
711
|
+
}
|
|
712
|
+
to {
|
|
713
|
+
opacity: 1;
|
|
714
|
+
transform: translateY(0);
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
@keyframes scale-fade-enter {
|
|
719
|
+
from {
|
|
720
|
+
opacity: 0;
|
|
721
|
+
transform: scale(0.96);
|
|
722
|
+
}
|
|
723
|
+
to {
|
|
724
|
+
opacity: 1;
|
|
725
|
+
transform: scale(1);
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
/* Sidebar & mobile header: Fiesta-brand gradient (red, orange, purple).
|
|
730
|
+
Repeating gradient for seamless loop (no flash).
|
|
731
|
+
Static when prefers-reduced-motion. */
|
|
732
|
+
@keyframes sidebar-gradient-flow {
|
|
733
|
+
/* Runs on .sidebar-gradient-layer::before (see below): translate exactly one
|
|
734
|
+
repeat period (300% of the container = 75% of the 400% layer) for a
|
|
735
|
+
seamless loop — no flash at end. transform-only so the loop stays on the
|
|
736
|
+
compositor with zero per-frame repaint (issue #57). */
|
|
737
|
+
0% {
|
|
738
|
+
transform: translateY(0);
|
|
739
|
+
}
|
|
740
|
+
100% {
|
|
741
|
+
transform: translateY(-75%);
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
/* Logo in light mode: deep warm brown Fiesta + black Board for contrast across gradient */
|
|
746
|
+
.sidebar-gradient:not(.dark) .logo-on-gradient,
|
|
747
|
+
.sidebar-gradient-horizontal:not(.dark) .logo-on-gradient {
|
|
748
|
+
--brand: oklch(0.28 0.08 50);
|
|
749
|
+
--sidebar-foreground: oklch(0 0 0);
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
/* Logo in dark mode: white Board text, brighter brand for contrast on dark gradient */
|
|
753
|
+
.dark .sidebar-gradient .logo-on-gradient,
|
|
754
|
+
.dark .sidebar-gradient-horizontal .logo-on-gradient {
|
|
755
|
+
--brand: oklch(0.78 0.14 75);
|
|
756
|
+
--sidebar-foreground: oklch(1 0 0);
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
/* Light mode: soft gradient (coral → peach → lavender).
|
|
760
|
+
Stops derive from the canonical --fiesta-* brand gradient tokens —
|
|
761
|
+
never fork the hexes here; seasons retint via --sidebar-fiesta-*. */
|
|
762
|
+
.sidebar-gradient {
|
|
763
|
+
--sidebar-fiesta-red: var(--fiesta-red);
|
|
764
|
+
--sidebar-fiesta-orange: var(--fiesta-orange);
|
|
765
|
+
--sidebar-fiesta-purple: var(--fiesta-purple);
|
|
766
|
+
background: repeating-linear-gradient(
|
|
767
|
+
170deg,
|
|
768
|
+
var(--sidebar-fiesta-red) 0%,
|
|
769
|
+
var(--sidebar-fiesta-orange) 33.33%,
|
|
770
|
+
var(--sidebar-fiesta-purple) 66.66%,
|
|
771
|
+
var(--sidebar-fiesta-red) 100%
|
|
772
|
+
);
|
|
773
|
+
background-size: 100% 300%;
|
|
774
|
+
background-position: 50% 0%;
|
|
775
|
+
background-repeat: repeat-y;
|
|
776
|
+
color: oklch(0 0 0);
|
|
777
|
+
border-radius: var(--radius-chrome);
|
|
778
|
+
--sidebar-foreground: oklch(0 0 0);
|
|
779
|
+
--sidebar-accent: oklch(1 0 0 / 35%);
|
|
780
|
+
--sidebar-accent-foreground: oklch(0 0 0);
|
|
781
|
+
--sidebar-border: oklch(0 0 0 / 12%);
|
|
782
|
+
--foreground: oklch(0 0 0);
|
|
783
|
+
--muted-foreground: oklch(0.15 0 0);
|
|
784
|
+
--accent: oklch(1 0 0 / 28%);
|
|
785
|
+
--accent-foreground: oklch(0 0 0);
|
|
786
|
+
--border: oklch(0 0 0 / 12%);
|
|
787
|
+
}
|
|
788
|
+
/* Dark mode: rich Fiesta gradient — deep-toned stops defined ONCE for both
|
|
789
|
+
the vertical sidebar and the mobile horizontal header. */
|
|
790
|
+
.dark .sidebar-gradient,
|
|
791
|
+
.dark .sidebar-gradient-horizontal {
|
|
792
|
+
--sidebar-fiesta-red: #7a2a24;
|
|
793
|
+
--sidebar-fiesta-orange: #6d5014;
|
|
794
|
+
--sidebar-fiesta-purple: #4e3070; /* board violet, darker for dark theme */
|
|
795
|
+
}
|
|
796
|
+
.dark .sidebar-gradient {
|
|
797
|
+
color: oklch(1 0 0);
|
|
798
|
+
--sidebar-foreground: oklch(1 0 0);
|
|
799
|
+
--sidebar-accent: oklch(1 0 0 / 24%);
|
|
800
|
+
--sidebar-accent-foreground: oklch(1 0 0);
|
|
801
|
+
--sidebar-border: oklch(1 0 0 / 18%);
|
|
802
|
+
--foreground: oklch(1 0 0);
|
|
803
|
+
--muted-foreground: oklch(1 0 0 / 85%);
|
|
804
|
+
--accent: oklch(1 0 0 / 20%);
|
|
805
|
+
--accent-foreground: oklch(1 0 0);
|
|
806
|
+
--border: oklch(1 0 0 / 18%);
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
.sidebar-gradient-horizontal {
|
|
810
|
+
/* Light mode: identical stops to the vertical sidebar — one brand gradient
|
|
811
|
+
on every viewport (previously the mobile header ran subtly warmer). */
|
|
812
|
+
--sidebar-fiesta-red: var(--fiesta-red);
|
|
813
|
+
--sidebar-fiesta-orange: var(--fiesta-orange);
|
|
814
|
+
--sidebar-fiesta-purple: var(--fiesta-purple);
|
|
815
|
+
background: repeating-linear-gradient(
|
|
816
|
+
135deg,
|
|
817
|
+
var(--sidebar-fiesta-red) 0,
|
|
818
|
+
var(--sidebar-fiesta-orange) 33.33%,
|
|
819
|
+
var(--sidebar-fiesta-purple) 66.66%,
|
|
820
|
+
var(--sidebar-fiesta-red) 100%
|
|
821
|
+
);
|
|
822
|
+
background-size: 400% 100%;
|
|
823
|
+
background-position: 0% 50%;
|
|
824
|
+
color: oklch(0 0 0);
|
|
825
|
+
border-radius: var(--radius-chrome-mobile);
|
|
826
|
+
--foreground: oklch(0 0 0);
|
|
827
|
+
--sidebar-accent: oklch(1 0 0 / 35%);
|
|
828
|
+
--sidebar-accent-foreground: oklch(0 0 0);
|
|
829
|
+
--muted-foreground: oklch(0.15 0 0);
|
|
830
|
+
--accent: oklch(1 0 0 / 28%);
|
|
831
|
+
--accent-foreground: oklch(0 0 0);
|
|
832
|
+
--border: oklch(0 0 0 / 12%);
|
|
833
|
+
}
|
|
834
|
+
@keyframes sidebar-gradient-flow-h {
|
|
835
|
+
/* Runs on .sidebar-gradient-layer::before: one period (33.33% of the 400%
|
|
836
|
+
background box = 100% of the container = 50% of the 200% layer) per loop —
|
|
837
|
+
same wrap as the old background-position scroll, compositor-only. */
|
|
838
|
+
0% {
|
|
839
|
+
transform: translateX(0);
|
|
840
|
+
}
|
|
841
|
+
100% {
|
|
842
|
+
transform: translateX(-50%);
|
|
843
|
+
}
|
|
844
|
+
}
|
|
845
|
+
.dark .sidebar-gradient-horizontal {
|
|
846
|
+
color: oklch(1 0 0);
|
|
847
|
+
--foreground: oklch(1 0 0);
|
|
848
|
+
--sidebar-accent: oklch(1 0 0 / 24%);
|
|
849
|
+
--sidebar-accent-foreground: oklch(1 0 0);
|
|
850
|
+
--muted-foreground: oklch(1 0 0 / 85%);
|
|
851
|
+
--accent: oklch(1 0 0 / 24%);
|
|
852
|
+
--accent-foreground: oklch(1 0 0);
|
|
853
|
+
--border: oklch(1 0 0 / 18%);
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
/* Compositor-friendly gradient flow (issue #57).
|
|
857
|
+
The .sidebar-gradient(-horizontal) containers above keep a STATIC copy of
|
|
858
|
+
the gradient as their background — the single surface seasons override
|
|
859
|
+
(light retint via --sidebar-fiesta-*, dark seasons flat-hex via
|
|
860
|
+
`background` + `animation: none`) and the graceful static fallback for
|
|
861
|
+
markup without the layer element. The moving copy lives on
|
|
862
|
+
.sidebar-gradient-layer::before: it re-inherits that same background
|
|
863
|
+
(container → layer → ::before), overscans it by exactly one repeat
|
|
864
|
+
period, and translates with a transform loop — so the flow runs on the
|
|
865
|
+
compositor with no per-frame rasterization, and the .nav-active
|
|
866
|
+
backdrop-filter pills get a cacheable backdrop (#93). Dark seasons stay
|
|
867
|
+
correct for free: the inherited background is their flat hex, so the
|
|
868
|
+
layer paints the same flat color. The layer clips itself to the rounded
|
|
869
|
+
corners because the desktop <aside> cannot take overflow:hidden (the
|
|
870
|
+
edge toggle button overhangs it). */
|
|
871
|
+
.sidebar-gradient,
|
|
872
|
+
.sidebar-gradient-horizontal {
|
|
873
|
+
/* Keep the z-index:-1 layer above this element's own background even when
|
|
874
|
+
the container isn't otherwise a stacking context (e.g. story wrappers). */
|
|
875
|
+
isolation: isolate;
|
|
876
|
+
}
|
|
877
|
+
.sidebar-gradient-layer {
|
|
878
|
+
position: absolute;
|
|
879
|
+
inset: 0;
|
|
880
|
+
z-index: -1; /* under the container's content, above its background */
|
|
881
|
+
overflow: hidden;
|
|
882
|
+
border-radius: inherit;
|
|
883
|
+
background: inherit; /* inheritance link: ::before re-inherits from here */
|
|
884
|
+
pointer-events: none;
|
|
885
|
+
}
|
|
886
|
+
.sidebar-gradient-layer::before {
|
|
887
|
+
content: "";
|
|
888
|
+
position: absolute;
|
|
889
|
+
top: 0;
|
|
890
|
+
left: 0;
|
|
891
|
+
background: inherit;
|
|
892
|
+
will-change: transform;
|
|
893
|
+
}
|
|
894
|
+
/* Vertical: the container's gradient box is 100% × 300%. The ::before
|
|
895
|
+
overscans to 400% height (100% visible + one 300% travel period), so
|
|
896
|
+
translateY(-75%) [= -300% of the container] wraps onto the repeat. */
|
|
897
|
+
.sidebar-gradient .sidebar-gradient-layer::before {
|
|
898
|
+
width: 100%;
|
|
899
|
+
height: 400%;
|
|
900
|
+
background-size: 100% 75%; /* same box as the container's 100% 300% */
|
|
901
|
+
background-position: 50% 0%; /* pinned to the container's t=0 anchor */
|
|
902
|
+
background-repeat: repeat-y;
|
|
903
|
+
animation: sidebar-gradient-flow 117s linear infinite;
|
|
904
|
+
}
|
|
905
|
+
/* Horizontal: the container's gradient box is 400% × 100%; one loop travels
|
|
906
|
+
33.33% background-position = 100% of the container, so the ::before
|
|
907
|
+
overscans to 200% width and translateX(-50%) reproduces one period. */
|
|
908
|
+
.sidebar-gradient-horizontal .sidebar-gradient-layer::before {
|
|
909
|
+
width: 200%;
|
|
910
|
+
height: 100%;
|
|
911
|
+
background-size: 200% 100%; /* same box as the container's 400% 100% */
|
|
912
|
+
background-position: 0% 50%;
|
|
913
|
+
background-repeat: repeat;
|
|
914
|
+
animation: sidebar-gradient-flow-h 117s linear 0.35s infinite;
|
|
915
|
+
}
|
|
916
|
+
|
|
917
|
+
/* Navigation entrance animations */
|
|
918
|
+
@keyframes nav-slide-in-left {
|
|
919
|
+
from {
|
|
920
|
+
opacity: 0;
|
|
921
|
+
transform: translateX(-8px);
|
|
922
|
+
}
|
|
923
|
+
to {
|
|
924
|
+
opacity: 1;
|
|
925
|
+
transform: translateX(0);
|
|
926
|
+
}
|
|
927
|
+
}
|
|
928
|
+
@keyframes nav-slide-in-top {
|
|
929
|
+
from {
|
|
930
|
+
opacity: 0;
|
|
931
|
+
transform: translateY(-6px);
|
|
932
|
+
}
|
|
933
|
+
to {
|
|
934
|
+
opacity: 1;
|
|
935
|
+
transform: translateY(0);
|
|
936
|
+
}
|
|
937
|
+
}
|
|
938
|
+
/* Desktop: entrance on the container; the gradient flow runs on
|
|
939
|
+
.sidebar-gradient-layer::before and starts after 400ms. */
|
|
940
|
+
/* 117s gradient loop is an ambient one-off; its 0.4s/0.35s start delays
|
|
941
|
+
mirror the entrance durations (slowest/slower) and stay literal with them */
|
|
942
|
+
@media (min-width: 1024px) {
|
|
943
|
+
.sidebar-gradient {
|
|
944
|
+
animation: nav-slide-in-left var(--motion-duration-slowest) var(--motion-ease-out) both;
|
|
945
|
+
}
|
|
946
|
+
.sidebar-gradient .sidebar-gradient-layer::before {
|
|
947
|
+
animation-delay: 0.4s;
|
|
948
|
+
}
|
|
949
|
+
}
|
|
950
|
+
.sidebar-gradient-horizontal {
|
|
951
|
+
animation: nav-slide-in-top var(--motion-duration-slower) var(--motion-ease-out) both;
|
|
952
|
+
}
|
|
953
|
+
|
|
954
|
+
/* Sidebar collapse/expand transition (#92).
|
|
955
|
+
Only the fixed sidebar transitions width — its layout scope is just the
|
|
956
|
+
sidebar itself (the contain below keeps that honest). The content root
|
|
957
|
+
(<main id="main-content">) must never transition layout properties: every
|
|
958
|
+
frame of a width/padding transition there relayouts the whole page
|
|
959
|
+
subtree. Instead, MainContent snaps its paddings instantly and slides an
|
|
960
|
+
inner .content-shift wrapper with a compositor-only transform (FLIP),
|
|
961
|
+
using the same 300ms easing so sidebar and content move in sync. */
|
|
962
|
+
.sidebar-transition {
|
|
963
|
+
transition: width var(--motion-duration-slow) var(--motion-ease-out-cubic);
|
|
964
|
+
contain: layout style;
|
|
965
|
+
}
|
|
966
|
+
/* Content root: keeps the containment it always had; paddings snap. */
|
|
967
|
+
.content-root {
|
|
968
|
+
contain: layout style;
|
|
969
|
+
}
|
|
970
|
+
/* Inner content wrapper: the only thing that moves during the slide.
|
|
971
|
+
will-change is scoped to the transition — transform is compositor-promoted,
|
|
972
|
+
so (unlike the old width/padding hint) the hint actually does something. */
|
|
973
|
+
.content-shift {
|
|
974
|
+
transition: transform var(--motion-duration-slow) var(--motion-ease-out-cubic);
|
|
975
|
+
}
|
|
976
|
+
.content-shift.is-transitioning {
|
|
977
|
+
will-change: transform;
|
|
978
|
+
}
|
|
979
|
+
@media (prefers-reduced-motion: reduce) {
|
|
980
|
+
.sidebar-transition,
|
|
981
|
+
.content-shift {
|
|
982
|
+
transition: none;
|
|
983
|
+
}
|
|
984
|
+
.sidebar-gradient {
|
|
985
|
+
animation: none;
|
|
986
|
+
background-position: 50% 25%;
|
|
987
|
+
}
|
|
988
|
+
.sidebar-gradient-horizontal {
|
|
989
|
+
animation: none;
|
|
990
|
+
background-position: 25% 50%;
|
|
991
|
+
}
|
|
992
|
+
/* Freeze the compositor layer at the same intended mid-gradient pose
|
|
993
|
+
(equivalent to background-position 50% 25% / 25% 50% on the container). */
|
|
994
|
+
.sidebar-gradient .sidebar-gradient-layer::before {
|
|
995
|
+
animation: none;
|
|
996
|
+
transform: translateY(-12.5%); /* -50% of the container = bg-position 50% 25% */
|
|
997
|
+
}
|
|
998
|
+
.sidebar-gradient-horizontal .sidebar-gradient-layer::before {
|
|
999
|
+
animation: none;
|
|
1000
|
+
transform: translateX(-37.5%); /* -75% of the container = bg-position 25% 50% */
|
|
1001
|
+
}
|
|
1002
|
+
}
|
|
1003
|
+
.reduce-motion .sidebar-transition,
|
|
1004
|
+
.reduce-motion .content-shift {
|
|
1005
|
+
transition: none;
|
|
1006
|
+
}
|
|
1007
|
+
.reduce-motion .sidebar-gradient {
|
|
1008
|
+
animation: none;
|
|
1009
|
+
background-position: 50% 25%;
|
|
1010
|
+
}
|
|
1011
|
+
.reduce-motion .sidebar-gradient-horizontal {
|
|
1012
|
+
animation: none;
|
|
1013
|
+
background-position: 25% 50%;
|
|
1014
|
+
}
|
|
1015
|
+
/* Same freeze pose as the prefers-reduced-motion block above. */
|
|
1016
|
+
.reduce-motion .sidebar-gradient .sidebar-gradient-layer::before {
|
|
1017
|
+
animation: none;
|
|
1018
|
+
transform: translateY(-12.5%);
|
|
1019
|
+
}
|
|
1020
|
+
.reduce-motion .sidebar-gradient-horizontal .sidebar-gradient-layer::before {
|
|
1021
|
+
animation: none;
|
|
1022
|
+
transform: translateX(-37.5%);
|
|
1023
|
+
}
|
|
1024
|
+
|
|
1025
|
+
/* Navigation active state — solid fallback (non-gradient surfaces, no backdrop support) */
|
|
1026
|
+
.nav-active {
|
|
1027
|
+
background: var(--nav-active);
|
|
1028
|
+
color: var(--nav-active-foreground);
|
|
1029
|
+
}
|
|
1030
|
+
.nav-active-hover:hover {
|
|
1031
|
+
background: var(--nav-active-hover);
|
|
1032
|
+
}
|
|
1033
|
+
|
|
1034
|
+
/*
|
|
1035
|
+
* Fiesta sidebar active pill: frosted glass over the animated gradient.
|
|
1036
|
+
* backdrop-filter samples the colors behind the row so the highlight moves with the gradient.
|
|
1037
|
+
* A minimum-opacity cool veil keeps white text ≥ WCAG 2.2 AA on the lightest gradient stops.
|
|
1038
|
+
*/
|
|
1039
|
+
.sidebar-gradient .nav-active,
|
|
1040
|
+
.sidebar-gradient-horizontal .nav-active {
|
|
1041
|
+
background: oklch(0.26 0.09 285 / 0.62);
|
|
1042
|
+
color: oklch(1 0 0);
|
|
1043
|
+
box-shadow: inset 0 0 0 1px oklch(1 0 0 / 0.18);
|
|
1044
|
+
backdrop-filter: blur(14px) saturate(1.35);
|
|
1045
|
+
-webkit-backdrop-filter: blur(14px) saturate(1.35);
|
|
1046
|
+
}
|
|
1047
|
+
|
|
1048
|
+
.dark .sidebar-gradient .nav-active,
|
|
1049
|
+
.dark .sidebar-gradient-horizontal .nav-active {
|
|
1050
|
+
background: oklch(0.22 0.08 285 / 0.48);
|
|
1051
|
+
box-shadow: inset 0 0 0 1px oklch(1 0 0 / 0.14);
|
|
1052
|
+
backdrop-filter: blur(16px) saturate(1.5) brightness(1.06);
|
|
1053
|
+
-webkit-backdrop-filter: blur(16px) saturate(1.5) brightness(1.06);
|
|
1054
|
+
}
|
|
1055
|
+
|
|
1056
|
+
.sidebar-gradient .nav-active-hover:hover,
|
|
1057
|
+
.sidebar-gradient-horizontal .nav-active-hover:hover {
|
|
1058
|
+
background: oklch(1 0 0 / 0.18);
|
|
1059
|
+
backdrop-filter: blur(10px) saturate(1.15);
|
|
1060
|
+
-webkit-backdrop-filter: blur(10px) saturate(1.15);
|
|
1061
|
+
}
|
|
1062
|
+
|
|
1063
|
+
.dark .sidebar-gradient .nav-active-hover:hover,
|
|
1064
|
+
.dark .sidebar-gradient-horizontal .nav-active-hover:hover {
|
|
1065
|
+
background: oklch(1 0 0 / 0.12);
|
|
1066
|
+
backdrop-filter: blur(12px) saturate(1.25);
|
|
1067
|
+
-webkit-backdrop-filter: blur(12px) saturate(1.25);
|
|
1068
|
+
}
|
|
1069
|
+
|
|
1070
|
+
@supports not ((-webkit-backdrop-filter: blur(1px)) or (backdrop-filter: blur(1px))) {
|
|
1071
|
+
.sidebar-gradient .nav-active,
|
|
1072
|
+
.sidebar-gradient-horizontal .nav-active {
|
|
1073
|
+
background: var(--nav-active);
|
|
1074
|
+
box-shadow: none;
|
|
1075
|
+
backdrop-filter: none;
|
|
1076
|
+
-webkit-backdrop-filter: none;
|
|
1077
|
+
}
|
|
1078
|
+
.sidebar-gradient .nav-active-hover:hover,
|
|
1079
|
+
.sidebar-gradient-horizontal .nav-active-hover:hover {
|
|
1080
|
+
background: var(--nav-active-hover);
|
|
1081
|
+
backdrop-filter: none;
|
|
1082
|
+
-webkit-backdrop-filter: none;
|
|
1083
|
+
}
|
|
1084
|
+
}
|
|
1085
|
+
|
|
1086
|
+
@media (prefers-reduced-transparency: reduce) {
|
|
1087
|
+
.sidebar-gradient .nav-active,
|
|
1088
|
+
.sidebar-gradient-horizontal .nav-active {
|
|
1089
|
+
background: var(--nav-active);
|
|
1090
|
+
box-shadow: none;
|
|
1091
|
+
backdrop-filter: none;
|
|
1092
|
+
-webkit-backdrop-filter: none;
|
|
1093
|
+
}
|
|
1094
|
+
.sidebar-gradient .nav-active-hover:hover,
|
|
1095
|
+
.sidebar-gradient-horizontal .nav-active-hover:hover {
|
|
1096
|
+
background: var(--nav-active-hover);
|
|
1097
|
+
backdrop-filter: none;
|
|
1098
|
+
-webkit-backdrop-filter: none;
|
|
1099
|
+
}
|
|
1100
|
+
}
|
|
1101
|
+
|
|
1102
|
+
@media (prefers-contrast: more) {
|
|
1103
|
+
.sidebar-gradient .nav-active,
|
|
1104
|
+
.sidebar-gradient-horizontal .nav-active {
|
|
1105
|
+
background: var(--nav-active);
|
|
1106
|
+
color: var(--nav-active-foreground);
|
|
1107
|
+
box-shadow: inset 0 0 0 2px oklch(1 0 0 / 0.35);
|
|
1108
|
+
backdrop-filter: none;
|
|
1109
|
+
-webkit-backdrop-filter: none;
|
|
1110
|
+
}
|
|
1111
|
+
.sidebar-gradient .nav-active-hover:hover,
|
|
1112
|
+
.sidebar-gradient-horizontal .nav-active-hover:hover {
|
|
1113
|
+
background: var(--nav-active-hover);
|
|
1114
|
+
backdrop-filter: none;
|
|
1115
|
+
-webkit-backdrop-filter: none;
|
|
1116
|
+
}
|
|
1117
|
+
}
|
|
1118
|
+
|
|
1119
|
+
/* Card fade-in animation (inspired by reactbits CardNav) */
|
|
1120
|
+
@keyframes card-fade-in {
|
|
1121
|
+
from {
|
|
1122
|
+
opacity: 0;
|
|
1123
|
+
transform: translateY(30px);
|
|
1124
|
+
}
|
|
1125
|
+
to {
|
|
1126
|
+
opacity: 1;
|
|
1127
|
+
transform: translateY(0);
|
|
1128
|
+
}
|
|
1129
|
+
}
|
|
1130
|
+
|
|
1131
|
+
.animate-card-fade-in {
|
|
1132
|
+
/* 1s hero-entrance duration is a one-off, so it stays inline */
|
|
1133
|
+
animation: card-fade-in 1s var(--motion-ease-spring) both;
|
|
1134
|
+
}
|
|
1135
|
+
|
|
1136
|
+
/* Type scale: ratio 1.25; body 1rem (16px). Tailwind text-xs/sm/base/lg/xl/2xl/3xl/4xl align. */
|
|
1137
|
+
/* Page typography - consistent titles and descriptions */
|
|
1138
|
+
.page-title {
|
|
1139
|
+
@apply text-lg sm:text-xl font-medium tracking-tight;
|
|
1140
|
+
text-wrap: balance;
|
|
1141
|
+
}
|
|
1142
|
+
.page-description {
|
|
1143
|
+
@apply text-muted-foreground mt-1 text-sm sm:text-base leading-relaxed;
|
|
1144
|
+
}
|
|
1145
|
+
|
|
1146
|
+
/* ─── Board display ─── */
|
|
1147
|
+
/* Split-flap board preview (components/board). The flap keyframes
|
|
1148
|
+
(flapDown/flapUp/flapShadow) are injected into <head> by the component
|
|
1149
|
+
itself so the animation works without CSS import-order coupling; this
|
|
1150
|
+
section carries only the global kill switches, copied verbatim from
|
|
1151
|
+
FiestaBoard app/globals.css. The app stamps these classes on <html>
|
|
1152
|
+
from the user's board-animation setting. */
|
|
1153
|
+
|
|
1154
|
+
/* Board animation kill switch. The board flap structure (4 layers) is
|
|
1155
|
+
already gated in JS so the extra DOM never renders when this is set,
|
|
1156
|
+
but the CSS belt-and-braces guarantees no leftover transforms animate
|
|
1157
|
+
if a flap node is mid-render when the setting flips. */
|
|
1158
|
+
.board-animations-off [data-board-preview] *,
|
|
1159
|
+
.board-animations-off [data-board-preview] *::before,
|
|
1160
|
+
.board-animations-off [data-board-preview] *::after {
|
|
1161
|
+
animation: none !important;
|
|
1162
|
+
transition: none !important;
|
|
1163
|
+
}
|
|
1164
|
+
|
|
1165
|
+
/* Desktop-only mode: suppress board animation below the `md` breakpoint
|
|
1166
|
+
(768px — matches Tailwind's default). */
|
|
1167
|
+
@media (max-width: 767px) {
|
|
1168
|
+
.board-animations-desktop-only [data-board-preview] *,
|
|
1169
|
+
.board-animations-desktop-only [data-board-preview] *::before,
|
|
1170
|
+
.board-animations-desktop-only [data-board-preview] *::after {
|
|
1171
|
+
animation: none !important;
|
|
1172
|
+
transition: none !important;
|
|
1173
|
+
}
|
|
1174
|
+
}
|