@olympusoss/canvas 2.17.0 → 2.20.1
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/package.json +1 -1
- package/src/components/atoms/button.tsx +1 -1
- package/src/components/charts/dot-pulse.tsx +61 -0
- package/src/components/charts/sparkline-area.tsx +80 -0
- package/src/components/molecules/stat-card.tsx +4 -0
- package/src/components/organisms/sidebar.tsx +1 -0
- package/src/index.ts +8 -0
- package/styles/glass.css +136 -142
- package/styles/tokens.css +40 -6
package/package.json
CHANGED
|
@@ -10,7 +10,7 @@ const buttonVariants = cva(
|
|
|
10
10
|
variants: {
|
|
11
11
|
variant: {
|
|
12
12
|
default: "bg-primary text-primary-foreground shadow hover:bg-primary/90",
|
|
13
|
-
destructive: "bg-destructive text-destructive-foreground shadow
|
|
13
|
+
destructive: "bg-destructive text-destructive-foreground shadow hover:bg-destructive/90",
|
|
14
14
|
outline:
|
|
15
15
|
"border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground",
|
|
16
16
|
secondary: "bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80",
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
|
|
3
|
+
import { cn } from "../../lib/utils";
|
|
4
|
+
|
|
5
|
+
export interface DotPulseProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
6
|
+
/** Number of active (pulsing) dots. Clamped to `[0, total]`. */
|
|
7
|
+
count: number;
|
|
8
|
+
/** Total number of dots rendered. Default `5`. */
|
|
9
|
+
total?: number;
|
|
10
|
+
/**
|
|
11
|
+
* CSS variable name (without leading `--`) used for active dot fill.
|
|
12
|
+
* Default `chart-1`. The variable should resolve to an HSL triplet.
|
|
13
|
+
*/
|
|
14
|
+
colorVar?: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Pure-CSS severity indicator: a row of small circles where the first `count`
|
|
19
|
+
* dots pulse with color and the rest are muted. Designed for `<StatCard>`
|
|
20
|
+
* slots where no time-series data exists (e.g. lockout counts).
|
|
21
|
+
*/
|
|
22
|
+
export const DotPulse = React.forwardRef<HTMLDivElement, DotPulseProps>(
|
|
23
|
+
({ count, total = 5, colorVar = "chart-1", className, ...props }, ref) => {
|
|
24
|
+
const active = Math.max(0, Math.min(total, count));
|
|
25
|
+
const color = `hsl(var(--${colorVar}))`;
|
|
26
|
+
|
|
27
|
+
return (
|
|
28
|
+
<div
|
|
29
|
+
ref={ref}
|
|
30
|
+
className={cn("flex items-center gap-2", className)}
|
|
31
|
+
role="presentation"
|
|
32
|
+
{...props}
|
|
33
|
+
>
|
|
34
|
+
{Array.from({ length: total }, (_, i) => {
|
|
35
|
+
const isActive = i < active;
|
|
36
|
+
return (
|
|
37
|
+
<span
|
|
38
|
+
key={i}
|
|
39
|
+
className={cn("inline-block size-2 rounded-full", !isActive && "bg-muted")}
|
|
40
|
+
style={
|
|
41
|
+
isActive
|
|
42
|
+
? {
|
|
43
|
+
background: color,
|
|
44
|
+
animation: `canvas-dot-pulse 1.5s ease-in-out infinite`,
|
|
45
|
+
animationDelay: `${i * 0.15}s`,
|
|
46
|
+
}
|
|
47
|
+
: undefined
|
|
48
|
+
}
|
|
49
|
+
aria-hidden
|
|
50
|
+
/>
|
|
51
|
+
);
|
|
52
|
+
})}
|
|
53
|
+
<style>{`@keyframes canvas-dot-pulse {
|
|
54
|
+
0%, 100% { transform: scale(1); opacity: 1; }
|
|
55
|
+
50% { transform: scale(1.4); opacity: 0.5; }
|
|
56
|
+
}`}</style>
|
|
57
|
+
</div>
|
|
58
|
+
);
|
|
59
|
+
},
|
|
60
|
+
);
|
|
61
|
+
DotPulse.displayName = "DotPulse";
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
|
|
3
|
+
import { cn } from "../../lib/utils";
|
|
4
|
+
|
|
5
|
+
export interface SparklineAreaProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
6
|
+
/** Values to plot. Each entry maps to a point on the area line. Needs at least 2 points. */
|
|
7
|
+
data: number[];
|
|
8
|
+
/** Pixel height of the chart area. */
|
|
9
|
+
height?: number;
|
|
10
|
+
/**
|
|
11
|
+
* CSS variable name (without leading `--`) used for the stroke and fill.
|
|
12
|
+
* Default `chart-1`. The variable should resolve to an HSL triplet.
|
|
13
|
+
*/
|
|
14
|
+
colorVar?: string;
|
|
15
|
+
/** Caption rendered below the chart. */
|
|
16
|
+
caption?: React.ReactNode;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Pure-SVG area sparkline for inline embedding in `<StatCard>` /
|
|
21
|
+
* `<SectionCard>`. Renders a filled area with a gradient fade, a stroke line,
|
|
22
|
+
* and a dot on the last data point. Decorative only (no tooltips, no axes).
|
|
23
|
+
*/
|
|
24
|
+
export const SparklineArea = React.forwardRef<HTMLDivElement, SparklineAreaProps>(
|
|
25
|
+
({ data, height = 48, colorVar = "chart-1", caption, className, ...props }, ref) => {
|
|
26
|
+
const fillId = React.useId();
|
|
27
|
+
|
|
28
|
+
if (data.length < 2) return null;
|
|
29
|
+
|
|
30
|
+
const max = Math.max(...data);
|
|
31
|
+
const min = Math.min(...data);
|
|
32
|
+
const range = max - min || 1;
|
|
33
|
+
const w = (data.length - 1) * 10;
|
|
34
|
+
const h = height;
|
|
35
|
+
const padding = 3; // vertical padding for the end-dot
|
|
36
|
+
|
|
37
|
+
const pts = data
|
|
38
|
+
.map((v, i) => {
|
|
39
|
+
const x = i * 10;
|
|
40
|
+
const y = h - padding - ((v - min) / range) * (h - padding * 2);
|
|
41
|
+
return `${x},${y}`;
|
|
42
|
+
})
|
|
43
|
+
.join(" ");
|
|
44
|
+
|
|
45
|
+
const area = `0,${h} ${pts} ${w},${h}`;
|
|
46
|
+
const last = data[data.length - 1];
|
|
47
|
+
const lastY = h - padding - ((last - min) / range) * (h - padding * 2);
|
|
48
|
+
const color = `hsl(var(--${colorVar}))`;
|
|
49
|
+
|
|
50
|
+
return (
|
|
51
|
+
<div ref={ref} className={cn("w-full", className)} {...props}>
|
|
52
|
+
<svg
|
|
53
|
+
viewBox={`0 0 ${w} ${h}`}
|
|
54
|
+
preserveAspectRatio="none"
|
|
55
|
+
className="h-full w-full overflow-visible"
|
|
56
|
+
style={{ height }}
|
|
57
|
+
aria-hidden
|
|
58
|
+
>
|
|
59
|
+
<defs>
|
|
60
|
+
<linearGradient id={fillId} x1="0" x2="0" y1="0" y2="1">
|
|
61
|
+
<stop offset="0%" stopColor={color} stopOpacity="0.25" />
|
|
62
|
+
<stop offset="100%" stopColor={color} stopOpacity="0" />
|
|
63
|
+
</linearGradient>
|
|
64
|
+
</defs>
|
|
65
|
+
<polygon points={area} fill={`url(#${fillId})`} />
|
|
66
|
+
<polyline
|
|
67
|
+
points={pts}
|
|
68
|
+
fill="none"
|
|
69
|
+
stroke={color}
|
|
70
|
+
strokeWidth="1.5"
|
|
71
|
+
vectorEffect="non-scaling-stroke"
|
|
72
|
+
/>
|
|
73
|
+
<circle cx={w} cy={lastY} r="2.5" fill={color} />
|
|
74
|
+
</svg>
|
|
75
|
+
{caption && <p className="mt-2 text-xs text-muted-foreground">{caption}</p>}
|
|
76
|
+
</div>
|
|
77
|
+
);
|
|
78
|
+
},
|
|
79
|
+
);
|
|
80
|
+
SparklineArea.displayName = "SparklineArea";
|
|
@@ -21,6 +21,8 @@ export interface StatCardProps {
|
|
|
21
21
|
* `delta` as ReactNode to fully control rendering yourself.
|
|
22
22
|
*/
|
|
23
23
|
deltaArrow?: boolean;
|
|
24
|
+
/** Optional content rendered below the delta row (e.g. a sparkline or mini chart). */
|
|
25
|
+
children?: React.ReactNode;
|
|
24
26
|
className?: string;
|
|
25
27
|
}
|
|
26
28
|
|
|
@@ -49,6 +51,7 @@ export function StatCard({
|
|
|
49
51
|
deltaTone = "up",
|
|
50
52
|
deltaCaption,
|
|
51
53
|
deltaArrow = true,
|
|
54
|
+
children,
|
|
52
55
|
className,
|
|
53
56
|
}: StatCardProps) {
|
|
54
57
|
const showArrow = deltaArrow && typeof delta === "string" && deltaTone !== "neutral";
|
|
@@ -88,6 +91,7 @@ export function StatCard({
|
|
|
88
91
|
{deltaCaption != null && <span className="text-muted-foreground">{deltaCaption}</span>}
|
|
89
92
|
</div>
|
|
90
93
|
)}
|
|
94
|
+
{children != null && <div className="mt-3">{children}</div>}
|
|
91
95
|
</CardContent>
|
|
92
96
|
</Card>
|
|
93
97
|
);
|
|
@@ -391,6 +391,7 @@ const SidebarInset = React.forwardRef<HTMLDivElement, SidebarInsetProps>(
|
|
|
391
391
|
return (
|
|
392
392
|
<main
|
|
393
393
|
ref={ref}
|
|
394
|
+
data-slot="sidebar-inset"
|
|
394
395
|
className={cn(
|
|
395
396
|
"relative flex w-full flex-1 flex-col bg-background",
|
|
396
397
|
"md:peer-data-[variant=inset]:m-2 md:peer-data-[state=collapsed]:peer-data-[variant=inset]:ml-2 md:peer-data-[variant=inset]:ml-0 md:peer-data-[variant=inset]:rounded-xl md:peer-data-[variant=inset]:shadow",
|
package/src/index.ts
CHANGED
|
@@ -94,6 +94,10 @@ export {
|
|
|
94
94
|
Sector,
|
|
95
95
|
Trapezoid,
|
|
96
96
|
} from "./components/charts/details";
|
|
97
|
+
export {
|
|
98
|
+
DotPulse,
|
|
99
|
+
type DotPulseProps,
|
|
100
|
+
} from "./components/charts/dot-pulse";
|
|
97
101
|
export { Gauge, type GaugeProps } from "./components/charts/gauge";
|
|
98
102
|
export { CartesianGrid, PolarGrid } from "./components/charts/grids";
|
|
99
103
|
export {
|
|
@@ -123,6 +127,10 @@ export {
|
|
|
123
127
|
Sparkline,
|
|
124
128
|
type SparklineProps,
|
|
125
129
|
} from "./components/charts/sparkline";
|
|
130
|
+
export {
|
|
131
|
+
SparklineArea,
|
|
132
|
+
type SparklineAreaProps,
|
|
133
|
+
} from "./components/charts/sparkline-area";
|
|
126
134
|
export {
|
|
127
135
|
StackedBar,
|
|
128
136
|
type StackedBarProps,
|
package/styles/glass.css
CHANGED
|
@@ -20,158 +20,152 @@
|
|
|
20
20
|
*
|
|
21
21
|
* Mirrors the Athena design handoff's `app.css` glass block; do not
|
|
22
22
|
* tighten these values without checking against the handoff bundle.
|
|
23
|
+
*
|
|
24
|
+
* Deliberately unlayered so glass backgrounds override Tailwind
|
|
25
|
+
* utilities like `bg-card` (which live in @layer utilities).
|
|
23
26
|
*/
|
|
24
27
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
}
|
|
28
|
+
html[data-surface="glass"] {
|
|
29
|
+
--glass-tint: 0 0% 100%;
|
|
30
|
+
--glass-tint-alpha: 0.55;
|
|
31
|
+
--glass-border: 0 0% 100%;
|
|
32
|
+
--glass-border-alpha: 0.45;
|
|
33
|
+
--glass-highlight: 0 0% 100%;
|
|
34
|
+
--glass-highlight-alpha: 0.55;
|
|
35
|
+
--glass-shadow: 220 30% 20%;
|
|
36
|
+
--glass-blur: 18px;
|
|
37
|
+
--glass-saturate: 140%;
|
|
38
|
+
}
|
|
37
39
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
40
|
+
html[data-surface="glass"].dark {
|
|
41
|
+
--glass-tint: 222 22% 18%;
|
|
42
|
+
--glass-tint-alpha: 0.55;
|
|
43
|
+
--glass-border: 0 0% 100%;
|
|
44
|
+
--glass-border-alpha: 0.1;
|
|
45
|
+
--glass-highlight: 0 0% 100%;
|
|
46
|
+
--glass-highlight-alpha: 0.1;
|
|
47
|
+
--glass-shadow: 222 60% 4%;
|
|
48
|
+
}
|
|
47
49
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
hsl(220 30% 97%);
|
|
60
|
-
background-attachment: fixed;
|
|
61
|
-
}
|
|
50
|
+
/* ──────────────────────────────────────────────────────────────
|
|
51
|
+
* Aurora backdrop
|
|
52
|
+
* ──────────────────────────────────────────────────────────── */
|
|
53
|
+
html[data-surface="glass"] body {
|
|
54
|
+
background:
|
|
55
|
+
radial-gradient(60% 50% at 12% 10%, hsl(28 100% 80% / 0.55), transparent 60%),
|
|
56
|
+
radial-gradient(55% 55% at 88% 8%, hsl(210 100% 78% / 0.55), transparent 60%),
|
|
57
|
+
radial-gradient(70% 60% at 50% 100%, hsl(270 90% 82% / 0.45), transparent 65%),
|
|
58
|
+
hsl(220 30% 97%);
|
|
59
|
+
background-attachment: fixed;
|
|
60
|
+
}
|
|
62
61
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
62
|
+
html[data-surface="glass"].dark body {
|
|
63
|
+
background:
|
|
64
|
+
radial-gradient(55% 50% at 10% 8%, hsl(245 90% 60% / 0.4), transparent 60%),
|
|
65
|
+
radial-gradient(50% 55% at 92% 12%, hsl(280 85% 55% / 0.35), transparent 60%),
|
|
66
|
+
radial-gradient(70% 60% at 50% 100%, hsl(190 90% 45% / 0.28), transparent 70%),
|
|
67
|
+
hsl(222 30% 6%);
|
|
68
|
+
background-attachment: fixed;
|
|
69
|
+
}
|
|
71
70
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
inset 0 1px 0 hsl(var(--glass-highlight) / var(--glass-highlight-alpha)),
|
|
110
|
-
0 1px 2px hsl(var(--glass-shadow) / 0.06),
|
|
111
|
-
0 8px 24px -12px hsl(var(--glass-shadow) / 0.18);
|
|
112
|
-
}
|
|
71
|
+
/* ──────────────────────────────────────────────────────────────
|
|
72
|
+
* Frosted-pane surfaces
|
|
73
|
+
* ──────────────────────────────────────────────────────────── */
|
|
74
|
+
html[data-surface="glass"] [data-slot="card"],
|
|
75
|
+
html[data-surface="glass"] [data-slot="sidebar"],
|
|
76
|
+
html[data-surface="glass"] [data-slot="topbar"],
|
|
77
|
+
html[data-surface="glass"] [data-slot="data-table"],
|
|
78
|
+
html[data-surface="glass"] [data-slot="empty-state"],
|
|
79
|
+
html[data-surface="glass"] [data-slot="popover-content"],
|
|
80
|
+
html[data-surface="glass"] [data-slot="sheet-content"],
|
|
81
|
+
html[data-surface="glass"] [data-slot="drawer-content"],
|
|
82
|
+
html[data-surface="glass"] [data-slot="alert"],
|
|
83
|
+
html[data-surface="glass"] [data-slot="alert-dialog-content"],
|
|
84
|
+
html[data-surface="glass"] [data-slot="dialog-content"],
|
|
85
|
+
html[data-surface="glass"] [data-slot="dropdown-menu-content"],
|
|
86
|
+
html[data-surface="glass"] [data-slot="dropdown-menu-sub-content"],
|
|
87
|
+
html[data-surface="glass"] [data-slot="context-menu-content"],
|
|
88
|
+
html[data-surface="glass"] [data-slot="context-menu-sub-content"],
|
|
89
|
+
html[data-surface="glass"] [data-slot="hover-card-content"],
|
|
90
|
+
html[data-surface="glass"] [data-slot="select-content"],
|
|
91
|
+
html[data-surface="glass"] [data-slot="menubar"],
|
|
92
|
+
html[data-surface="glass"] [data-slot="menubar-content"],
|
|
93
|
+
html[data-surface="glass"] [data-slot="menubar-sub-content"],
|
|
94
|
+
html[data-surface="glass"] [data-slot="navigation-menu-viewport"],
|
|
95
|
+
html[data-surface="glass"] [data-slot="tooltip-content"],
|
|
96
|
+
html[data-surface="glass"] [data-slot="command"],
|
|
97
|
+
html[data-surface="glass"] [data-slot="calendar"],
|
|
98
|
+
html[data-surface="glass"] [data-slot="terminal"] {
|
|
99
|
+
background: hsl(var(--glass-tint) / var(--glass-tint-alpha));
|
|
100
|
+
backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturate));
|
|
101
|
+
-webkit-backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturate));
|
|
102
|
+
border-color: hsl(var(--glass-border) / var(--glass-border-alpha));
|
|
103
|
+
box-shadow:
|
|
104
|
+
inset 0 1px 0 hsl(var(--glass-highlight) / var(--glass-highlight-alpha)),
|
|
105
|
+
0 1px 2px hsl(var(--glass-shadow) / 0.06),
|
|
106
|
+
0 8px 24px -12px hsl(var(--glass-shadow) / 0.18);
|
|
107
|
+
}
|
|
113
108
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
border-color: hsl(var(--glass-border) / var(--glass-border-alpha));
|
|
120
|
-
}
|
|
109
|
+
html[data-surface="glass"] [data-slot="sidebar"],
|
|
110
|
+
html[data-surface="glass"] [data-slot="topbar"] {
|
|
111
|
+
background: hsl(var(--glass-tint) / calc(var(--glass-tint-alpha) - 0.1));
|
|
112
|
+
border-color: hsl(var(--glass-border) / var(--glass-border-alpha));
|
|
113
|
+
}
|
|
121
114
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
background: hsl(var(--glass-highlight) / 0.1);
|
|
132
|
-
}
|
|
115
|
+
/* SidebarInset (the main content column) is fully transparent in glass
|
|
116
|
+
* mode so the aurora gradient on <body> reads through unimpeded. No
|
|
117
|
+
* backdrop-filter is applied: the content area is a window onto the
|
|
118
|
+
* aurora, not a frosted pane. The Tailwind `bg-background` utility on
|
|
119
|
+
* the component is overridden here by the higher-specificity selector
|
|
120
|
+
* and the unlayered cascade of glass.css. */
|
|
121
|
+
html[data-surface="glass"] [data-slot="sidebar-inset"] {
|
|
122
|
+
background: transparent;
|
|
123
|
+
}
|
|
133
124
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
125
|
+
/* DataTable internal rules */
|
|
126
|
+
html[data-surface="glass"] [data-slot="data-table"] thead tr {
|
|
127
|
+
background: hsl(var(--glass-highlight) / 0.1);
|
|
128
|
+
border-bottom: 1px solid hsl(var(--glass-border) / var(--glass-border-alpha));
|
|
129
|
+
}
|
|
130
|
+
html[data-surface="glass"] [data-slot="data-table"] tbody tr {
|
|
131
|
+
border-bottom: 1px solid hsl(var(--glass-border) / calc(var(--glass-border-alpha) - 0.2));
|
|
132
|
+
}
|
|
133
|
+
html[data-surface="glass"] [data-slot="data-table"] tbody tr:hover {
|
|
134
|
+
background: hsl(var(--glass-highlight) / 0.1);
|
|
135
|
+
}
|
|
138
136
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
html[data-surface="glass"] [data-slot="code-block"],
|
|
143
|
-
html[data-surface="glass"] [data-slot="tabs-list"] {
|
|
144
|
-
background: hsl(var(--glass-tint) / 0.35);
|
|
145
|
-
border-color: hsl(var(--glass-border) / var(--glass-border-alpha));
|
|
146
|
-
backdrop-filter: blur(8px);
|
|
147
|
-
-webkit-backdrop-filter: blur(8px);
|
|
148
|
-
}
|
|
137
|
+
html[data-surface="glass"] [data-slot="card"] [data-slot="card-divider"] {
|
|
138
|
+
background: hsl(var(--glass-border) / var(--glass-border-alpha));
|
|
139
|
+
}
|
|
149
140
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
141
|
+
html[data-surface="glass"] [data-slot="input"],
|
|
142
|
+
html[data-surface="glass"] [data-slot="code-block"],
|
|
143
|
+
html[data-surface="glass"] [data-slot="tabs-list"] {
|
|
144
|
+
background: hsl(var(--glass-tint) / 0.35);
|
|
145
|
+
border-color: hsl(var(--glass-border) / var(--glass-border-alpha));
|
|
146
|
+
backdrop-filter: blur(8px);
|
|
147
|
+
-webkit-backdrop-filter: blur(8px);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
html[data-surface="glass"] [data-slot="accordion-item"] {
|
|
151
|
+
border-bottom-color: hsl(var(--glass-border) / var(--glass-border-alpha));
|
|
152
|
+
}
|
|
155
153
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
html[data-surface="glass"] [data-slot="tooltip-content"],
|
|
174
|
-
html[data-surface="glass"] [data-slot="command"] {
|
|
175
|
-
background: hsl(var(--glass-tint) / 0.85);
|
|
176
|
-
}
|
|
154
|
+
html[data-surface="glass"] [data-slot="popover-content"],
|
|
155
|
+
html[data-surface="glass"] [data-slot="sheet-content"],
|
|
156
|
+
html[data-surface="glass"] [data-slot="drawer-content"],
|
|
157
|
+
html[data-surface="glass"] [data-slot="alert-dialog-content"],
|
|
158
|
+
html[data-surface="glass"] [data-slot="dialog-content"],
|
|
159
|
+
html[data-surface="glass"] [data-slot="dropdown-menu-content"],
|
|
160
|
+
html[data-surface="glass"] [data-slot="dropdown-menu-sub-content"],
|
|
161
|
+
html[data-surface="glass"] [data-slot="context-menu-content"],
|
|
162
|
+
html[data-surface="glass"] [data-slot="context-menu-sub-content"],
|
|
163
|
+
html[data-surface="glass"] [data-slot="hover-card-content"],
|
|
164
|
+
html[data-surface="glass"] [data-slot="select-content"],
|
|
165
|
+
html[data-surface="glass"] [data-slot="menubar-content"],
|
|
166
|
+
html[data-surface="glass"] [data-slot="menubar-sub-content"],
|
|
167
|
+
html[data-surface="glass"] [data-slot="navigation-menu-viewport"],
|
|
168
|
+
html[data-surface="glass"] [data-slot="tooltip-content"],
|
|
169
|
+
html[data-surface="glass"] [data-slot="command"] {
|
|
170
|
+
background: hsl(var(--glass-tint) / 0.85);
|
|
177
171
|
}
|
package/styles/tokens.css
CHANGED
|
@@ -42,8 +42,13 @@
|
|
|
42
42
|
--card-foreground: 240 10% 3.9%;
|
|
43
43
|
--popover: 0 0% 100%;
|
|
44
44
|
--popover-foreground: 240 10% 3.9%;
|
|
45
|
-
|
|
46
|
-
|
|
45
|
+
/* Vivid blue primary (per handoff's rendered theme, not the canonical
|
|
46
|
+
* dark-zinc default in colors_and_type.css). The handoff prototype
|
|
47
|
+
* runs a runtime theme picker that overrides --primary + --ring inline
|
|
48
|
+
* on <html>; this bakes that override into Canvas so every consumer
|
|
49
|
+
* gets the same blue accent without the picker. */
|
|
50
|
+
--primary: 240 79% 60%;
|
|
51
|
+
--primary-foreground: 0 0% 100%;
|
|
47
52
|
--secondary: 240 4.8% 95.9%;
|
|
48
53
|
--secondary-foreground: 240 5.9% 10%;
|
|
49
54
|
--muted: 240 4.8% 95.9%;
|
|
@@ -56,7 +61,7 @@
|
|
|
56
61
|
--brand-foreground: 0 0% 100%;
|
|
57
62
|
--border: 240 5.9% 90%;
|
|
58
63
|
--input: 240 5.9% 90%;
|
|
59
|
-
--ring: 240
|
|
64
|
+
--ring: 240 79% 60%;
|
|
60
65
|
--radius: 0.5rem;
|
|
61
66
|
|
|
62
67
|
/* ── Chart palette ─────────────────────────────────────────── */
|
|
@@ -119,8 +124,11 @@
|
|
|
119
124
|
--popover: 240 10% 3.9%;
|
|
120
125
|
--popover-foreground: 0 0% 98%;
|
|
121
126
|
|
|
122
|
-
|
|
123
|
-
|
|
127
|
+
/* Same vivid blue as light mode so the accent stays consistent
|
|
128
|
+
* across themes (per handoff runtime override). The brighter blue
|
|
129
|
+
* still contrasts white text in dark mode. */
|
|
130
|
+
--primary: 240 79% 60%;
|
|
131
|
+
--primary-foreground: 0 0% 100%;
|
|
124
132
|
|
|
125
133
|
--secondary: 240 3.7% 15.9%;
|
|
126
134
|
--secondary-foreground: 0 0% 98%;
|
|
@@ -136,7 +144,7 @@
|
|
|
136
144
|
|
|
137
145
|
--border: 240 3.7% 15.9%;
|
|
138
146
|
--input: 240 3.7% 15.9%;
|
|
139
|
-
--ring: 240
|
|
147
|
+
--ring: 240 79% 60%;
|
|
140
148
|
|
|
141
149
|
/* Chart palette (Canvas-specific; handoff dashboard uses --primary
|
|
142
150
|
* for bar fills rather than --chart-1, so these stay as-is). */
|
|
@@ -259,6 +267,32 @@
|
|
|
259
267
|
--color-stat-purple: hsl(var(--stat-purple));
|
|
260
268
|
--color-stat-destructive: hsl(var(--stat-destructive));
|
|
261
269
|
--color-stat-amber: hsl(var(--stat-amber));
|
|
270
|
+
|
|
271
|
+
/* ── Shadow scale (per handoff canvas.css) ──────────────────
|
|
272
|
+
* Two base tones, both single-layer at the small end:
|
|
273
|
+
* - `2xs` / `xs` / `sm`: 0 1px 2px / 5% — inputs, outline /
|
|
274
|
+
* secondary buttons (matches handoff `.input` and
|
|
275
|
+
* `.btn-outline` / `.btn-secondary`).
|
|
276
|
+
* - DEFAULT (`shadow`): 0 1px 3px / 8% — cards and primary /
|
|
277
|
+
* destructive buttons (matches handoff `.card`,
|
|
278
|
+
* `.btn-default`, `.btn-destructive`).
|
|
279
|
+
* - `md` / `lg` / `xl`: two-layer stacks softened to 8% / 6%
|
|
280
|
+
* opacity so popovers, dialogs, and drawers sit on the same
|
|
281
|
+
* depth ramp as cards instead of feeling heavier (Tailwind
|
|
282
|
+
* defaults use 10%).
|
|
283
|
+
* - `inner`: keeps Tailwind default (no handoff override).
|
|
284
|
+
*
|
|
285
|
+
* Overriding the @theme tokens propagates through every Canvas
|
|
286
|
+
* component class (`shadow-sm`, `shadow-md`, etc.) without
|
|
287
|
+
* touching component sources.
|
|
288
|
+
*/
|
|
289
|
+
--shadow-2xs: 0 1px 2px 0 rgb(0 0 0 / 0.05);
|
|
290
|
+
--shadow-xs: 0 1px 2px 0 rgb(0 0 0 / 0.05);
|
|
291
|
+
--shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.05);
|
|
292
|
+
--shadow: 0 1px 3px 0 rgb(0 0 0 / 0.08);
|
|
293
|
+
--shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.08), 0 2px 4px -2px rgb(0 0 0 / 0.06);
|
|
294
|
+
--shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.08), 0 4px 6px -4px rgb(0 0 0 / 0.06);
|
|
295
|
+
--shadow-xl: 0 20px 25px -5px rgb(0 0 0 / 0.08), 0 8px 10px -6px rgb(0 0 0 / 0.06);
|
|
262
296
|
}
|
|
263
297
|
|
|
264
298
|
@keyframes orb-float-1 {
|