Files
Portfolio/src/styles/title-navigation.css
LucaP f46131723b feat: finish portfolio title, navigation, and destination effects
Add the approved name animation, rounded docking navigation, localized keyboard focus cue, unified builds card, and destination copy hierarchy.

Introduce lightweight one-shot meteors and a sun ring that repeats every three seconds at Contact. Open the email action in a separate browsing context. Preserve reduced-motion behavior and keep private handoff and visual-check artifacts out of version control.

Validation: 649 tests passed; all 20 failures in world-models and SceneCanvas reproduce on baseline 103427f. Typecheck, artifact verification, and transfer budgets passed. Client/SSR preview build passed before this commit. Existing baseline test failures remain unchanged.
2026-09-05 22:16:42 +03:00

146 lines
4.3 KiB
CSS

/* The name remains ordinary, crawlable text; only this small decoration moves. */
.hero-name {
position: relative;
isolation: isolate;
inline-size: min-content;
overflow-wrap: normal;
/* Reserve the waves' full travel plus breathing room before the role. This
stays in place when motion pauses, so the content does not jump. */
margin-block-end: 16px;
}
.hero-name__text {
position: relative;
z-index: 1;
}
.hero-name-particles {
position: absolute;
inset: 0;
z-index: 2;
pointer-events: none;
opacity: 0;
}
.hero-name-particles[data-active='true'] {
opacity: 1;
}
.hero-name-particles canvas {
position: absolute;
inset: -8px auto auto -12px;
inline-size: calc(100% + 24px);
block-size: calc(100% + 32px);
max-inline-size: none;
}
.hero-name-particles::after {
content: attr(data-name);
position: absolute;
inset: 0;
background: linear-gradient(110deg, transparent 30%, #fff8e8 46%, white 49%, transparent 62%);
background-size: 260% 100%;
background-clip: text;
-webkit-text-fill-color: transparent;
animation: name-sheen 3.2s ease-in-out infinite;
animation-play-state: paused;
}
.hero-name-particles[data-active='true']::after {
animation-play-state: running;
}
@keyframes name-sheen {
0%, 12% { background-position: 150% 0; opacity: 0; }
25%, 75% { opacity: 0.95; }
88%, 100% { background-position: -70% 0; opacity: 0; }
}
/* A scaled background expands independently of the links, keeping glyphs sharp
and avoiding per-frame document layout during the docking transition. */
.site-navigation {
border: 0;
background: none;
box-shadow: none;
isolation: isolate;
}
.site-navigation::before {
content: '';
position: absolute;
inset: 0;
z-index: -1;
pointer-events: none;
border: 1px solid rgb(198 122 209 / 34%);
border-radius: inherit;
background: linear-gradient(110deg, rgb(39 19 52 / 97%), rgb(22 22 48 / 97%));
box-shadow: 0 8px 32px rgb(13 2 28 / 24%), inset 0 1px rgb(255 159 216 / 7%);
}
.site-navigation a {
border-block-end-color: transparent;
border-radius: 6px;
}
.site-navigation a[aria-current='page'],
.site-navigation a:hover {
border-block-end-color: transparent;
}
@media (min-width: 768px) {
.site-navigation ul {
grid-auto-rows: 44px;
padding: 4px;
gap: 4px;
}
.site-navigation a {
block-size: 44px;
}
.site-navigation[data-enhanced='true'] {
inset: 0 0 auto;
transform: translateY(14px);
transition: transform 300ms cubic-bezier(0.22, 1, 0.36, 1);
}
.site-navigation[data-enhanced='true']::before {
transform: scaleX(var(--nav-rest-scale));
transition: transform 300ms cubic-bezier(0.22, 1, 0.36, 1);
}
.site-navigation[data-enhanced='true'] .site-navigation__disclosure {
inline-size: min(1400px, calc(100% - 48px));
max-inline-size: none;
}
.site-navigation[data-enhanced='true'][data-docked='true'] {
transform: translateY(0);
}
.site-navigation[data-enhanced='true'][data-docked='true']::before {
transform: scaleX(1);
}
.site-navigation[data-motion='false'],
.site-navigation[data-motion='false']::before {
transition: none;
}
}
/* This cue is a sibling of the masked viewport, not a pseudo-element inside it.
Native main focus and skip-link behavior also work without JavaScript. */
.content-focus-cue {
display: none;
position: fixed;
z-index: var(--z-hud);
inset-block-start: calc(var(--site-navigation-block-size) + 4px);
inset-inline-start: max(24px, calc((100vw - 1400px) / 2));
max-inline-size: calc(100vw - 48px);
padding: 6px 12px;
border: 2px solid var(--focus);
border-radius: 6px;
background: var(--surface-strong);
color: var(--focus);
font: 560 0.8rem/1.5 var(--sans);
pointer-events: none;
}
.reading-stage:has(.portfolio-main:focus-visible) + .content-focus-cue {
display: block;
}
@media (prefers-reduced-motion: reduce) {
.hero-name-particles { display: none; }
.site-navigation, .site-navigation::before { transition: none; }
}
@media (forced-colors: active) {
.hero-name-particles { display: none; }
.site-navigation::before { background: Canvas; border-color: ButtonText; }
.site-navigation a[aria-current='page'] { outline: 1px solid Highlight; outline-offset: -4px; }
.content-focus-cue { background: Canvas; border-color: Highlight; color: CanvasText; }
}
@media print {
.hero-name-particles, .content-focus-cue { display: none !important; }
}