feat(web): Dashboard-Kacheln im Fluent-Stil und waehlbarer Hintergrund

Einheitliche Kachel-Kopfzeile, ruhigere Notiz-/Kalender-/Favoriten-
Darstellung, 12 px Raster-Abstand, gestrichelte Kontur und Griff im
Bearbeitungsmodus. Neuer Knopf Hintergrund: Keiner, fuenf eingebaute
(Nebel, Salbei, Bluete, Duenen, Mosaik) oder eigenes Bild aus den
Bilderrahmen-Bildern; Prototyp speichert im localStorage je Benutzer.
Mit Hintergrund werden Kacheln Mica-artig durchscheinend (Kontrast
>= 4,5:1 auch im schlechtesten Fall).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-09-25 14:49:01 +02:00
parent 522cdfd368
commit 219038b3ee
17 changed files with 521 additions and 43 deletions
@@ -243,21 +243,21 @@ export function CalendarWidget({ config }: WidgetProps) {
const hoverEvents = hover ? (eventsByDate.get(hover.key) ?? []) : [];
return (
<div className="flex h-full flex-col gap-1 overflow-hidden p-1.5">
<div className="flex h-full flex-col gap-2 overflow-hidden p-3">
{showMonth && (
<div data-testid="calendar-month" className="flex shrink-0 flex-col gap-1">
{/* Nav-Zeile */}
<div className="grid grid-cols-[1fr_1.4fr_1fr] gap-1">
<button
type="button"
className="widgetNoDrag rounded border border-border bg-muted/50 px-1 py-[clamp(2px,0.8cqh,6px)] text-[clamp(10px,2.6cqw,13px)] leading-none text-foreground hover:bg-muted"
className="widgetNoDrag rounded-md px-1 py-[clamp(3px,0.9cqh,6px)] text-[clamp(10px,2.6cqw,13px)] leading-none text-muted-foreground transition-colors hover:bg-muted hover:text-foreground"
onClick={showPrev}
>
{t('calendar.monthPrev')}
</button>
<button
type="button"
className="widgetNoDrag truncate rounded border border-border bg-muted/50 px-1 py-[clamp(2px,0.8cqh,6px)] text-[clamp(10px,2.6cqw,13px)] font-semibold leading-none text-foreground hover:bg-muted"
className="widgetNoDrag truncate rounded-md px-1 py-[clamp(3px,0.9cqh,6px)] text-[clamp(11px,2.8cqw,14px)] font-semibold leading-none text-foreground transition-colors hover:bg-muted"
title={t('calendar.monthToday')}
onClick={showToday}
>
@@ -265,7 +265,7 @@ export function CalendarWidget({ config }: WidgetProps) {
</button>
<button
type="button"
className="widgetNoDrag rounded border border-border bg-muted/50 px-1 py-[clamp(2px,0.8cqh,6px)] text-[clamp(10px,2.6cqw,13px)] leading-none text-foreground hover:bg-muted"
className="widgetNoDrag rounded-md px-1 py-[clamp(3px,0.9cqh,6px)] text-[clamp(10px,2.6cqw,13px)] leading-none text-muted-foreground transition-colors hover:bg-muted hover:text-foreground"
onClick={showNext}
>
{t('calendar.monthNext')}
@@ -286,9 +286,9 @@ export function CalendarWidget({ config }: WidgetProps) {
const badges = buildDayBadges(day.events);
const single = badges.length === 1;
const cellClass = [
'widgetNoDrag relative flex min-h-[clamp(16px,5.5cqh,40px)] items-start rounded bg-muted/50 px-1 py-0.5 text-[clamp(9px,2.4cqw,13px)] leading-none',
day.inCurrentMonth ? 'text-foreground' : 'text-muted-foreground/60',
day.isToday ? 'ring-1 ring-primary font-semibold text-primary' : '',
'widgetNoDrag relative flex min-h-[clamp(16px,5.5cqh,40px)] items-start rounded-md px-1 py-0.5 text-[clamp(9px,2.4cqw,13px)] leading-none',
day.inCurrentMonth ? 'text-foreground' : 'text-muted-foreground/70',
day.isToday ? 'bg-primary font-semibold text-primary-foreground' : '',
hasEvents ? 'cursor-default hover:bg-muted' : '',
]
.filter(Boolean)
@@ -405,7 +405,7 @@ export function CalendarWidget({ config }: WidgetProps) {
) : (
<ul data-testid="calendar-upcoming" className="min-h-0 flex-1 space-y-1 overflow-y-auto">
{upcoming.map((event) => (
<li key={event.id} className="flex items-start gap-2 rounded bg-muted/50 px-2 py-1">
<li key={event.id} className="flex items-start gap-2 rounded-md px-2 py-1 hover:bg-muted">
<span
data-testid="event-color-dot"
className="mt-1 h-2 w-2 shrink-0 rounded-full"
@@ -416,7 +416,7 @@ export function CalendarWidget({ config }: WidgetProps) {
<p className="truncate text-[clamp(9px,2.2cqw,12px)] text-muted-foreground">
{formatEventDate(event)}
</p>
<p className="truncate text-[clamp(10px,2.5cqw,14px)] font-semibold text-foreground">
<p className="truncate text-[clamp(10px,2.5cqw,14px)] font-medium text-foreground">
{event.title}
</p>
{event.location && (
@@ -438,7 +438,7 @@ export function CalendarWidget({ config }: WidgetProps) {
<div
data-testid="calendar-day-tooltip"
role="tooltip"
className="pointer-events-none fixed z-50 rounded border border-border bg-card p-2 text-xs text-foreground shadow-lg"
className="pointer-events-none fixed z-50 rounded bg-card dark:border dark:border-border p-2 text-xs text-foreground shadow-lg"
style={{
width: TOOLTIP_WIDTH_PX,
top: hover.rect.bottom + TOOLTIP_EDGE_PX,