style(web): dunkle App-Leiste und Fluent-Seitenleiste
Kopfzeile 48 px dunkel mit Seitentitel statt immer Startseite, Dashboard-Reiter mit Akzent-Unterstrich; Seitenleiste mit Auswahlpille, Modul-Kacheln, aufgeklappten Kategorien und gefuelltem Suchfeld; eingeklappt nur Kacheln. Einheitlicher PageHeader. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -312,18 +312,20 @@ export function DashboardTabs({
|
||||
const bar = (
|
||||
<nav
|
||||
aria-label={t('tabs.navLabel')}
|
||||
className="flex min-w-0 max-w-full items-center gap-1.5"
|
||||
className="flex h-[var(--header-height)] min-w-0 max-w-full items-center gap-1.5"
|
||||
title={dashboards.length > 1 ? t('tabs.dragHint') : undefined}
|
||||
>
|
||||
{/* Eingelassene Spur (32 px): der aktive Reiter liegt erhaben darauf. */}
|
||||
<div className="h-8 min-w-0 max-w-full rounded-lg bg-muted p-0.5 shadow-[inset_0_1px_2px_oklch(0_0_0/0.08)] md:max-w-[min(56vw,720px)]">
|
||||
{/* Reiter im Fluent-Stil auf der dunklen App-Leiste (Design „Mosaik“):
|
||||
volle Leistenhoehe, der aktive Reiter weiss und halbfett mit 2 px
|
||||
Akzentlinie an der Unterkante der Leiste. */}
|
||||
<div className="h-[var(--header-height)] min-w-0 max-w-full md:max-w-[min(56vw,720px)]">
|
||||
<div
|
||||
ref={scrollRef}
|
||||
role="tablist"
|
||||
aria-describedby={dashboards.length > 1 ? hintId : undefined}
|
||||
onScroll={measureOverflow}
|
||||
style={maskStyle}
|
||||
className="flex h-7 items-center gap-0.5 overflow-x-auto [scrollbar-width:none] [&::-webkit-scrollbar]:hidden"
|
||||
className="flex h-full items-stretch gap-0.5 overflow-x-auto [scrollbar-width:none] [&::-webkit-scrollbar]:hidden"
|
||||
>
|
||||
{orderedTabs.map((tab, index) => {
|
||||
const isActive = tab.id === activeDashboardId;
|
||||
@@ -339,16 +341,22 @@ export function DashboardTabs({
|
||||
}}
|
||||
data-tab-index={index}
|
||||
data-testid={`dashboard-tab-${tab.id}`}
|
||||
className={`flex h-7 shrink-0 touch-none items-center rounded-md transition-colors ${
|
||||
className={`relative flex shrink-0 touch-none items-center transition-colors ${
|
||||
isActive
|
||||
? 'bg-card text-foreground shadow-sm'
|
||||
: 'text-muted-foreground hover:bg-card/60 hover:text-foreground'
|
||||
? 'text-appbar-foreground'
|
||||
: 'text-appbar-muted hover:text-appbar-foreground'
|
||||
}`}
|
||||
onPointerDown={(e) => handlePointerDown(e, tab.id)}
|
||||
onPointerMove={handlePointerMove}
|
||||
onPointerUp={handlePointerUp}
|
||||
onPointerCancel={handlePointerCancel}
|
||||
>
|
||||
{isActive && (
|
||||
<span
|
||||
aria-hidden="true"
|
||||
className="pointer-events-none absolute inset-x-3 bottom-0 h-[2px] rounded-full bg-primary"
|
||||
/>
|
||||
)}
|
||||
{isRenaming ? (
|
||||
<input
|
||||
ref={renameInputRef}
|
||||
@@ -366,7 +374,7 @@ export function DashboardTabs({
|
||||
}}
|
||||
maxLength={40}
|
||||
aria-label={t('tabs.renameInputLabel')}
|
||||
className="h-6 w-32 rounded border border-border bg-background px-2 text-sm text-foreground"
|
||||
className="h-7 w-32 rounded-md border border-white/25 bg-white/10 px-2 text-sm text-appbar-foreground outline-none focus:border-primary"
|
||||
/>
|
||||
) : (
|
||||
<button
|
||||
@@ -380,8 +388,9 @@ export function DashboardTabs({
|
||||
tabIndex={isFocusable ? 0 : -1}
|
||||
onClick={() => handleTabClick(tab.id)}
|
||||
onKeyDown={(e) => handleTabKeyDown(e, index)}
|
||||
className={`h-7 cursor-grab whitespace-nowrap rounded-md px-3 text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring ${
|
||||
isActive ? 'font-medium' : ''
|
||||
data-text={tab.name}
|
||||
className={`inline-flex h-8 cursor-grab flex-col items-center justify-center whitespace-nowrap rounded-md px-3 text-sm transition-colors hover:bg-appbar-hover focus-visible:outline-2 focus-visible:outline-offset-[-2px] focus-visible:outline-appbar-foreground after:invisible after:h-0 after:overflow-hidden after:font-semibold after:content-[attr(data-text)] ${
|
||||
isActive ? 'font-semibold' : ''
|
||||
}`}
|
||||
>
|
||||
{tab.name}
|
||||
@@ -394,7 +403,7 @@ export function DashboardTabs({
|
||||
onClick={() => startRename(tab)}
|
||||
aria-label={t('tabs.renameButtonLabel')}
|
||||
title={t('tabs.renameButtonLabel')}
|
||||
className="-ml-1.5 rounded p-1 text-muted-foreground hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
|
||||
className="-ml-1.5 rounded p-1 text-appbar-muted hover:text-appbar-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-appbar-foreground"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
@@ -419,7 +428,7 @@ export function DashboardTabs({
|
||||
onClick={() => setPendingDeleteId(tab.id)}
|
||||
aria-label={t('tabs.deleteButtonLabel')}
|
||||
title={t('tabs.deleteButtonLabel')}
|
||||
className={`${isEditMode && isActive ? '' : '-ml-1.5'} mr-1 rounded p-1 text-muted-foreground hover:text-destructive focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring`}
|
||||
className={`${isEditMode && isActive ? '' : '-ml-1.5'} mr-1 rounded p-1 text-appbar-muted hover:text-red-300 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-appbar-foreground`}
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
@@ -453,7 +462,7 @@ export function DashboardTabs({
|
||||
onClick={onCreate}
|
||||
aria-label={t('tabs.add')}
|
||||
title={t('tabs.add')}
|
||||
className="flex h-7 w-7 shrink-0 items-center justify-center rounded-full border border-border bg-card text-muted-foreground shadow-sm transition-colors hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
|
||||
className="flex h-7 w-7 shrink-0 items-center justify-center rounded-md border border-white/20 text-appbar-muted transition-colors hover:bg-appbar-hover hover:text-appbar-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-appbar-foreground"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
@@ -510,7 +519,7 @@ export function DashboardTabs({
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setPendingDeleteId(null)}
|
||||
className="rounded border border-border px-4 py-2 text-sm text-foreground transition-colors hover:bg-muted"
|
||||
className="btn btn-secondary"
|
||||
>
|
||||
{tCommon('cancel')}
|
||||
</button>
|
||||
|
||||
Reference in New Issue
Block a user