@alpic-ai/ui 0.0.0-dev.g2d48d44 → 0.0.0-dev.g2eefcc2
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/dist/components/accordion-card.d.mts +5 -6
- package/dist/components/accordion.d.mts +5 -6
- package/dist/components/alert.d.mts +9 -11
- package/dist/components/area-chart.d.mts +62 -0
- package/dist/components/area-chart.mjs +269 -0
- package/dist/components/attachment-tile.d.mts +1 -3
- package/dist/components/avatar.d.mts +8 -10
- package/dist/components/badge.d.mts +2 -4
- package/dist/components/bar-chart.d.mts +48 -0
- package/dist/components/bar-chart.mjs +256 -0
- package/dist/components/bar-list.d.mts +28 -0
- package/dist/components/bar-list.mjs +98 -0
- package/dist/components/breadcrumb.d.mts +10 -11
- package/dist/components/button.d.mts +6 -8
- package/dist/components/card.d.mts +9 -10
- package/dist/components/chart-card.d.mts +25 -0
- package/dist/components/chart-card.mjs +48 -0
- package/dist/components/chart-container.d.mts +20 -0
- package/dist/components/chart-container.mjs +37 -0
- package/dist/components/chart-legend.d.mts +16 -0
- package/dist/components/chart-legend.mjs +26 -0
- package/dist/components/chart-tooltip.d.mts +33 -0
- package/dist/components/chart-tooltip.mjs +52 -0
- package/dist/components/checkbox.d.mts +2 -3
- package/dist/components/collapsible.d.mts +4 -5
- package/dist/components/combobox.d.mts +12 -11
- package/dist/components/combobox.mjs +7 -4
- package/dist/components/command.d.mts +9 -10
- package/dist/components/copyable.d.mts +2 -3
- package/dist/components/description-list.d.mts +5 -6
- package/dist/components/dialog.d.mts +15 -17
- package/dist/components/donut-chart.d.mts +46 -0
- package/dist/components/donut-chart.mjs +185 -0
- package/dist/components/dropdown-menu.d.mts +18 -20
- package/dist/components/form.d.mts +38 -21
- package/dist/components/form.mjs +6 -6
- package/dist/components/github-button.d.mts +1 -2
- package/dist/components/heatmap-chart.d.mts +40 -0
- package/dist/components/heatmap-chart.mjs +198 -0
- package/dist/components/input-group.d.mts +5 -7
- package/dist/components/input.d.mts +4 -5
- package/dist/components/input.mjs +2 -2
- package/dist/components/label.d.mts +2 -3
- package/dist/components/line-chart.d.mts +55 -0
- package/dist/components/line-chart.mjs +211 -0
- package/dist/components/page-loader.d.mts +1 -3
- package/dist/components/pagination.d.mts +3 -4
- package/dist/components/popover.d.mts +5 -6
- package/dist/components/radio-group.d.mts +3 -4
- package/dist/components/scroll-area.d.mts +3 -4
- package/dist/components/select-trigger-variants.d.mts +1 -3
- package/dist/components/select.d.mts +9 -10
- package/dist/components/separator.d.mts +2 -3
- package/dist/components/sheet.d.mts +11 -12
- package/dist/components/shimmer-text.d.mts +2 -2
- package/dist/components/sidebar.d.mts +34 -36
- package/dist/components/sidebar.mjs +10 -10
- package/dist/components/skeleton.d.mts +2 -4
- package/dist/components/sonner.d.mts +5 -6
- package/dist/components/spinner.d.mts +3 -5
- package/dist/components/stat.d.mts +30 -0
- package/dist/components/stat.mjs +107 -0
- package/dist/components/status-dot.d.mts +2 -4
- package/dist/components/switch.d.mts +2 -3
- package/dist/components/table.d.mts +10 -11
- package/dist/components/tabs.d.mts +12 -14
- package/dist/components/tag.d.mts +3 -5
- package/dist/components/task-progress.d.mts +1 -3
- package/dist/components/textarea.d.mts +3 -4
- package/dist/components/textarea.mjs +2 -2
- package/dist/components/toggle-group.d.mts +4 -6
- package/dist/components/toggle-group.mjs +3 -3
- package/dist/components/tooltip-icon-button.d.mts +1 -2
- package/dist/components/tooltip.d.mts +5 -6
- package/dist/components/typography.d.mts +4 -5
- package/dist/components/wizard.d.mts +4 -23
- package/dist/components/wizard.mjs +1 -19
- package/dist/hooks/use-chart-theme.d.mts +18 -0
- package/dist/hooks/use-chart-theme.mjs +57 -0
- package/dist/hooks/use-mobile.mjs +3 -3
- package/dist/hooks/use-reduced-motion.d.mts +4 -0
- package/dist/hooks/use-reduced-motion.mjs +16 -0
- package/dist/lib/chart-palette.d.mts +4 -0
- package/dist/lib/chart-palette.mjs +95 -0
- package/dist/lib/chart.d.mts +14 -0
- package/dist/lib/chart.mjs +27 -0
- package/package.json +30 -29
- package/src/components/area-chart.tsx +339 -0
- package/src/components/bar-chart.tsx +309 -0
- package/src/components/bar-list.tsx +150 -0
- package/src/components/chart-card.tsx +63 -0
- package/src/components/chart-container.tsx +49 -0
- package/src/components/chart-legend.tsx +41 -0
- package/src/components/chart-tooltip.tsx +93 -0
- package/src/components/combobox.tsx +9 -2
- package/src/components/donut-chart.tsx +217 -0
- package/src/components/heatmap-chart.tsx +287 -0
- package/src/components/line-chart.tsx +264 -0
- package/src/components/stat.tsx +109 -0
- package/src/components/wizard.tsx +1 -35
- package/src/hooks/use-chart-theme.ts +75 -0
- package/src/hooks/use-reduced-motion.ts +17 -0
- package/src/lib/chart-palette.ts +110 -0
- package/src/lib/chart.ts +56 -0
- package/src/stories/area-chart.stories.tsx +198 -0
- package/src/stories/bar-chart.stories.tsx +167 -0
- package/src/stories/bar-list.stories.tsx +83 -0
- package/src/stories/donut-chart.stories.tsx +110 -0
- package/src/stories/heatmap-chart.stories.tsx +105 -0
- package/src/stories/line-chart.stories.tsx +144 -0
- package/src/stories/stat.stories.tsx +64 -0
- package/src/stories/wizard.stories.tsx +22 -4
- package/src/styles/tokens.css +63 -0
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import type { Story } from "@ladle/react";
|
|
2
|
+
|
|
3
|
+
import { ChartCard } from "../components/chart-card";
|
|
4
|
+
import { LineChart } from "../components/line-chart";
|
|
5
|
+
import { Stat } from "../components/stat";
|
|
6
|
+
|
|
7
|
+
export default { title: "Charts/Line Chart" };
|
|
8
|
+
|
|
9
|
+
const mulberry32 = (seed: number) => () => {
|
|
10
|
+
seed |= 0;
|
|
11
|
+
seed = (seed + 0x6d2b79f5) | 0;
|
|
12
|
+
let hash = Math.imul(seed ^ (seed >>> 15), 1 | seed);
|
|
13
|
+
hash = (hash + Math.imul(hash ^ (hash >>> 7), 61 | hash)) ^ hash;
|
|
14
|
+
return ((hash ^ (hash >>> 14)) >>> 0) / 4294967296;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const hourLabel = (index: number, length: number) =>
|
|
18
|
+
`${String(Math.round((index / (length - 1)) * 24)).padStart(2, "0")}:00`;
|
|
19
|
+
|
|
20
|
+
const genSeries = (seed: number, scale: number) => {
|
|
21
|
+
const rnd = mulberry32(seed);
|
|
22
|
+
const length = 30;
|
|
23
|
+
let value = scale * 0.6;
|
|
24
|
+
return Array.from({ length }, (_, index) => {
|
|
25
|
+
value = Math.max(scale * 0.12, value + (rnd() - 0.46) * scale * 0.18);
|
|
26
|
+
return { t: hourLabel(index, length), v: Math.round(value * (1 + Math.sin(index / 5) * 0.16)) };
|
|
27
|
+
});
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const genLatency = (seed: number) => {
|
|
31
|
+
const rnd = mulberry32(seed);
|
|
32
|
+
const length = 30;
|
|
33
|
+
let p50 = 240;
|
|
34
|
+
let p95 = 720;
|
|
35
|
+
return Array.from({ length }, (_, index) => {
|
|
36
|
+
p50 = Math.max(120, p50 + (rnd() - 0.48) * 40);
|
|
37
|
+
p95 = Math.max(p50 + 200, p95 + (rnd() - 0.46) * 120);
|
|
38
|
+
return { t: hourLabel(index, length), p50: Math.round(p50), p95: Math.round(p95 * (index === 19 ? 1.7 : 1)) };
|
|
39
|
+
});
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const genCompare = (seed: number) => {
|
|
43
|
+
const rnd = mulberry32(seed);
|
|
44
|
+
const length = 30;
|
|
45
|
+
let claude = 60;
|
|
46
|
+
let chatgpt = 80;
|
|
47
|
+
return Array.from({ length }, (_, index) => {
|
|
48
|
+
claude = Math.max(20, claude + (rnd() - 0.42) * 14);
|
|
49
|
+
chatgpt = Math.max(20, chatgpt + (rnd() - 0.5) * 12);
|
|
50
|
+
return { t: hourLabel(index, length), claude: Math.round(claude), chatgpt: Math.round(chatgpt) };
|
|
51
|
+
});
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
const tokens = genSeries(11, 84000);
|
|
55
|
+
const latency = genLatency(31);
|
|
56
|
+
const compare = genCompare(23);
|
|
57
|
+
|
|
58
|
+
const nf = (value: number) => value.toLocaleString("en-US");
|
|
59
|
+
const fmtK = (value: number) => (value >= 1000 ? `${(value / 1000).toFixed(value >= 10000 ? 0 : 1)}k` : `${value}`);
|
|
60
|
+
const ms = (value: number) => `${value}ms`;
|
|
61
|
+
|
|
62
|
+
const tokensSpark = tokens.map((row) => row.v);
|
|
63
|
+
const latencyPeak = latency.reduce((best, row) => (row.p95 > best.p95 ? row : best));
|
|
64
|
+
|
|
65
|
+
export const AllVariants: Story = () => (
|
|
66
|
+
<div className="chart-canvas mx-auto max-w-[1600px] p-8">
|
|
67
|
+
<div className="grid grid-cols-1 gap-6 md:grid-cols-2 xl:grid-cols-3">
|
|
68
|
+
<ChartCard
|
|
69
|
+
palette="cyan"
|
|
70
|
+
accent="left"
|
|
71
|
+
kicker="Last 24h"
|
|
72
|
+
title="Sessions"
|
|
73
|
+
description="Full-width · left accent"
|
|
74
|
+
className="md:col-span-2 xl:col-span-3"
|
|
75
|
+
>
|
|
76
|
+
<LineChart
|
|
77
|
+
data={compare}
|
|
78
|
+
index="t"
|
|
79
|
+
series={[
|
|
80
|
+
{ key: "claude", name: "Claude" },
|
|
81
|
+
{ key: "chatgpt", name: "ChatGPT" },
|
|
82
|
+
]}
|
|
83
|
+
lastValueLabel
|
|
84
|
+
legend
|
|
85
|
+
valueFormatter={nf}
|
|
86
|
+
/>
|
|
87
|
+
</ChartCard>
|
|
88
|
+
|
|
89
|
+
<ChartCard palette="magenta" kicker="Last 24h" title="Output tokens" description="Single series · value flags">
|
|
90
|
+
<Stat value="1.4M" unit="tokens" delta={{ value: 8.1, direction: "up" }} sparkline={tokensSpark} />
|
|
91
|
+
<LineChart data={tokens} index="t" series={[{ key: "v", name: "tokens" }]} valueFlags valueFormatter={fmtK} />
|
|
92
|
+
</ChartCard>
|
|
93
|
+
|
|
94
|
+
<ChartCard palette="magenta" kicker="Last 24h" title="Request latency" description="p50 vs p95 · SLA band + peak">
|
|
95
|
+
<Stat value="240ms" unit="p50 · current" delta={{ value: 4.2, direction: "down", invert: true }} />
|
|
96
|
+
<LineChart
|
|
97
|
+
data={latency}
|
|
98
|
+
index="t"
|
|
99
|
+
series={[
|
|
100
|
+
{ key: "p50", name: "p50" },
|
|
101
|
+
{ key: "p95", name: "p95", dashed: true, color: "#9b5de5" },
|
|
102
|
+
]}
|
|
103
|
+
referenceLine={{ y: 1000, label: "SLA 1s", band: true }}
|
|
104
|
+
markers={[{ x: latencyPeak.t, y: latencyPeak.p95, label: "peak", color: "#9b5de5" }]}
|
|
105
|
+
legend
|
|
106
|
+
valueFormatter={ms}
|
|
107
|
+
/>
|
|
108
|
+
</ChartCard>
|
|
109
|
+
|
|
110
|
+
<ChartCard palette="cyan" kicker="Last 24h" title="Sessions" description="Claude vs ChatGPT · last values">
|
|
111
|
+
<LineChart
|
|
112
|
+
data={compare}
|
|
113
|
+
index="t"
|
|
114
|
+
series={[
|
|
115
|
+
{ key: "claude", name: "Claude" },
|
|
116
|
+
{ key: "chatgpt", name: "ChatGPT" },
|
|
117
|
+
]}
|
|
118
|
+
lastValueLabel
|
|
119
|
+
legend
|
|
120
|
+
valueFormatter={nf}
|
|
121
|
+
/>
|
|
122
|
+
</ChartCard>
|
|
123
|
+
|
|
124
|
+
<ChartCard palette="cyan" kicker="Last 24h" title="Throughput" description="Dots · stepped curve">
|
|
125
|
+
<LineChart
|
|
126
|
+
data={tokens}
|
|
127
|
+
index="t"
|
|
128
|
+
series={[{ key: "v", name: "tokens" }]}
|
|
129
|
+
curve="step"
|
|
130
|
+
dots
|
|
131
|
+
valueFormatter={fmtK}
|
|
132
|
+
/>
|
|
133
|
+
</ChartCard>
|
|
134
|
+
|
|
135
|
+
<ChartCard palette="magenta" kicker="State" title="Loading" description="Mono spinner placeholder">
|
|
136
|
+
<LineChart data={[]} index="t" series={[{ key: "v", name: "tokens" }]} loading />
|
|
137
|
+
</ChartCard>
|
|
138
|
+
|
|
139
|
+
<ChartCard palette="magenta" kicker="State" title="Empty" description="No data in range">
|
|
140
|
+
<LineChart data={[]} index="t" series={[{ key: "v", name: "tokens" }]} />
|
|
141
|
+
</ChartCard>
|
|
142
|
+
</div>
|
|
143
|
+
</div>
|
|
144
|
+
);
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import type { Story } from "@ladle/react";
|
|
2
|
+
|
|
3
|
+
import { ChartContainer } from "../components/chart-container";
|
|
4
|
+
import { Stat } from "../components/stat";
|
|
5
|
+
|
|
6
|
+
export default { title: "Charts/Stat" };
|
|
7
|
+
|
|
8
|
+
const mulberry32 = (seed: number) => () => {
|
|
9
|
+
seed |= 0;
|
|
10
|
+
seed = (seed + 0x6d2b79f5) | 0;
|
|
11
|
+
let hash = Math.imul(seed ^ (seed >>> 15), 1 | seed);
|
|
12
|
+
hash = (hash + Math.imul(hash ^ (hash >>> 7), 61 | hash)) ^ hash;
|
|
13
|
+
return ((hash ^ (hash >>> 14)) >>> 0) / 4294967296;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const spark = (seed: number, length = 24) => {
|
|
17
|
+
const rnd = mulberry32(seed);
|
|
18
|
+
let value = 40;
|
|
19
|
+
return Array.from({ length }, () => {
|
|
20
|
+
value = Math.max(8, value + (rnd() - 0.45) * 16);
|
|
21
|
+
return Math.round(value);
|
|
22
|
+
});
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const Row = ({ label, children }: { label: string; children: React.ReactNode }) => (
|
|
26
|
+
<div className="flex flex-col gap-1.5">
|
|
27
|
+
<p className="type-text-xs font-medium text-subtle-foreground">{label}</p>
|
|
28
|
+
{children}
|
|
29
|
+
</div>
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
export const AllVariants: Story = () => (
|
|
33
|
+
<div className="grid max-w-3xl grid-cols-2 gap-8">
|
|
34
|
+
<Row label="Value + unit">
|
|
35
|
+
<Stat value="48,231" unit="sessions · 24h" />
|
|
36
|
+
</Row>
|
|
37
|
+
|
|
38
|
+
<Row label="Positive delta (up = good)">
|
|
39
|
+
<Stat value="1.4M" unit="tokens · 24h" delta={{ value: 8.1, direction: "up" }} />
|
|
40
|
+
</Row>
|
|
41
|
+
|
|
42
|
+
<Row label="Negative delta (down = bad)">
|
|
43
|
+
<Stat value="312" unit="errors · 24h" delta={{ value: 14.2, direction: "up", invert: true }} />
|
|
44
|
+
</Row>
|
|
45
|
+
|
|
46
|
+
<Row label="Inverted: latency down = good">
|
|
47
|
+
<Stat value="240ms" unit="p50 · current" delta={{ value: 4.2, direction: "down", invert: true }} />
|
|
48
|
+
</Row>
|
|
49
|
+
|
|
50
|
+
<Row label="With sparkline">
|
|
51
|
+
<Stat value="48,231" unit="sessions · 24h" delta={{ value: 12.4, direction: "up" }} sparkline={spark(7)} />
|
|
52
|
+
</Row>
|
|
53
|
+
|
|
54
|
+
<Row label="Custom delta label">
|
|
55
|
+
<Stat value="312" unit="errors · 24h" delta={{ value: 0, direction: "up", invert: true, label: "burst 16:00" }} />
|
|
56
|
+
</Row>
|
|
57
|
+
|
|
58
|
+
<Row label="Cyan palette (sparkline lead)">
|
|
59
|
+
<ChartContainer palette="cyan">
|
|
60
|
+
<Stat value="9,204" unit="tasks · 24h" delta={{ value: 22, direction: "up" }} sparkline={spark(53)} />
|
|
61
|
+
</ChartContainer>
|
|
62
|
+
</Row>
|
|
63
|
+
</div>
|
|
64
|
+
);
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import type { Story } from "@ladle/react";
|
|
2
2
|
import { useState } from "react";
|
|
3
3
|
|
|
4
|
-
import {
|
|
4
|
+
import { TabsNav, TabsNavList, TabsNavTrigger } from "../components/tabs";
|
|
5
|
+
import { WizardProgress } from "../components/wizard";
|
|
5
6
|
|
|
6
7
|
const SECTION_HEADER = "type-text-xs font-medium text-muted-foreground uppercase tracking-wide pt-4";
|
|
7
8
|
|
|
8
|
-
const steps
|
|
9
|
+
const steps = [
|
|
9
10
|
{ id: "overview", label: "Overview" },
|
|
10
11
|
{ id: "branding", label: "Branding & metadata" },
|
|
11
12
|
{ id: "auth", label: "Authentication" },
|
|
@@ -13,6 +14,23 @@ const steps: WizardStep[] = [
|
|
|
13
14
|
{ id: "review", label: "Review & submit" },
|
|
14
15
|
];
|
|
15
16
|
|
|
17
|
+
/** The step rail is a vertical `TabsNav` composed with the consumer's selection state. */
|
|
18
|
+
function StepRail({ activeIdx, onSelect }: { activeIdx: number; onSelect: (idx: number) => void }) {
|
|
19
|
+
return (
|
|
20
|
+
<TabsNav orientation="vertical" aria-label="Submission steps">
|
|
21
|
+
<TabsNavList>
|
|
22
|
+
{steps.map((step, idx) => (
|
|
23
|
+
<TabsNavTrigger key={step.id} active={idx === activeIdx} asChild>
|
|
24
|
+
<button type="button" onClick={() => onSelect(idx)} className="w-full justify-start text-left">
|
|
25
|
+
{step.label}
|
|
26
|
+
</button>
|
|
27
|
+
</TabsNavTrigger>
|
|
28
|
+
))}
|
|
29
|
+
</TabsNavList>
|
|
30
|
+
</TabsNav>
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
|
|
16
34
|
export const AllVariants: Story = () => {
|
|
17
35
|
const [activeIdx, setActiveIdx] = useState(1);
|
|
18
36
|
|
|
@@ -23,7 +41,7 @@ export const AllVariants: Story = () => {
|
|
|
23
41
|
<p className={SECTION_HEADER}>Full rail — steps + progress</p>
|
|
24
42
|
<div className="mt-4 flex gap-6">
|
|
25
43
|
<aside className="basis-56 shrink-0 flex flex-col gap-4 self-start">
|
|
26
|
-
<
|
|
44
|
+
<StepRail activeIdx={activeIdx} onSelect={setActiveIdx} />
|
|
27
45
|
<WizardProgress current={activeIdx + 1} total={steps.length} />
|
|
28
46
|
</aside>
|
|
29
47
|
<div className="flex-1 rounded-md border p-4">
|
|
@@ -36,7 +54,7 @@ export const AllVariants: Story = () => {
|
|
|
36
54
|
<div>
|
|
37
55
|
<p className={SECTION_HEADER}>Steps only</p>
|
|
38
56
|
<div className="mt-4 max-w-56">
|
|
39
|
-
<
|
|
57
|
+
<StepRail activeIdx={activeIdx} onSelect={setActiveIdx} />
|
|
40
58
|
</div>
|
|
41
59
|
</div>
|
|
42
60
|
|
package/src/styles/tokens.css
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
--font-sans: "Inter", system-ui, sans-serif;
|
|
5
5
|
--font-display: "Inter", sans-serif;
|
|
6
6
|
--font-body: "Inter", sans-serif;
|
|
7
|
+
--font-mono: "JetBrains Mono", ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
|
|
7
8
|
|
|
8
9
|
--color-background: #ffffff; /* Figma: bg-primary */
|
|
9
10
|
--color-foreground: #121e1e; /* Figma: fg-primary */
|
|
@@ -446,6 +447,68 @@
|
|
|
446
447
|
}
|
|
447
448
|
}
|
|
448
449
|
|
|
450
|
+
/* ─── Chart canvas — control-room atmosphere for analytics surfaces ───────────
|
|
451
|
+
Apply `.chart-canvas` to a dashboard/page container holding chart cards: a
|
|
452
|
+
dotted grid faded from the top, plus a dual brand glow in dark mode. The grid
|
|
453
|
+
is masked away so it never bleeds into the cards stacked above it. */
|
|
454
|
+
|
|
455
|
+
.chart-canvas {
|
|
456
|
+
position: relative;
|
|
457
|
+
isolation: isolate;
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
.chart-canvas::before {
|
|
461
|
+
content: "";
|
|
462
|
+
position: absolute;
|
|
463
|
+
inset: 0;
|
|
464
|
+
z-index: -1;
|
|
465
|
+
pointer-events: none;
|
|
466
|
+
background-image:
|
|
467
|
+
linear-gradient(var(--color-sidebar-border) 1px, transparent 1px),
|
|
468
|
+
linear-gradient(90deg, var(--color-sidebar-border) 1px, transparent 1px);
|
|
469
|
+
background-size:
|
|
470
|
+
46px 46px,
|
|
471
|
+
46px 46px;
|
|
472
|
+
opacity: 0.65;
|
|
473
|
+
-webkit-mask-image: radial-gradient(120% 90% at 50% 0%, #000 35%, transparent 100%);
|
|
474
|
+
mask-image: radial-gradient(120% 90% at 50% 0%, #000 35%, transparent 100%);
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
.dark .chart-canvas::before {
|
|
478
|
+
background-image:
|
|
479
|
+
radial-gradient(
|
|
480
|
+
900px 480px at 78% -8%,
|
|
481
|
+
color-mix(in oklab, var(--color-cta-accent) 12%, transparent),
|
|
482
|
+
transparent 60%
|
|
483
|
+
),
|
|
484
|
+
radial-gradient(820px 520px at 8% 4%, color-mix(in oklab, var(--color-primary) 12%, transparent), transparent 60%),
|
|
485
|
+
linear-gradient(var(--color-sidebar-border) 1px, transparent 1px),
|
|
486
|
+
linear-gradient(90deg, var(--color-sidebar-border) 1px, transparent 1px);
|
|
487
|
+
background-size:
|
|
488
|
+
100% 100%,
|
|
489
|
+
100% 100%,
|
|
490
|
+
46px 46px,
|
|
491
|
+
46px 46px;
|
|
492
|
+
opacity: 0.6;
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
496
|
+
.chart-rise {
|
|
497
|
+
animation: chart-rise 0.65s cubic-bezier(0.2, 0.7, 0.2, 1) both;
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
@keyframes chart-rise {
|
|
502
|
+
from {
|
|
503
|
+
opacity: 0;
|
|
504
|
+
transform: translateY(12px);
|
|
505
|
+
}
|
|
506
|
+
to {
|
|
507
|
+
opacity: 1;
|
|
508
|
+
transform: none;
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
|
|
449
512
|
/* ─── Typography ──────────────────────────────────────────────────────────── */
|
|
450
513
|
|
|
451
514
|
:root {
|