From e8c6ce5a8ab1b43d73bc38aa4122e93cb6854ba8 Mon Sep 17 00:00:00 2001 From: Nils Pukropp Date: Fri, 15 May 2026 19:25:58 +0200 Subject: [PATCH] fixed cut names + hover tooltip --- frontend/src/components/react/Search.tsx | 7 +- frontend/src/styles/global.css | 94 +++++++++++++++++++++++- 2 files changed, 96 insertions(+), 5 deletions(-) diff --git a/frontend/src/components/react/Search.tsx b/frontend/src/components/react/Search.tsx index 0d1e767..cbb350f 100644 --- a/frontend/src/components/react/Search.tsx +++ b/frontend/src/components/react/Search.tsx @@ -129,8 +129,7 @@ export default function Search() { type="button" onClick={() => setOpen(true)} aria-label={`Search the catalogue (${isMac ? '⌘' : 'Ctrl'}+K)`} - title={`Search (${isMac ? '⌘' : 'Ctrl'}+K)`} - className="topbar-control tc-collapse-md" + className="topbar-control tc-collapse-md kbd-tip-host" > Search + {open && ( diff --git a/frontend/src/styles/global.css b/frontend/src/styles/global.css index e9aee35..b936ecf 100644 --- a/frontend/src/styles/global.css +++ b/frontend/src/styles/global.css @@ -829,6 +829,19 @@ code, pre, kbd, samp { gap: 2px; position: relative; } +.nameplate::after { + content: ""; + position: absolute; + left: 0; + right: 0; + bottom: -6px; + height: 2px; + background: linear-gradient(to right, + var(--mauve) 0%, + var(--mauve) 35%, + var(--surface2) 35%, + var(--surface2) 100%); +} .nameplate-title { font-family: var(--font-display); font-weight: 600; @@ -1098,6 +1111,67 @@ code, pre, kbd, samp { .topbar-control svg { width: 15px; height: 15px; flex-shrink: 0; } /* Exact-square icon-only variant — keeps the row aligned. */ .topbar-control--icon { width: 2rem; padding: 0; } + +/* Keyboard-shortcut hover/focus tooltip — kept out of the button label, + * surfaced only on hover or keyboard focus. */ +.kbd-tip-host { position: relative; } +.kbd-tip { + position: absolute; + top: calc(100% + 8px); + left: 50%; + display: flex; + align-items: center; + gap: 0.3rem; + white-space: nowrap; + padding: 4px 8px; + font-family: var(--font-sans); + font-size: 0.6rem; + font-style: normal; + letter-spacing: 0.16em; + text-transform: uppercase; + color: var(--subtext1); + background: color-mix(in srgb, var(--crust) 90%, transparent); + border: 1px solid color-mix(in srgb, var(--surface2) 70%, transparent); + border-radius: 4px; + box-shadow: 0 8px 20px -10px rgba(0, 0, 0, 0.5); + opacity: 0; + transform: translate(-50%, 4px); + pointer-events: none; + transition: opacity 0.16s ease, transform 0.16s ease; + z-index: 60; +} +.kbd-tip kbd { + font-family: var(--font-mono); + font-size: 0.62rem; + line-height: 1; + padding: 2px 5px; + color: var(--text); + background: color-mix(in srgb, var(--surface0) 70%, transparent); + border: 1px solid color-mix(in srgb, var(--surface2) 80%, transparent); + border-radius: 3px; +} +.kbd-tip-host:hover .kbd-tip, +.kbd-tip-host:focus-visible .kbd-tip { + opacity: 1; + transform: translate(-50%, 0); +} +/* Breakcore: hard neon tooltip — matches the layer's offset-shadow chrome. */ +.breakcore .kbd-tip { + background: var(--crust); + border-color: var(--mauve); + border-radius: 0; + color: var(--green); + box-shadow: 2px 2px 0 var(--mauve); +} +.breakcore .kbd-tip kbd { + color: var(--rosewater); + background: var(--surface0); + border-color: var(--mauve); + border-radius: 0; +} +@media (prefers-reduced-motion: reduce) { + .kbd-tip { transition: opacity 0.16s ease; transform: translate(-50%, 0); } +} .topbar-control--danger:hover { color: var(--red); border-color: color-mix(in srgb, var(--red) 55%, var(--surface2)); @@ -1283,7 +1357,17 @@ input[type="date"] { color-scheme: light; } ); } -/* Nameplate — glitch-shear burst on hover (underline removed site-wide). */ +/* Nameplate — breakcore reworks the underline: hard cyan offset + magenta + * neon glow (the layer's hard-offset chrome language) instead of the + * default two-tone rule. Plus a glitch-shear burst on hover. */ +.breakcore .nameplate::after { + height: 2px; + bottom: -7px; + background: var(--mauve); + box-shadow: + 2px 2px 0 var(--blue), + 0 0 10px color-mix(in srgb, var(--mauve) 70%, transparent); +} @keyframes bc-shear { 0% { clip-path: inset(0 0 0 0); transform: translateX(0); text-shadow: -1px 0 0 var(--teal), 1px 0 0 var(--mauve); } @@ -1310,11 +1394,15 @@ input[type="date"] { color-scheme: light; } 40% { clip-path: inset(68% 0 8% 0); transform: translateX(-5px); } 60% { clip-path: inset(24% 0 36% 0); transform: translateX(3px); } 80% { clip-path: inset(4% 0 84% 0); transform: translateX(-2px); } - 100% { opacity: 1; clip-path: inset(0 0 0 0); transform: translateX(0); } + /* End unclipped (none, not inset(0)) so italic-Fraunces descenders + * (g, y, p) aren't sliced at the box edge once the glitch settles. */ + 100% { opacity: 1; clip-path: none; transform: translateX(0); } } .breakcore .prose h1, .breakcore h1.font-display { - animation: bc-load-glitch 460ms steps(5, jump-none) both; + /* `backwards` (not `both`): after the one-shot, props revert to base — + * clip-path: none — instead of persisting the final inset clip. */ + animation: bc-load-glitch 460ms steps(5, jump-none) backwards; } /* Plate — hard hover (no soft lift), RGB-split image, scanline sweep. */