feat: Add dark theme charts ref:

This commit is contained in:
Janez T
2026-04-03 22:38:21 +02:00
parent d2fee94678
commit d66bbca82c
8 changed files with 73 additions and 35 deletions

View File

@@ -489,7 +489,7 @@
buildSettings = { buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = 130; CURRENT_PROJECT_VERSION = 131;
DEVELOPMENT_TEAM = JND55328G8; DEVELOPMENT_TEAM = JND55328G8;
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist; INFOPLIST_FILE = Runner/Info.plist;
@@ -511,7 +511,7 @@
buildSettings = { buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)"; BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 130; CURRENT_PROJECT_VERSION = 131;
DEVELOPMENT_TEAM = JND55328G8; DEVELOPMENT_TEAM = JND55328G8;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0; MARKETING_VERSION = 1.0;
@@ -530,7 +530,7 @@
buildSettings = { buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)"; BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 130; CURRENT_PROJECT_VERSION = 131;
DEVELOPMENT_TEAM = JND55328G8; DEVELOPMENT_TEAM = JND55328G8;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0; MARKETING_VERSION = 1.0;
@@ -547,7 +547,7 @@
buildSettings = { buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)"; BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 130; CURRENT_PROJECT_VERSION = 131;
DEVELOPMENT_TEAM = JND55328G8; DEVELOPMENT_TEAM = JND55328G8;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0; MARKETING_VERSION = 1.0;
@@ -679,7 +679,7 @@
buildSettings = { buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = 130; CURRENT_PROJECT_VERSION = 131;
DEVELOPMENT_TEAM = JND55328G8; DEVELOPMENT_TEAM = JND55328G8;
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist; INFOPLIST_FILE = Runner/Info.plist;
@@ -702,7 +702,7 @@
buildSettings = { buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = 130; CURRENT_PROJECT_VERSION = 131;
DEVELOPMENT_TEAM = JND55328G8; DEVELOPMENT_TEAM = JND55328G8;
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist; INFOPLIST_FILE = Runner/Info.plist;

View File

@@ -43,7 +43,7 @@
<key>CFBundleSignature</key> <key>CFBundleSignature</key>
<string>????</string> <string>????</string>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>130</string> <string>131</string>
<key>LSRequiresIPhoneOS</key> <key>LSRequiresIPhoneOS</key>
<true/> <true/>
<key>ITSAppUsesNonExemptEncryption</key> <key>ITSAppUsesNonExemptEncryption</key>

View File

@@ -16,7 +16,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
# In Windows, build-name is used as the major, minor, and patch parts # In Windows, build-name is used as the major, minor, and patch parts
# of the product and file versions while build-number is used as the build suffix. # of the product and file versions while build-number is used as the build suffix.
version: 2026.0402.1+44 version: 2026.0403.1+45
environment: environment:
sdk: ^3.9.2 sdk: ^3.9.2

View File

@@ -504,12 +504,12 @@ function CompositionChart({ points }: { points: CompositionPoint[] }) {
> >
<defs> <defs>
<linearGradient id="humanFill" x1="0" y1="0" x2="0" y2="1"> <linearGradient id="humanFill" x1="0" y1="0" x2="0" y2="1">
<stop offset="0%" stopColor="hsl(217, 91%, 60%)" stopOpacity={0.5} /> <stop offset="0%" stopColor="hsl(var(--chart-primary))" stopOpacity={0.5} />
<stop offset="100%" stopColor="hsl(217, 91%, 60%)" stopOpacity={0.1} /> <stop offset="100%" stopColor="hsl(var(--chart-primary))" stopOpacity={0.1} />
</linearGradient> </linearGradient>
<linearGradient id="overheadFill" x1="0" y1="0" x2="0" y2="1"> <linearGradient id="overheadFill" x1="0" y1="0" x2="0" y2="1">
<stop offset="0%" stopColor="hsl(206, 22%, 75%)" stopOpacity={0.5} /> <stop offset="0%" stopColor="hsl(var(--chart-secondary))" stopOpacity={0.5} />
<stop offset="100%" stopColor="hsl(206, 22%, 75%)" stopOpacity={0.1} /> <stop offset="100%" stopColor="hsl(var(--chart-secondary))" stopOpacity={0.1} />
</linearGradient> </linearGradient>
</defs> </defs>
@@ -517,21 +517,21 @@ function CompositionChart({ points }: { points: CompositionPoint[] }) {
const y = (1 - val / maxVal) * innerH; const y = (1 - val / maxVal) * innerH;
return ( return (
<g key={val}> <g key={val}>
<line x1={0} y1={y} x2={innerW} y2={y} stroke="hsl(206, 22%, 87%)" strokeWidth={0.3} /> <line x1={0} y1={y} x2={innerW} y2={y} stroke="hsl(var(--chart-grid))" strokeWidth={0.3} />
<text x={-6} y={y} textAnchor="end" dominantBaseline="middle" fill="hsl(208, 19%, 55%)" fontSize={3.2} fontFamily="var(--font-sans)"> <text x={-6} y={y} textAnchor="end" dominantBaseline="middle" fill="hsl(var(--chart-label))" fontSize={3.2} fontFamily="var(--font-sans)">
{formatCompact(val)} {formatCompact(val)}
</text> </text>
</g> </g>
); );
})} })}
<line x1={0} y1={innerH} x2={innerW} y2={innerH} stroke="hsl(206, 22%, 87%)" strokeWidth={0.4} /> <line x1={0} y1={innerH} x2={innerW} y2={innerH} stroke="hsl(var(--chart-grid))" strokeWidth={0.4} />
<path d={overheadArea} fill="url(#overheadFill)" /> <path d={overheadArea} fill="url(#overheadFill)" />
<path d={humanArea} fill="url(#humanFill)" /> <path d={humanArea} fill="url(#humanFill)" />
{/* X labels */} {/* X labels */}
{points.map((p, i) => (i % labelStep === 0 || i === count - 1) ? ( {points.map((p, i) => (i % labelStep === 0 || i === count - 1) ? (
<text key={i} x={xs[i] * innerW} y={innerH + 10} textAnchor="middle" fill="hsl(208, 19%, 55%)" fontSize={2.8} fontFamily="var(--font-sans)"> <text key={i} x={xs[i] * innerW} y={innerH + 10} textAnchor="middle" fill="hsl(var(--chart-label))" fontSize={2.8} fontFamily="var(--font-sans)">
{p.label.slice(5)} {p.label.slice(5)}
</text> </text>
) : null)} ) : null)}
@@ -550,7 +550,7 @@ function CompositionChart({ points }: { points: CompositionPoint[] }) {
))} ))}
{hover !== null && ( {hover !== null && (
<line x1={xs[hover] * innerW} y1={0} x2={xs[hover] * innerW} y2={innerH} stroke="hsl(217, 91%, 60%)" strokeWidth={0.3} strokeDasharray="1.5 1" /> <line x1={xs[hover] * innerW} y1={0} x2={xs[hover] * innerW} y2={innerH} stroke="hsl(var(--chart-primary))" strokeWidth={0.3} strokeDasharray="1.5 1" />
)} )}
</svg> </svg>
@@ -630,12 +630,12 @@ function TrafficChart({ points, maxValue: _rawMax }: { points: ChartPoint[]; max
> >
<defs> <defs>
<linearGradient id="areaFill" x1="0" y1="0" x2="0" y2="1"> <linearGradient id="areaFill" x1="0" y1="0" x2="0" y2="1">
<stop offset="0%" stopColor="hsl(217, 91%, 60%)" stopOpacity={0.35} /> <stop offset="0%" stopColor="hsl(var(--chart-primary))" stopOpacity={0.35} />
<stop offset="100%" stopColor="hsl(217, 91%, 60%)" stopOpacity={0.03} /> <stop offset="100%" stopColor="hsl(var(--chart-primary))" stopOpacity={0.03} />
</linearGradient> </linearGradient>
<linearGradient id="lineStroke" x1="0" y1="0" x2="1" y2="0"> <linearGradient id="lineStroke" x1="0" y1="0" x2="1" y2="0">
<stop offset="0%" stopColor="hsl(217, 91%, 65%)" /> <stop offset="0%" stopColor="hsl(var(--chart-primary-light))" />
<stop offset="100%" stopColor="hsl(217, 70%, 50%)" /> <stop offset="100%" stopColor="hsl(var(--chart-primary-dark))" />
</linearGradient> </linearGradient>
</defs> </defs>
@@ -644,8 +644,8 @@ function TrafficChart({ points, maxValue: _rawMax }: { points: ChartPoint[]; max
const y = (1 - val / maxValue) * innerH; const y = (1 - val / maxValue) * innerH;
return ( return (
<g key={val}> <g key={val}>
<line x1={0} y1={y} x2={innerW} y2={y} stroke="hsl(206, 22%, 87%)" strokeWidth={0.3} /> <line x1={0} y1={y} x2={innerW} y2={y} stroke="hsl(var(--chart-grid))" strokeWidth={0.3} />
<text x={-6} y={y} textAnchor="end" dominantBaseline="middle" fill="hsl(208, 19%, 55%)" fontSize={3.2} fontFamily="var(--font-sans)"> <text x={-6} y={y} textAnchor="end" dominantBaseline="middle" fill="hsl(var(--chart-label))" fontSize={3.2} fontFamily="var(--font-sans)">
{formatCompact(val)} {formatCompact(val)}
</text> </text>
</g> </g>
@@ -653,7 +653,7 @@ function TrafficChart({ points, maxValue: _rawMax }: { points: ChartPoint[]; max
})} })}
{/* baseline */} {/* baseline */}
<line x1={0} y1={innerH} x2={innerW} y2={innerH} stroke="hsl(206, 22%, 87%)" strokeWidth={0.4} /> <line x1={0} y1={innerH} x2={innerW} y2={innerH} stroke="hsl(var(--chart-grid))" strokeWidth={0.4} />
{/* area fill */} {/* area fill */}
<path d={areaPath} fill="url(#areaFill)" /> <path d={areaPath} fill="url(#areaFill)" />
@@ -666,7 +666,7 @@ function TrafficChart({ points, maxValue: _rawMax }: { points: ChartPoint[]; max
cx={xs[peakIdx] * innerW} cx={xs[peakIdx] * innerW}
cy={ys[peakIdx] * innerH} cy={ys[peakIdx] * innerH}
r={1.5} r={1.5}
fill="hsl(217, 91%, 60%)" fill="hsl(var(--chart-primary))"
stroke="white" stroke="white"
strokeWidth={0.6} strokeWidth={0.6}
/> />
@@ -676,7 +676,7 @@ function TrafficChart({ points, maxValue: _rawMax }: { points: ChartPoint[]; max
x={xs[peakIdx] * innerW} x={xs[peakIdx] * innerW}
y={ys[peakIdx] * innerH - 4} y={ys[peakIdx] * innerH - 4}
textAnchor="middle" textAnchor="middle"
fill="hsl(217, 91%, 45%)" fill="hsl(var(--chart-primary-accent))"
fontSize={3} fontSize={3}
fontWeight={600} fontWeight={600}
fontFamily="var(--font-sans)" fontFamily="var(--font-sans)"
@@ -691,7 +691,7 @@ function TrafficChart({ points, maxValue: _rawMax }: { points: ChartPoint[]; max
x={xs[i] * innerW} x={xs[i] * innerW}
y={innerH + 10} y={innerH + 10}
textAnchor="middle" textAnchor="middle"
fill="hsl(208, 19%, 55%)" fill="hsl(var(--chart-label))"
fontSize={2.8} fontSize={2.8}
fontFamily="var(--font-sans)" fontFamily="var(--font-sans)"
> >
@@ -723,7 +723,7 @@ function TrafficChart({ points, maxValue: _rawMax }: { points: ChartPoint[]; max
y1={0} y1={0}
x2={xs[hover] * innerW} x2={xs[hover] * innerW}
y2={innerH} y2={innerH}
stroke="hsl(217, 91%, 60%)" stroke="hsl(var(--chart-primary))"
strokeWidth={0.3} strokeWidth={0.3}
strokeDasharray="1.5 1" strokeDasharray="1.5 1"
/> />
@@ -732,7 +732,7 @@ function TrafficChart({ points, maxValue: _rawMax }: { points: ChartPoint[]; max
cy={ys[hover] * innerH} cy={ys[hover] * innerH}
r={1.2} r={1.2}
fill="white" fill="white"
stroke="hsl(217, 91%, 60%)" stroke="hsl(var(--chart-primary))"
strokeWidth={0.6} strokeWidth={0.6}
/> />
</> </>

View File

@@ -11,7 +11,7 @@ const badgeVariants = cva(
default: "border-transparent bg-primary text-primary-foreground", default: "border-transparent bg-primary text-primary-foreground",
secondary: "border-transparent bg-secondary text-secondary-foreground", secondary: "border-transparent bg-secondary text-secondary-foreground",
destructive: "border-transparent bg-destructive text-destructive-foreground", destructive: "border-transparent bg-destructive text-destructive-foreground",
outline: "text-foreground bg-white/60", outline: "text-foreground bg-card/60",
}, },
}, },
defaultVariants: { defaultVariants: {

View File

@@ -7,7 +7,7 @@ function Card({ className, ...props }: React.ComponentProps<"div">) {
<div <div
data-slot="card" data-slot="card"
className={cn( className={cn(
"bg-card/88 text-card-foreground flex flex-col gap-6 rounded-[1.5rem] border border-white/70 py-6 shadow-[0_18px_48px_rgba(16,33,47,0.08)] backdrop-blur-md", "bg-card/88 text-card-foreground flex flex-col gap-6 rounded-[1.5rem] border border-border/50 py-6 shadow-[0_18px_48px_rgba(16,33,47,0.08)] dark:shadow-[0_18px_48px_rgba(0,0,0,0.3)] backdrop-blur-md",
className, className,
)} )}
{...props} {...props}

View File

@@ -46,6 +46,40 @@
--input: 220 20% 88%; --input: 220 20% 88%;
--ring: 217 91% 60%; --ring: 217 91% 60%;
--radius: 1rem; --radius: 1rem;
--chart-grid: 220 20% 88%;
--chart-label: 220 13% 46%;
--chart-primary: 217 91% 60%;
--chart-primary-light: 217 91% 65%;
--chart-primary-dark: 217 70% 50%;
--chart-primary-accent: 217 91% 45%;
--chart-secondary: 206 22% 75%;
}
.dark {
--background: 222 47% 8%;
--foreground: 220 20% 90%;
--card: 222 40% 12%;
--card-foreground: 220 20% 90%;
--primary: 217 91% 60%;
--primary-foreground: 0 0% 100%;
--secondary: 220 30% 18%;
--secondary-foreground: 220 20% 90%;
--muted: 220 20% 16%;
--muted-foreground: 220 13% 55%;
--accent: 217 91% 60%;
--accent-foreground: 0 0% 100%;
--destructive: 0 72% 51%;
--destructive-foreground: 0 0% 100%;
--border: 220 20% 20%;
--input: 220 20% 20%;
--ring: 217 91% 60%;
--chart-grid: 220 20% 22%;
--chart-label: 220 13% 50%;
--chart-primary: 217 91% 65%;
--chart-primary-light: 217 91% 70%;
--chart-primary-dark: 217 70% 55%;
--chart-primary-accent: 217 91% 55%;
--chart-secondary: 220 20% 35%;
} }
* { * {
@@ -54,14 +88,12 @@
html { html {
font-family: var(--font-sans); font-family: var(--font-sans);
color-scheme: light dark;
} }
body { body {
min-height: 100vh; min-height: 100vh;
background: background: hsl(var(--background));
radial-gradient(circle at top left, hsl(217 91% 60% / 0.10), transparent 30%),
radial-gradient(circle at top right, hsl(217 70% 50% / 0.08), transparent 24%),
linear-gradient(180deg, #f5f8fc 0%, hsl(var(--background)) 100%);
color: hsl(var(--foreground)); color: hsl(var(--foreground));
} }
} }

View File

@@ -18,5 +18,11 @@ import "../index.css";
</head> </head>
<body> <body>
<slot /> <slot />
<script>
const mq = window.matchMedia("(prefers-color-scheme: dark)");
function apply(e) { document.documentElement.classList.toggle("dark", e.matches); }
apply(mq);
mq.addEventListener("change", apply);
</script>
</body> </body>
</html> </html>