Refine Dusk system layer and card spacing

This commit is contained in:
Janpeter Visser 2026-04-19 17:17:15 +02:00
parent 645d2b8b3b
commit 3170cfda18
24 changed files with 237 additions and 75 deletions

View file

@ -21,7 +21,7 @@ export default function CheckInLoading() {
</div>
<div className="grid gap-5 lg:grid-cols-[1.1fr_0.9fr]">
<Card className="py-0">
<Card className="pb-0">
<CardContent className="space-y-5 pb-6 pt-4">
<Skeleton className="h-4 w-32" />
<Skeleton className="h-8 w-full" />
@ -36,7 +36,7 @@ export default function CheckInLoading() {
</Card>
<div className="space-y-5">
<Card className="py-0">
<Card className="pb-0">
<CardHeader className="pb-0">
<Skeleton className="h-3 w-16" />
<Skeleton className="mt-1 h-5 w-48" />
@ -46,7 +46,7 @@ export default function CheckInLoading() {
<Skeleton className="mt-1.5 h-4 w-3/4" />
</CardContent>
</Card>
<Card className="py-0">
<Card className="pb-0">
<CardHeader className="pb-0">
<Skeleton className="h-3 w-24" />
</CardHeader>

View file

@ -76,7 +76,7 @@ export default async function CheckInPage({ searchParams }: CheckInPageProps) {
<CheckInForm todayCheckIn={checkInStatus?.todayCheckIn ?? null} />
<aside className="space-y-5">
<Card className="py-0">
<Card className="pb-0">
<CardHeader className="pb-0">
<p className="text-xs font-semibold uppercase tracking-[0.24em] text-muted-foreground">
Vandaag
@ -100,7 +100,7 @@ export default async function CheckInPage({ searchParams }: CheckInPageProps) {
</CardContent>
</Card>
<Card tone="primary" elevation="raised" className="py-0">
<Card tone="primary" elevation="raised" className="pb-0">
<CardHeader className="pb-0">
<p className="text-xs font-semibold uppercase tracking-[0.24em] text-primary-foreground/75">
Bewuste grens

View file

@ -22,7 +22,7 @@ export default function DashboardLoading() {
<div className="grid gap-5 md:grid-cols-3">
{Array.from({ length: 6 }).map((_, i) => (
<Card key={i} className="py-0">
<Card key={i} className="pb-0">
<CardHeader className="pb-0">
<Skeleton className="h-3 w-14" />
<Skeleton className="mt-1 h-5 w-40" />

View file

@ -98,7 +98,7 @@ export default async function DashboardPage({ searchParams }: DashboardPageProps
/>
<section className="grid gap-5 md:grid-cols-3">
<Card className="py-0">
<Card className="pb-0">
<CardHeader className="pb-0">
<p className="text-xs font-semibold uppercase tracking-[0.24em] text-muted-foreground">
Auth
@ -112,7 +112,7 @@ export default async function DashboardPage({ searchParams }: DashboardPageProps
</CardContent>
</Card>
<Card className="py-0">
<Card className="pb-0">
<CardHeader className="pb-0">
<p className="text-xs font-semibold uppercase tracking-[0.24em] text-muted-foreground">
Profiel
@ -145,7 +145,7 @@ export default async function DashboardPage({ searchParams }: DashboardPageProps
</CardContent>
</Card>
<Card className="py-0">
<Card className="pb-0">
<CardHeader className="pb-0">
<p className="text-xs font-semibold uppercase tracking-[0.24em] text-muted-foreground">
Onboarding
@ -160,7 +160,7 @@ export default async function DashboardPage({ searchParams }: DashboardPageProps
</CardContent>
</Card>
<Card className="py-0">
<Card className="pb-0">
<CardHeader className="pb-0">
<p className="text-xs font-semibold uppercase tracking-[0.24em] text-muted-foreground">
Instellingen
@ -179,7 +179,7 @@ export default async function DashboardPage({ searchParams }: DashboardPageProps
<CheckInCard todayCheckIn={checkInStatus?.todayCheckIn ?? null} />
<Card className="py-0">
<Card className="pb-0">
<CardHeader className="pb-0">
<p className="text-xs font-semibold uppercase tracking-[0.24em] text-muted-foreground">
Dagplanning
@ -205,7 +205,7 @@ export default async function DashboardPage({ searchParams }: DashboardPageProps
<EnergyMeterCard meter={planningMeter} tone="subtle" />
{isTestWizardEnabled() ? (
<Card className="py-0">
<Card className="pb-0">
<CardHeader className="pb-0">
<p className="text-xs font-semibold uppercase tracking-[0.24em] text-muted-foreground">
Wizard core
@ -222,7 +222,7 @@ export default async function DashboardPage({ searchParams }: DashboardPageProps
</section>
{!profile.onboardingCompleted ? (
<Card className="border-warning/32 bg-warning/16 py-0 text-foreground shadow-[var(--shadow-1)]">
<Card className="border-warning/32 bg-warning/16 pb-0 text-foreground shadow-[var(--shadow-1)]">
<CardContent className="flex flex-col gap-4 px-6 py-5 sm:flex-row sm:items-center sm:justify-between">
<div>
<p className="font-semibold">Je onboarding is nog niet afgerond.</p>
@ -237,7 +237,7 @@ export default async function DashboardPage({ searchParams }: DashboardPageProps
</CardContent>
</Card>
) : (
<Card tone="primary" elevation="raised" className="py-0">
<Card tone="primary" elevation="raised" className="pb-0">
<CardContent className="flex flex-col gap-4 px-6 py-5 sm:flex-row sm:items-center sm:justify-between">
<div>
<p className="font-semibold">Je instellingen kun je nu ook los beheren.</p>

View file

@ -8,6 +8,28 @@
--font-body: var(--font-inter-tight), ui-sans-serif, system-ui, sans-serif;
--font-display: var(--font-body);
--font-mono: var(--font-plex-mono), ui-monospace, monospace;
--typescale-display-large-size: clamp(2.625rem, 5vw, 3.5625rem);
--typescale-display-large-line-height: 1.08;
--typescale-display-medium-size: clamp(2.125rem, 4vw, 2.8125rem);
--typescale-display-medium-line-height: 1.12;
--typescale-headline-large-size: clamp(1.375rem, 3vw, 2rem);
--typescale-headline-large-line-height: 1.2;
--typescale-headline-medium-size: 1.375rem;
--typescale-headline-medium-line-height: 1.2;
--typescale-title-large-size: 1rem;
--typescale-title-large-line-height: 1.3;
--typescale-title-medium-size: 0.875rem;
--typescale-title-medium-line-height: 1.3;
--typescale-body-large-size: 1rem;
--typescale-body-large-line-height: 1.65;
--typescale-body-medium-size: 0.875rem;
--typescale-body-medium-line-height: 1.65;
--typescale-label-large-size: 0.875rem;
--typescale-label-large-line-height: 1.4;
--typescale-label-medium-size: 0.75rem;
--typescale-label-medium-line-height: 1.4;
--typescale-label-small-size: 0.6875rem;
--typescale-label-small-line-height: 1.4;
--background: oklch(97% 0.008 80);
--foreground: oklch(22% 0.03 262);
@ -18,12 +40,24 @@
--primary: oklch(44% 0.11 262);
--primary-foreground: oklch(98% 0.01 262);
--primary-container: oklch(90% 0.03 262);
--primary-container-foreground: oklch(28% 0.06 262);
--secondary: oklch(92% 0.03 262);
--secondary-foreground: oklch(44% 0.11 262);
--secondary-container: oklch(94% 0.018 262);
--secondary-container-foreground: oklch(32% 0.05 262);
--tertiary: oklch(56% 0.09 200);
--tertiary-foreground: oklch(98% 0.006 200);
--tertiary-container: oklch(91% 0.025 200);
--tertiary-container-foreground: oklch(29% 0.055 200);
--muted: oklch(95% 0.012 82);
--muted-foreground: oklch(58% 0.015 262);
--accent: oklch(92% 0.03 262);
--accent-foreground: oklch(44% 0.11 262);
--surface-container-low: oklch(98.5% 0.004 80);
--surface-container: oklch(97.4% 0.007 80);
--surface-container-high: oklch(95.8% 0.012 80);
--outline-variant: oklch(76% 0.012 262 / 0.4);
--destructive: oklch(58% 0.16 25);
--success: oklch(62% 0.09 155);
@ -91,6 +125,18 @@ a {
--color-chart-1: var(--chart-1);
--color-warning: var(--warning);
--color-success: var(--success);
--color-outline-variant: var(--outline-variant);
--color-surface-container-high: var(--surface-container-high);
--color-surface-container: var(--surface-container);
--color-surface-container-low: var(--surface-container-low);
--color-tertiary-container-foreground: var(--tertiary-container-foreground);
--color-tertiary-container: var(--tertiary-container);
--color-tertiary-foreground: var(--tertiary-foreground);
--color-tertiary: var(--tertiary);
--color-secondary-container-foreground: var(--secondary-container-foreground);
--color-secondary-container: var(--secondary-container);
--color-primary-container-foreground: var(--primary-container-foreground);
--color-primary-container: var(--primary-container);
--color-ring: var(--ring);
--color-input: var(--input);
--color-border: var(--border);
@ -110,8 +156,9 @@ a {
--color-foreground: var(--foreground);
--color-background: var(--background);
--font-mono: var(--font-plex-mono), ui-monospace, monospace;
--radius-xs: 4px;
--radius-sm: 8px;
--radius-md: 11px;
--radius-md: 12px;
--radius-lg: var(--radius);
--radius-xl: 21px;
--radius-2xl: 25px;
@ -130,12 +177,24 @@ a {
--primary: oklch(52% 0.13 262);
--primary-foreground: oklch(98% 0.01 262);
--primary-container: oklch(30% 0.06 262);
--primary-container-foreground: oklch(94% 0.012 262);
--secondary: oklch(28% 0.03 262);
--secondary-foreground: oklch(96% 0.008 80);
--secondary-container: oklch(31% 0.028 262);
--secondary-container-foreground: oklch(93% 0.01 262);
--tertiary: oklch(68% 0.09 200);
--tertiary-foreground: oklch(16% 0.025 200);
--tertiary-container: oklch(32% 0.05 200);
--tertiary-container-foreground: oklch(94% 0.012 200);
--muted: oklch(26% 0.025 262);
--muted-foreground: oklch(70% 0.015 262);
--accent: oklch(30% 0.04 262);
--accent-foreground: oklch(96% 0.008 80);
--surface-container-low: oklch(20% 0.022 262);
--surface-container: oklch(23% 0.024 262);
--surface-container-high: oklch(27% 0.026 262);
--outline-variant: oklch(68% 0.014 262 / 0.36);
--destructive: oklch(70% 0.16 25);
--success: oklch(74% 0.09 155);
@ -259,6 +318,109 @@ a {
@apply transition hover:text-foreground;
}
.type-display-large {
font-size: var(--typescale-display-large-size);
line-height: var(--typescale-display-large-line-height);
font-weight: 400;
letter-spacing: -0.03em;
}
.type-display-medium {
font-size: var(--typescale-display-medium-size);
line-height: var(--typescale-display-medium-line-height);
font-weight: 400;
letter-spacing: -0.03em;
}
.type-headline-large {
font-size: var(--typescale-headline-large-size);
line-height: var(--typescale-headline-large-line-height);
font-weight: 600;
letter-spacing: -0.02em;
}
.type-headline-medium {
font-size: var(--typescale-headline-medium-size);
line-height: var(--typescale-headline-medium-line-height);
font-weight: 600;
letter-spacing: -0.02em;
}
.type-title-large {
font-size: var(--typescale-title-large-size);
line-height: var(--typescale-title-large-line-height);
font-weight: 600;
letter-spacing: -0.01em;
}
.type-title-medium {
font-size: var(--typescale-title-medium-size);
line-height: var(--typescale-title-medium-line-height);
font-weight: 500;
}
.type-body-large {
font-size: var(--typescale-body-large-size);
line-height: var(--typescale-body-large-line-height);
font-weight: 400;
}
.type-body-medium {
font-size: var(--typescale-body-medium-size);
line-height: var(--typescale-body-medium-line-height);
font-weight: 400;
}
.type-label-large {
font-size: var(--typescale-label-large-size);
line-height: var(--typescale-label-large-line-height);
font-weight: 500;
}
.type-label-medium {
font-size: var(--typescale-label-medium-size);
line-height: var(--typescale-label-medium-line-height);
font-weight: 500;
}
.type-label-small {
font-size: var(--typescale-label-small-size);
line-height: var(--typescale-label-small-line-height);
font-weight: 600;
letter-spacing: 0.16em;
text-transform: uppercase;
}
.state-layer {
--state-layer-opacity: 0;
background-image: linear-gradient(
color-mix(in oklab, currentColor calc(var(--state-layer-opacity) * 100%), transparent),
color-mix(in oklab, currentColor calc(var(--state-layer-opacity) * 100%), transparent)
);
background-repeat: no-repeat;
background-size: 100% 100%;
transition:
background-color 160ms ease,
border-color 160ms ease,
box-shadow 160ms ease,
color 160ms ease,
transform 150ms ease;
}
.state-layer:hover {
--state-layer-opacity: 0.08;
}
.state-layer:focus-visible,
.state-layer:active {
--state-layer-opacity: 0.12;
}
.state-layer:disabled,
.state-layer[data-disabled] {
--state-layer-opacity: 0;
}
.app-page-copy {
@apply mt-4 max-w-2xl text-base leading-8 text-muted-foreground;
}

View file

@ -74,7 +74,7 @@ export default async function Home({ searchParams }: HomePageProps) {
/>
<section className="grid gap-6 lg:grid-cols-[1.05fr_0.95fr]">
<Card elevation="raised" className="py-0">
<Card elevation="raised" className="pb-0">
<CardContent className="p-6 sm:p-8">
<p className="mb-5 max-w-2xl text-lg leading-8 text-muted-foreground">
Deze app wordt ontwikkeld door Jan Peter Visser als compacte dagtool
@ -84,7 +84,7 @@ export default async function Home({ searchParams }: HomePageProps) {
</p>
<div className="grid gap-4">
{makerNotes.map((note) => (
<Card key={note} tone="subtle" className="py-0 shadow-none">
<Card key={note} tone="subtle" className="pb-0 shadow-none">
<CardContent className="px-5 py-4 text-sm leading-7 text-muted-foreground">
{note}
</CardContent>
@ -94,7 +94,7 @@ export default async function Home({ searchParams }: HomePageProps) {
</CardContent>
</Card>
<Card tone="primary" elevation="raised" className="py-0">
<Card tone="primary" elevation="raised" className="pb-0">
<CardHeader className="px-6 pt-7 sm:px-8">
<p className="text-xs font-semibold uppercase tracking-[0.24em] text-primary-foreground/75">
Specificaties van de app
@ -104,7 +104,7 @@ export default async function Home({ searchParams }: HomePageProps) {
{appSpecs.map((item) => (
<Card
key={item}
className="rounded-[var(--radius-2xl)] border-white/10 bg-white/8 py-0 text-primary-foreground shadow-none"
className="rounded-[var(--radius-2xl)] border-white/10 bg-white/8 pb-0 text-primary-foreground shadow-none"
>
<CardContent className="px-4 py-3 text-sm leading-7">{item}</CardContent>
</Card>
@ -117,7 +117,7 @@ export default async function Home({ searchParams }: HomePageProps) {
</Card>
</section>
<Card elevation="raised" className="py-0">
<Card elevation="raised" className="pb-0">
<CardHeader className="pb-0">
<p className="text-xs font-semibold uppercase tracking-[0.24em] text-muted-foreground">
Dagflow
@ -126,7 +126,7 @@ export default async function Home({ searchParams }: HomePageProps) {
</CardHeader>
<CardContent className="grid gap-5 p-6 md:grid-cols-3">
{productLoop.map((step, index) => (
<Card key={step.title} tone="subtle" className="py-0 shadow-none">
<Card key={step.title} tone="subtle" className="pb-0 shadow-none">
<CardHeader className="pb-0">
<p className="text-xs font-semibold uppercase tracking-[0.22em] text-muted-foreground">
Stap {index + 1}
@ -145,7 +145,7 @@ export default async function Home({ searchParams }: HomePageProps) {
</CardContent>
</Card>
<Card tone="subtle" className="py-0 backdrop-blur">
<Card tone="subtle" className="pb-0 backdrop-blur">
<CardContent className="grid gap-5 p-6 sm:grid-cols-2 lg:grid-cols-4">
<div>
<p className="text-xs font-semibold uppercase tracking-[0.24em] text-muted-foreground">

View file

@ -21,7 +21,7 @@ export default function PlanningLoading() {
</div>
<div className="grid gap-5 lg:grid-cols-[1.1fr_0.9fr]">
<Card className="py-0">
<Card className="pb-0">
<CardContent className="space-y-4 pb-6 pt-4">
<Skeleton className="h-4 w-32" />
<Skeleton className="h-10 w-full" />
@ -36,7 +36,7 @@ export default function PlanningLoading() {
</Card>
<div className="space-y-5">
<Card className="py-0">
<Card className="pb-0">
<CardHeader className="pb-0">
<Skeleton className="h-3 w-16" />
<Skeleton className="mt-1 h-5 w-48" />
@ -46,14 +46,14 @@ export default function PlanningLoading() {
<Skeleton className="mt-1.5 h-4 w-3/4" />
</CardContent>
</Card>
<Card className="py-0">
<Card className="pb-0">
<CardContent className="space-y-3 pb-6 pt-4">
<Skeleton className="h-4 w-32" />
<Skeleton className="h-5 w-full rounded-full" />
<Skeleton className="h-3 w-24" />
</CardContent>
</Card>
<Card className="py-0">
<Card className="pb-0">
<CardHeader className="pb-0">
<Skeleton className="h-3 w-24" />
</CardHeader>
@ -69,7 +69,7 @@ export default function PlanningLoading() {
<div className="space-y-3">
<Skeleton className="h-5 w-48" />
{[0, 1, 2].map((i) => (
<Card key={i} className="py-0">
<Card key={i} className="pb-0">
<CardContent className="flex items-center justify-between px-4 py-3">
<div className="space-y-1.5">
<Skeleton className="h-4 w-48" />

View file

@ -106,7 +106,7 @@ export default async function PlanningPage({ searchParams }: PlanningPageProps)
</div>
<aside className="space-y-5">
<Card className="py-0">
<Card className="pb-0">
<CardHeader className="pb-0">
<p className="text-xs font-semibold uppercase tracking-[0.24em] text-muted-foreground">
Vandaag
@ -139,7 +139,7 @@ export default async function PlanningPage({ searchParams }: PlanningPageProps)
<EnergyMeterCard meter={planningMeter} />
<Card tone="primary" elevation="raised" className="py-0">
<Card tone="primary" elevation="raised" className="pb-0">
<CardHeader className="pb-0">
<p className="text-xs font-semibold uppercase tracking-[0.24em] text-primary-foreground/75">
Bewuste grens

View file

@ -79,7 +79,7 @@ export default async function SettingsPage({ searchParams }: SettingsPageProps)
<SettingsForm profileBundle={profileBundle} />
<aside className="space-y-5">
<Card className="py-0">
<Card className="pb-0">
<CardHeader className="pb-0">
<p className="text-xs font-semibold uppercase tracking-[0.24em] text-muted-foreground">
Account
@ -111,7 +111,7 @@ export default async function SettingsPage({ searchParams }: SettingsPageProps)
</CardContent>
</Card>
<Card className="py-0">
<Card className="pb-0">
<CardHeader className="pb-0">
<p className="text-xs font-semibold uppercase tracking-[0.24em] text-muted-foreground">
Huidige status