This commit is contained in:
2
LOG.md
2
LOG.md
@@ -5,11 +5,13 @@
|
|||||||
- Refined the public layout after desktop/mobile review: the shell now guards horizontal overflow, uses route-aware bottom padding, hides the top menubar on mobile, and opens the mobile sidebar/search panel from the Dock.
|
- Refined the public layout after desktop/mobile review: the shell now guards horizontal overflow, uses route-aware bottom padding, hides the top menubar on mobile, and opens the mobile sidebar/search panel from the Dock.
|
||||||
- Reworked the Dock so mobile uses it as the primary navigation hub, while desktop keeps a folded Dock that expands on hover/focus and includes a pin toggle.
|
- Reworked the Dock so mobile uses it as the primary navigation hub, while desktop keeps a folded Dock that expands on hover/focus and includes a pin toggle.
|
||||||
- Smoothed the desktop Dock hide/show interaction by separating hover-open, closing, and fully-collapsed states; the Dock now keeps the expanded hit area during the closing animation so re-entering mid-collapse reverses smoothly.
|
- Smoothed the desktop Dock hide/show interaction by separating hover-open, closing, and fully-collapsed states; the Dock now keeps the expanded hit area during the closing animation so re-entering mid-collapse reverses smoothly.
|
||||||
|
- Changed the desktop Dock's default state to pinned/open and added distinct soft pastel tones to Dock buttons, including stronger visual treatment for pin/unpin and auth actions.
|
||||||
- Rebalanced the home page around latest posts with popular posts as a supporting panel, converted the post detail page into a quieter reader surface, and hardened Markdown/code/table wrapping against mobile overflow.
|
- Rebalanced the home page around latest posts with popular posts as a supporting panel, converted the post detail page into a quieter reader surface, and hardened Markdown/code/table wrapping against mobile overflow.
|
||||||
- Added a client-side archive explorer with year, month, category, and compact/comfort density controls over the existing server-fetched post list.
|
- Added a client-side archive explorer with year, month, category, and compact/comfort density controls over the existing server-fetched post list.
|
||||||
- Rebuilt the chess page with safer viewport-aware board sizing, quieter puzzle panels, and enough layout clearance for the Dock.
|
- Rebuilt the chess page with safer viewport-aware board sizing, quieter puzzle panels, and enough layout clearance for the Dock.
|
||||||
- Validation: `npm run lint` passed and `npm run build` passed. In-app browser verification on a local `next start` server confirmed no horizontal overflow on `/`, `/archive`, and `/play/chess` at mobile width, mobile Dock-centered navigation opens the sidebar panel without overflow, desktop CSS loads, the mobile Dock is hidden on desktop, and the desktop Dock starts folded with a pin control. The live chess board itself could not be verified because the local backend chess API was unavailable, so `/play/chess` rendered the error state.
|
- Validation: `npm run lint` passed and `npm run build` passed. In-app browser verification on a local `next start` server confirmed no horizontal overflow on `/`, `/archive`, and `/play/chess` at mobile width, mobile Dock-centered navigation opens the sidebar panel without overflow, desktop CSS loads, the mobile Dock is hidden on desktop, and the desktop Dock starts folded with a pin control. The live chess board itself could not be verified because the local backend chess API was unavailable, so `/play/chess` rendered the error state.
|
||||||
- Validation: `npm run lint` passed and `npm run build` passed. In-app browser verification on a local `next start` server confirmed the desktop Dock starts as a small bottom tab, uses 560ms transitions for tab/panel geometry, and expands into the larger Dock hit area when opened.
|
- Validation: `npm run lint` passed and `npm run build` passed. In-app browser verification on a local `next start` server confirmed the desktop Dock starts as a small bottom tab, uses 560ms transitions for tab/panel geometry, and expands into the larger Dock hit area when opened.
|
||||||
|
- Validation: `npm run lint` passed and `npm run build` passed. In-app browser verification on a local `next start` server confirmed the Dock opens by default and Home/Archive/Chess/Login/Signup/Pin buttons render distinct pastel colors.
|
||||||
- Recommended next task: run a browser pass against a reachable backend API, especially one real post detail route and the populated chess puzzle board, then tune any remaining content-specific long-title or board-height edge cases.
|
- Recommended next task: run a browser pass against a reachable backend API, especially one real post detail route and the populated chess puzzle board, then tune any remaining content-specific long-title or board-height edge cases.
|
||||||
|
|
||||||
## 2026-05-29
|
## 2026-05-29
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { type FocusEvent, useEffect, useRef, useState } from 'react';
|
import { type CSSProperties, type FocusEvent, useEffect, useRef, useState } from 'react';
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
import { usePathname, useRouter } from 'next/navigation';
|
import { usePathname, useRouter } from 'next/navigation';
|
||||||
import { clsx } from 'clsx';
|
import { clsx } from 'clsx';
|
||||||
@@ -43,12 +43,61 @@ const isActivePath = (target: string) => (pathname: string) => {
|
|||||||
|
|
||||||
const DOCK_LEAVE_DELAY_MS = 120;
|
const DOCK_LEAVE_DELAY_MS = 120;
|
||||||
const DOCK_COLLAPSE_MS = 560;
|
const DOCK_COLLAPSE_MS = 560;
|
||||||
|
const DOCK_PINNED_STORAGE_KEY = 'dock-pinned-v2';
|
||||||
|
|
||||||
|
type DockToneStyle = CSSProperties & {
|
||||||
|
'--dock-item-bg': string;
|
||||||
|
'--dock-item-bg-hover': string;
|
||||||
|
'--dock-item-bg-active': string;
|
||||||
|
'--dock-item-border': string;
|
||||||
|
'--dock-item-border-hover': string;
|
||||||
|
'--dock-item-fg': string;
|
||||||
|
'--dock-item-fg-strong': string;
|
||||||
|
'--dock-item-ring': string;
|
||||||
|
};
|
||||||
|
|
||||||
|
const createDockTone = (
|
||||||
|
bg: string,
|
||||||
|
hover: string,
|
||||||
|
active: string,
|
||||||
|
border: string,
|
||||||
|
borderHover: string,
|
||||||
|
fg: string,
|
||||||
|
fgStrong: string,
|
||||||
|
ring: string,
|
||||||
|
): DockToneStyle => ({
|
||||||
|
'--dock-item-bg': bg,
|
||||||
|
'--dock-item-bg-hover': hover,
|
||||||
|
'--dock-item-bg-active': active,
|
||||||
|
'--dock-item-border': border,
|
||||||
|
'--dock-item-border-hover': borderHover,
|
||||||
|
'--dock-item-fg': fg,
|
||||||
|
'--dock-item-fg-strong': fgStrong,
|
||||||
|
'--dock-item-ring': ring,
|
||||||
|
});
|
||||||
|
|
||||||
|
const dockToneStyles: Record<string, DockToneStyle> = {
|
||||||
|
home: createDockTone('rgba(215, 246, 231, 0.78)', 'rgba(197, 238, 219, 0.92)', 'rgba(183, 231, 208, 0.96)', 'rgba(66, 178, 126, 0.28)', 'rgba(45, 153, 102, 0.44)', '#287653', '#135c3c', 'rgba(45, 153, 102, 0.26)'),
|
||||||
|
archive: createDockTone('rgba(230, 224, 255, 0.78)', 'rgba(218, 210, 250, 0.92)', 'rgba(207, 196, 246, 0.96)', 'rgba(126, 100, 214, 0.28)', 'rgba(105, 78, 190, 0.44)', '#63509b', '#4f3d86', 'rgba(105, 78, 190, 0.24)'),
|
||||||
|
chess: createDockTone('rgba(255, 239, 190, 0.82)', 'rgba(255, 231, 161, 0.94)', 'rgba(250, 220, 133, 0.96)', 'rgba(195, 145, 34, 0.28)', 'rgba(170, 119, 18, 0.42)', '#7a5a12', '#654706', 'rgba(170, 119, 18, 0.24)'),
|
||||||
|
admin: createDockTone('rgba(211, 236, 255, 0.8)', 'rgba(190, 226, 252, 0.94)', 'rgba(173, 215, 247, 0.96)', 'rgba(62, 145, 205, 0.3)', 'rgba(36, 119, 181, 0.46)', '#2c6f9d', '#155a86', 'rgba(36, 119, 181, 0.25)'),
|
||||||
|
write: createDockTone('rgba(255, 225, 207, 0.82)', 'rgba(255, 212, 189, 0.94)', 'rgba(250, 198, 169, 0.96)', 'rgba(205, 110, 68, 0.3)', 'rgba(181, 88, 48, 0.44)', '#995333', '#7d3e20', 'rgba(181, 88, 48, 0.24)'),
|
||||||
|
login: createDockTone('rgba(211, 244, 246, 0.8)', 'rgba(190, 234, 238, 0.94)', 'rgba(170, 225, 230, 0.96)', 'rgba(53, 163, 172, 0.3)', 'rgba(28, 136, 146, 0.45)', '#237984', '#12626c', 'rgba(28, 136, 146, 0.24)'),
|
||||||
|
signup: createDockTone('rgba(245, 223, 255, 0.8)', 'rgba(238, 207, 252, 0.94)', 'rgba(228, 190, 244, 0.96)', 'rgba(166, 93, 191, 0.3)', 'rgba(139, 68, 166, 0.44)', '#814b93', '#6b347f', 'rgba(139, 68, 166, 0.24)'),
|
||||||
|
logout: createDockTone('rgba(255, 224, 229, 0.82)', 'rgba(255, 207, 216, 0.94)', 'rgba(250, 190, 202, 0.96)', 'rgba(204, 82, 98, 0.3)', 'rgba(179, 58, 76, 0.46)', '#9b4250', '#81303d', 'rgba(179, 58, 76, 0.24)'),
|
||||||
|
pin: createDockTone('rgba(255, 231, 196, 0.9)', 'rgba(255, 219, 170, 0.98)', 'rgba(250, 204, 137, 1)', 'rgba(205, 132, 48, 0.38)', 'rgba(177, 101, 22, 0.58)', '#90571f', '#713f10', 'rgba(177, 101, 22, 0.3)'),
|
||||||
|
menu: createDockTone('rgba(223, 241, 215, 0.82)', 'rgba(207, 231, 197, 0.94)', 'rgba(190, 218, 178, 0.96)', 'rgba(105, 157, 88, 0.3)', 'rgba(82, 134, 66, 0.45)', '#557a43', '#3f6630', 'rgba(82, 134, 66, 0.24)'),
|
||||||
|
more: createDockTone('rgba(221, 235, 255, 0.82)', 'rgba(204, 224, 252, 0.94)', 'rgba(185, 211, 245, 0.96)', 'rgba(89, 137, 205, 0.3)', 'rgba(67, 112, 181, 0.45)', '#4f6f9a', '#395a86', 'rgba(67, 112, 181, 0.24)'),
|
||||||
|
default: createDockTone('rgba(239, 241, 246, 0.78)', 'rgba(226, 230, 238, 0.94)', 'rgba(214, 220, 232, 0.96)', 'rgba(110, 122, 145, 0.24)', 'rgba(86, 99, 124, 0.38)', '#626d82', '#47566f', 'rgba(86, 99, 124, 0.2)'),
|
||||||
|
};
|
||||||
|
|
||||||
|
const getDockToneStyle = (key: string) => dockToneStyles[key] ?? dockToneStyles.default;
|
||||||
|
|
||||||
export default function DesktopDock({ isSidebarCollapsed, onOpenMobileMenu }: DesktopDockProps) {
|
export default function DesktopDock({ isSidebarCollapsed, onOpenMobileMenu }: DesktopDockProps) {
|
||||||
const pathname = usePathname();
|
const pathname = usePathname();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { isLoggedIn, role, logout, _hasHydrated } = useAuthStore();
|
const { isLoggedIn, role, logout, _hasHydrated } = useAuthStore();
|
||||||
const [isPinned, setIsPinned] = useState(false);
|
const [isPinned, setIsPinned] = useState(true);
|
||||||
const [isMoreOpen, setIsMoreOpen] = useState(false);
|
const [isMoreOpen, setIsMoreOpen] = useState(false);
|
||||||
const [isDockHovered, setIsDockHovered] = useState(false);
|
const [isDockHovered, setIsDockHovered] = useState(false);
|
||||||
const [isDockFocused, setIsDockFocused] = useState(false);
|
const [isDockFocused, setIsDockFocused] = useState(false);
|
||||||
@@ -99,7 +148,8 @@ export default function DesktopDock({ isSidebarCollapsed, onOpenMobileMenu }: De
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const frameId = window.requestAnimationFrame(() => {
|
const frameId = window.requestAnimationFrame(() => {
|
||||||
setIsPinned(window.localStorage.getItem('dock-pinned') === 'true');
|
const savedValue = window.localStorage.getItem(DOCK_PINNED_STORAGE_KEY);
|
||||||
|
setIsPinned(savedValue === null ? true : savedValue === 'true');
|
||||||
});
|
});
|
||||||
|
|
||||||
return () => window.cancelAnimationFrame(frameId);
|
return () => window.cancelAnimationFrame(frameId);
|
||||||
@@ -120,7 +170,7 @@ export default function DesktopDock({ isSidebarCollapsed, onOpenMobileMenu }: De
|
|||||||
const handlePinnedChange = () => {
|
const handlePinnedChange = () => {
|
||||||
setIsPinned((previous) => {
|
setIsPinned((previous) => {
|
||||||
const nextValue = !previous;
|
const nextValue = !previous;
|
||||||
window.localStorage.setItem('dock-pinned', String(nextValue));
|
window.localStorage.setItem(DOCK_PINNED_STORAGE_KEY, String(nextValue));
|
||||||
clearDockTimers();
|
clearDockTimers();
|
||||||
setIsDockClosing(false);
|
setIsDockClosing(false);
|
||||||
setIsDockHovered(!nextValue);
|
setIsDockHovered(!nextValue);
|
||||||
@@ -255,9 +305,10 @@ export default function DesktopDock({ isSidebarCollapsed, onOpenMobileMenu }: De
|
|||||||
const renderDesktopItem = (item: DockAction) => {
|
const renderDesktopItem = (item: DockAction) => {
|
||||||
const Icon = item.icon;
|
const Icon = item.icon;
|
||||||
const active = item.isActive?.(pathname) ?? false;
|
const active = item.isActive?.(pathname) ?? false;
|
||||||
|
const toneStyle = getDockToneStyle(item.key);
|
||||||
const itemClass = clsx(
|
const itemClass = clsx(
|
||||||
'group/item relative flex h-12 w-12 items-center justify-center rounded-lg border border-[var(--control-border)] bg-[var(--color-control)] text-[var(--color-text-muted)] shadow-[var(--shadow-control)] transition duration-150 hover:-translate-y-1 hover:bg-[var(--card-bg-strong)] hover:text-[var(--color-text)] focus-visible:-translate-y-1',
|
'group/item relative flex h-12 w-12 items-center justify-center rounded-lg border border-[var(--dock-item-border)] bg-[var(--dock-item-bg)] text-[var(--dock-item-fg)] shadow-[var(--shadow-control)] transition duration-150 hover:-translate-y-1 hover:border-[var(--dock-item-border-hover)] hover:bg-[var(--dock-item-bg-hover)] hover:text-[var(--dock-item-fg-strong)] focus-visible:-translate-y-1',
|
||||||
active && 'bg-[var(--card-bg-strong)] text-[var(--color-accent)] ring-1 ring-[var(--color-accent-soft)]',
|
active && 'border-[var(--dock-item-border-hover)] bg-[var(--dock-item-bg-active)] text-[var(--dock-item-fg-strong)] ring-1 ring-[var(--dock-item-ring)]',
|
||||||
);
|
);
|
||||||
|
|
||||||
const content = (
|
const content = (
|
||||||
@@ -267,7 +318,7 @@ export default function DesktopDock({ isSidebarCollapsed, onOpenMobileMenu }: De
|
|||||||
{item.label}
|
{item.label}
|
||||||
</span>
|
</span>
|
||||||
{active && (
|
{active && (
|
||||||
<span className="absolute -bottom-1 h-1 w-1 rounded-full bg-[var(--color-accent)]" />
|
<span className="absolute -bottom-1 h-1 w-1 rounded-full bg-[var(--dock-item-fg-strong)]" />
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
@@ -281,6 +332,7 @@ export default function DesktopDock({ isSidebarCollapsed, onOpenMobileMenu }: De
|
|||||||
aria-label={item.label}
|
aria-label={item.label}
|
||||||
aria-current={active ? 'page' : undefined}
|
aria-current={active ? 'page' : undefined}
|
||||||
className={itemClass}
|
className={itemClass}
|
||||||
|
style={toneStyle}
|
||||||
>
|
>
|
||||||
{content}
|
{content}
|
||||||
</Link>
|
</Link>
|
||||||
@@ -295,6 +347,7 @@ export default function DesktopDock({ isSidebarCollapsed, onOpenMobileMenu }: De
|
|||||||
aria-label={item.label}
|
aria-label={item.label}
|
||||||
onClick={item.onClick}
|
onClick={item.onClick}
|
||||||
className={itemClass}
|
className={itemClass}
|
||||||
|
style={toneStyle}
|
||||||
>
|
>
|
||||||
{content}
|
{content}
|
||||||
</button>
|
</button>
|
||||||
@@ -304,10 +357,11 @@ export default function DesktopDock({ isSidebarCollapsed, onOpenMobileMenu }: De
|
|||||||
const renderMobileItem = (item: DockAction, activeOverride?: boolean) => {
|
const renderMobileItem = (item: DockAction, activeOverride?: boolean) => {
|
||||||
const Icon = item.icon;
|
const Icon = item.icon;
|
||||||
const active = activeOverride ?? item.isActive?.(pathname) ?? false;
|
const active = activeOverride ?? item.isActive?.(pathname) ?? false;
|
||||||
|
const toneStyle = getDockToneStyle(item.key);
|
||||||
const itemClass = clsx(
|
const itemClass = clsx(
|
||||||
'flex h-12 min-w-0 flex-1 flex-col items-center justify-center gap-0.5 rounded-lg border border-transparent px-1 text-[10px] font-semibold leading-none text-[var(--color-text-subtle)] transition-colors',
|
'flex h-12 min-w-0 flex-1 flex-col items-center justify-center gap-0.5 rounded-lg border border-[var(--dock-item-border)] bg-[var(--dock-item-bg)] px-1 text-[10px] font-semibold leading-none text-[var(--dock-item-fg)] shadow-[var(--shadow-control)] transition-colors',
|
||||||
'hover:bg-[var(--card-bg)] hover:text-[var(--color-text)] focus-visible:bg-[var(--card-bg-strong)]',
|
'hover:border-[var(--dock-item-border-hover)] hover:bg-[var(--dock-item-bg-hover)] hover:text-[var(--dock-item-fg-strong)] focus-visible:bg-[var(--dock-item-bg-active)]',
|
||||||
active && 'border-[var(--control-border)] bg-[var(--card-bg-strong)] text-[var(--color-accent)] shadow-[var(--shadow-control)]',
|
active && 'border-[var(--dock-item-border-hover)] bg-[var(--dock-item-bg-active)] text-[var(--dock-item-fg-strong)] ring-1 ring-[var(--dock-item-ring)]',
|
||||||
);
|
);
|
||||||
const content = (
|
const content = (
|
||||||
<>
|
<>
|
||||||
@@ -325,6 +379,7 @@ export default function DesktopDock({ isSidebarCollapsed, onOpenMobileMenu }: De
|
|||||||
aria-label={item.label}
|
aria-label={item.label}
|
||||||
aria-current={active ? 'page' : undefined}
|
aria-current={active ? 'page' : undefined}
|
||||||
className={itemClass}
|
className={itemClass}
|
||||||
|
style={toneStyle}
|
||||||
onClick={() => setIsMoreOpen(false)}
|
onClick={() => setIsMoreOpen(false)}
|
||||||
>
|
>
|
||||||
{content}
|
{content}
|
||||||
@@ -340,6 +395,7 @@ export default function DesktopDock({ isSidebarCollapsed, onOpenMobileMenu }: De
|
|||||||
aria-label={item.label}
|
aria-label={item.label}
|
||||||
onClick={item.onClick}
|
onClick={item.onClick}
|
||||||
className={itemClass}
|
className={itemClass}
|
||||||
|
style={toneStyle}
|
||||||
>
|
>
|
||||||
{content}
|
{content}
|
||||||
</button>
|
</button>
|
||||||
@@ -376,12 +432,13 @@ export default function DesktopDock({ isSidebarCollapsed, onOpenMobileMenu }: De
|
|||||||
onMouseEnter={expandDock}
|
onMouseEnter={expandDock}
|
||||||
onFocus={handleDockHandleFocus}
|
onFocus={handleDockHandleFocus}
|
||||||
className={clsx(
|
className={clsx(
|
||||||
'absolute bottom-0 left-1/2 z-10 flex h-9 w-12 -translate-x-1/2 items-start justify-center rounded-t-lg border border-b-0 border-[var(--dock-border)] bg-[var(--dock-bg)] pt-1.5 text-[var(--color-text-subtle)] shadow-[var(--shadow-control)] backdrop-blur-[22px]',
|
'absolute bottom-0 left-1/2 z-10 flex h-9 w-12 -translate-x-1/2 items-start justify-center rounded-t-lg border border-b-0 border-[var(--dock-item-border-hover)] bg-[var(--dock-item-bg-active)] pt-1.5 text-[var(--dock-item-fg-strong)] shadow-[var(--shadow-control)] backdrop-blur-[22px]',
|
||||||
'transition-[transform,opacity,width,height] duration-[560ms] ease-[cubic-bezier(0.22,1,0.36,1)] hover:text-[var(--color-text)] focus-visible:text-[var(--color-text)]',
|
'transition-[transform,opacity,width,height] duration-[560ms] ease-[cubic-bezier(0.22,1,0.36,1)] hover:bg-[var(--dock-item-bg-hover)] focus-visible:bg-[var(--dock-item-bg-hover)]',
|
||||||
isDockRangeExpanded
|
isDockRangeExpanded
|
||||||
? 'pointer-events-none translate-y-5 scale-75 opacity-0'
|
? 'pointer-events-none translate-y-5 scale-75 opacity-0'
|
||||||
: 'pointer-events-auto translate-y-0 scale-100 opacity-100 hover:h-10 hover:w-14',
|
: 'pointer-events-auto translate-y-0 scale-100 opacity-100 hover:h-10 hover:w-14',
|
||||||
)}
|
)}
|
||||||
|
style={getDockToneStyle('more')}
|
||||||
>
|
>
|
||||||
<MoreHorizontal size={18} strokeWidth={2.3} />
|
<MoreHorizontal size={18} strokeWidth={2.3} />
|
||||||
</button>
|
</button>
|
||||||
@@ -407,9 +464,10 @@ export default function DesktopDock({ isSidebarCollapsed, onOpenMobileMenu }: De
|
|||||||
aria-pressed={isPinned}
|
aria-pressed={isPinned}
|
||||||
onClick={handlePinnedChange}
|
onClick={handlePinnedChange}
|
||||||
className={clsx(
|
className={clsx(
|
||||||
'group/item relative ml-1 flex h-12 w-12 items-center justify-center rounded-lg border border-[var(--control-border)] bg-[var(--color-control)] text-[var(--color-text-muted)] shadow-[var(--shadow-control)] transition duration-150 hover:-translate-y-1 hover:bg-[var(--card-bg-strong)] hover:text-[var(--color-text)]',
|
'group/item relative ml-1 flex h-12 w-12 items-center justify-center rounded-lg border border-[var(--dock-item-border-hover)] bg-[var(--dock-item-bg-active)] text-[var(--dock-item-fg-strong)] shadow-[var(--shadow-control)] ring-1 ring-[var(--dock-item-ring)] transition duration-150 hover:-translate-y-1 hover:bg-[var(--dock-item-bg-hover)]',
|
||||||
isPinned && 'bg-[var(--card-bg-strong)] text-[var(--color-accent)]',
|
isPinned && 'shadow-[var(--shadow-dock)]',
|
||||||
)}
|
)}
|
||||||
|
style={getDockToneStyle('pin')}
|
||||||
>
|
>
|
||||||
{isPinned ? <PinOff size={19} /> : <Pin size={19} />}
|
{isPinned ? <PinOff size={19} /> : <Pin size={19} />}
|
||||||
<span className="pointer-events-none absolute -top-9 whitespace-nowrap rounded-full border border-[var(--card-border)] bg-[var(--card-bg-strong)] px-2.5 py-1 text-xs font-semibold text-[var(--color-text)] opacity-0 shadow-[var(--shadow-control)] backdrop-blur-[18px] transition group-hover/item:opacity-100">
|
<span className="pointer-events-none absolute -top-9 whitespace-nowrap rounded-full border border-[var(--card-border)] bg-[var(--card-bg-strong)] px-2.5 py-1 text-xs font-semibold text-[var(--color-text)] opacity-0 shadow-[var(--shadow-control)] backdrop-blur-[18px] transition group-hover/item:opacity-100">
|
||||||
@@ -433,9 +491,10 @@ export default function DesktopDock({ isSidebarCollapsed, onOpenMobileMenu }: De
|
|||||||
{authItems.map((item) => {
|
{authItems.map((item) => {
|
||||||
const Icon = item.icon;
|
const Icon = item.icon;
|
||||||
const active = item.isActive?.(pathname) ?? false;
|
const active = item.isActive?.(pathname) ?? false;
|
||||||
|
const toneStyle = getDockToneStyle(item.key);
|
||||||
const className = clsx(
|
const className = clsx(
|
||||||
'inline-flex h-10 min-w-0 items-center justify-center gap-2 rounded-lg border border-[var(--control-border)] bg-[var(--color-control)] px-3 text-sm font-semibold text-[var(--color-text-muted)] shadow-[var(--shadow-control)] transition-colors hover:bg-[var(--card-bg-strong)] hover:text-[var(--color-text)]',
|
'inline-flex h-10 min-w-0 items-center justify-center gap-2 rounded-lg border border-[var(--dock-item-border)] bg-[var(--dock-item-bg)] px-3 text-sm font-semibold text-[var(--dock-item-fg)] shadow-[var(--shadow-control)] transition-colors hover:border-[var(--dock-item-border-hover)] hover:bg-[var(--dock-item-bg-hover)] hover:text-[var(--dock-item-fg-strong)]',
|
||||||
active && 'text-[var(--color-accent)]',
|
active && 'border-[var(--dock-item-border-hover)] bg-[var(--dock-item-bg-active)] text-[var(--dock-item-fg-strong)] ring-1 ring-[var(--dock-item-ring)]',
|
||||||
);
|
);
|
||||||
|
|
||||||
if (item.href) {
|
if (item.href) {
|
||||||
@@ -444,6 +503,7 @@ export default function DesktopDock({ isSidebarCollapsed, onOpenMobileMenu }: De
|
|||||||
key={item.key}
|
key={item.key}
|
||||||
href={item.href}
|
href={item.href}
|
||||||
className={className}
|
className={className}
|
||||||
|
style={toneStyle}
|
||||||
onClick={() => setIsMoreOpen(false)}
|
onClick={() => setIsMoreOpen(false)}
|
||||||
>
|
>
|
||||||
<Icon size={16} />
|
<Icon size={16} />
|
||||||
@@ -457,6 +517,7 @@ export default function DesktopDock({ isSidebarCollapsed, onOpenMobileMenu }: De
|
|||||||
key={item.key}
|
key={item.key}
|
||||||
type="button"
|
type="button"
|
||||||
className={className}
|
className={className}
|
||||||
|
style={toneStyle}
|
||||||
onClick={item.onClick}
|
onClick={item.onClick}
|
||||||
>
|
>
|
||||||
<Icon size={16} />
|
<Icon size={16} />
|
||||||
|
|||||||
Reference in New Issue
Block a user