Ein Raster fuer alle acht Kacheln, nicht drei nebeneinandergestellte
Gleich breit und gleich weit auseinander werden Kacheln erst, wenn sie demselben Raster angehoeren. Die beiden Anlaeufe davor stellten drei eigene Raster nebeneinander: zuerst mit gleichem Anteil je Gruppe (fuenf Kacheln gequetscht, eine mit demselben Platz), dann mit einem Anteil nach Kachelzahl. Da stimmten die Breiten fast — aber eben nur fast: eine Gruppe mit fuenf Kacheln hat vier Abstaende in sich, eine mit einer keinen, und dieser Unterschied verteilt sich auf die Breiten. Jetzt acht gleiche Spalten und ein einziger Abstandswert. Die Ueberschriften sitzen in Zeile 1 und ueberspannen die Spalten ihrer Gruppe (5, 2, 1), die Kacheln in Zeile 2. Beide Zeilen entstehen aus derselben DOM-Reihenfolge — Ueberschrift, ihre Kacheln, naechste Ueberschrift —, die fuer Vorlesegeraete die richtige ist; die ausdrueckliche Zeilenangabe sortiert sie fuers Auge. Die Spannweite steht ausgeschrieben in einer Tabelle und nicht als span var(--kacheln): Tailwind erzeugt nur Klassen, die als Zeichenkette im Quelltext stehen, und eine berechnete Spannweite waere zur Bauzeit nicht zu sehen. Unterhalb der grossen Breite keine Zeilenangabe: dann fliesst alles der Reihe nach, Ueberschrift ueber die volle Breite, ihre Kacheln zu zweit darunter.
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import { ChevronRight } from "lucide-react";
|
||||
import Link from "next/link";
|
||||
import { Suspense, type CSSProperties } from "react";
|
||||
import { Fragment, Suspense } from "react";
|
||||
import { AnstehendFilter } from "@/components/dashboard/AnstehendFilter";
|
||||
import { AnstehendListe, type AnstehendEintrag } from "@/components/dashboard/AnstehendListe";
|
||||
import { DraftsCard } from "@/components/dashboard/DraftsCard";
|
||||
@@ -31,6 +31,28 @@ const TONE: Record<string, { text: string; bar: string }> = {
|
||||
/** Eine Kennzahl auf der Übersicht. */
|
||||
type Kachel = { label: string; value: string | number; tone: keyof typeof TONE; href: string };
|
||||
|
||||
/**
|
||||
* Wie viele Spalten die Überschrift einer Gruppe überspannt — nach der Zahl
|
||||
* ihrer Kacheln.
|
||||
*
|
||||
* Ausgeschrieben und nicht als `span var(--kacheln)`: Tailwind erzeugt nur
|
||||
* Klassen, die als Zeichenkette im Quelltext stehen, und eine aus einer
|
||||
* Variablen berechnete Spannweite wäre zur Bauzeit nicht zu sehen. Die
|
||||
* Tabelle deckt jede mögliche Gruppengrösse bis zur Rasterbreite ab; ein
|
||||
* Zugriff daneben gäbe `undefined` und damit eine Überschrift über einer
|
||||
* einzigen Spalte — auffällig genug, um bemerkt zu werden.
|
||||
*/
|
||||
const SPALTEN_SPAN: Record<number, string> = {
|
||||
1: "xl:col-span-1",
|
||||
2: "xl:col-span-2",
|
||||
3: "xl:col-span-3",
|
||||
4: "xl:col-span-4",
|
||||
5: "xl:col-span-5",
|
||||
6: "xl:col-span-6",
|
||||
7: "xl:col-span-7",
|
||||
8: "xl:col-span-8",
|
||||
};
|
||||
|
||||
// Die Tabelle, die hier stand, ist nach lib/colors.ts gewandert: Punkt und
|
||||
// Chip derselben Zeile kamen aus zwei getrennten Verzeichnissen, und eines
|
||||
// davon war das falsche. Jetzt leiten beide aus EVENT_CATEGORY ab und koennen
|
||||
@@ -224,58 +246,66 @@ export default async function DashboardPage({
|
||||
return (
|
||||
<div className="flex flex-col gap-6">
|
||||
{drafts.length > 0 && <DraftsCard drafts={drafts} />}
|
||||
{/* ── Eine Reihe, drei Gruppen ──────────────────────────────────
|
||||
Ab der grossen Breite stehen alle acht Kacheln nebeneinander, die
|
||||
Überschrift jeweils über den ihren.
|
||||
{/* ── Ein Raster, nicht drei nebeneinandergestellte ──────────────
|
||||
Alle acht Kacheln liegen in **einem** Raster mit acht gleichen
|
||||
Spalten und einem einzigen Abstandswert. Nur so sind sie gleich
|
||||
breit und gleich weit auseinander — unabhängig davon, zu welcher
|
||||
Gruppe sie gehören.
|
||||
|
||||
Die Breite einer Gruppe folgt der **Zahl ihrer Kacheln**: fünf, zwei,
|
||||
eine. Der erste Versuch gab jeder Gruppe denselben Anteil (`flex-1`)
|
||||
— dann quetschten sich fünf Kacheln in ein Drittel und brachen um,
|
||||
während die einzelne rechts dasselbe Drittel für sich hatte.
|
||||
Die beiden Anläufe davor stellten drei eigene Raster nebeneinander.
|
||||
Das ging zweimal schief: erst bekam jede Gruppe ein Drittel der
|
||||
Breite (fünf Kacheln gequetscht, eine mit demselben Platz), dann
|
||||
einen Anteil nach Kachelzahl — da stimmten die Breiten fast, aber
|
||||
eben nur fast: eine Gruppe mit fünf Kacheln hat vier Abstände in
|
||||
sich, eine mit einer keinen, und dieser Unterschied verteilt sich
|
||||
auf die Kachelbreiten. Gleichmässig wird es erst, wenn alle Kacheln
|
||||
demselben Raster angehören.
|
||||
|
||||
Die Zahl geht als CSS-Variable in beide Regeln, statt als
|
||||
ausgeschriebene Klasse je Gruppe. Käme eine neunte Kachel dazu,
|
||||
stimmte die Aufteilung von selbst; mit `xl:flex-[5]` von Hand müsste
|
||||
jemand daran denken, und es fiele nicht auf, wenn er es nicht tut.
|
||||
Die Überschriften sitzen in Zeile 1 und überspannen die Spalten
|
||||
ihrer Gruppe, die Kacheln in Zeile 2. Beide Zeilen entstehen aus
|
||||
derselben DOM-Reihenfolge (Überschrift, ihre Kacheln, nächste
|
||||
Überschrift …) — die ist für Vorlesegeräte die richtige, und die
|
||||
ausdrückliche Zeilenangabe sortiert sie fürs Auge.
|
||||
|
||||
Darunter untereinander, je Gruppe zwei Kacheln nebeneinander — in
|
||||
einer Reihe wären acht Kacheln auf einem Laptop unlesbar schmal. */}
|
||||
<div className="flex flex-col gap-4 xl:flex-row xl:items-start xl:gap-5">
|
||||
Unterhalb der grossen Breite gibt es keine Zeilenangabe: dann fliesst
|
||||
alles der Reihe nach, die Überschrift über die volle Breite und ihre
|
||||
Kacheln zu zweit darunter. Acht nebeneinander wären auf einem Laptop
|
||||
unlesbar schmal. */}
|
||||
<div className="grid grid-cols-2 gap-x-3 gap-y-3 xl:grid-cols-8 xl:gap-y-1.5">
|
||||
{kpiGruppen.map((gruppe) => (
|
||||
<section
|
||||
key={gruppe.titel}
|
||||
className="min-w-0 xl:[flex:var(--kacheln)_1_0%]"
|
||||
style={{ "--kacheln": gruppe.kacheln.length } as CSSProperties}
|
||||
>
|
||||
<h2 className="mb-1.5 text-[11px] font-bold uppercase tracking-wider text-ink-muted">{gruppe.titel}</h2>
|
||||
<div className="grid grid-cols-2 gap-3 xl:[grid-template-columns:repeat(var(--kacheln),minmax(0,1fr))]">
|
||||
{gruppe.kacheln.map((kpi) => (
|
||||
<Link
|
||||
key={kpi.label}
|
||||
href={kpi.href}
|
||||
className={`${CARD_CLASS} group relative overflow-hidden p-4 pl-5 transition-shadow hover:shadow-[var(--shadow-card-hover)] focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-brand-500`}
|
||||
>
|
||||
<span className={`absolute inset-y-0 left-0 w-1 ${TONE[kpi.tone].bar}`} aria-hidden />
|
||||
{/* Number first in the visual order: it is what the eye is
|
||||
looking for, and the label only qualifies it. */}
|
||||
{/* Kleiner, sobald alle acht in einer Reihe stehen: dort
|
||||
bleiben je Kachel gut 110 px, und „748.4" braucht in
|
||||
30 px Schriftgrad mehr davon, als nach dem Innenabstand
|
||||
übrig ist. Die Kachel hat `overflow-hidden` — die Zahl
|
||||
wäre also nicht zu breit, sondern abgeschnitten, und
|
||||
eine abgeschnittene Kennzahl ist schlimmer als eine
|
||||
kleine. Darunter stehen zwei je Reihe, da ist Platz. */}
|
||||
<div className={`text-3xl font-extrabold leading-none tabular-nums xl:text-2xl ${TONE[kpi.tone].text}`}>
|
||||
{kpi.value}
|
||||
</div>
|
||||
<div className="mt-1.5 flex items-center gap-1 text-xs font-semibold leading-tight text-ink-muted">
|
||||
{kpi.label}
|
||||
<ChevronRight className="h-3 w-3 shrink-0 opacity-0 transition-opacity group-hover:opacity-100" aria-hidden />
|
||||
</div>
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
<Fragment key={gruppe.titel}>
|
||||
<h2
|
||||
className={`mt-2 self-end text-[11px] font-bold uppercase tracking-wider text-ink-muted
|
||||
col-span-2 xl:mt-0 xl:[grid-row:1] ${SPALTEN_SPAN[gruppe.kacheln.length]}`}
|
||||
>
|
||||
{gruppe.titel}
|
||||
</h2>
|
||||
{gruppe.kacheln.map((kpi) => (
|
||||
<Link
|
||||
key={kpi.label}
|
||||
href={kpi.href}
|
||||
className={`${CARD_CLASS} group relative overflow-hidden p-4 pl-5 transition-shadow xl:[grid-row:2] hover:shadow-[var(--shadow-card-hover)] focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-brand-500`}
|
||||
>
|
||||
<span className={`absolute inset-y-0 left-0 w-1 ${TONE[kpi.tone].bar}`} aria-hidden />
|
||||
{/* Number first in the visual order: it is what the eye is
|
||||
looking for, and the label only qualifies it.
|
||||
|
||||
Kleiner, sobald alle acht in einer Reihe stehen: dort
|
||||
bleiben je Kachel gut 110 px, und „748.4" braucht in 30 px
|
||||
Schriftgrad mehr davon, als nach dem Innenabstand übrig
|
||||
ist. Die Kachel hat `overflow-hidden` — die Zahl wäre also
|
||||
nicht zu breit, sondern abgeschnitten, und eine
|
||||
abgeschnittene Kennzahl ist schlimmer als eine kleine. */}
|
||||
<div className={`text-3xl font-extrabold leading-none tabular-nums xl:text-2xl ${TONE[kpi.tone].text}`}>
|
||||
{kpi.value}
|
||||
</div>
|
||||
<div className="mt-1.5 flex items-center gap-1 text-xs font-semibold leading-tight text-ink-muted">
|
||||
{kpi.label}
|
||||
<ChevronRight className="h-3 w-3 shrink-0 opacity-0 transition-opacity group-hover:opacity-100" aria-hidden />
|
||||
</div>
|
||||
</Link>
|
||||
))}
|
||||
</Fragment>
|
||||
))}
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user