swarm-ui: page-switch animation (pop entrance + nav underline travel)
Shell's page content now remounts on every navigation and plays a scale+fade pop-in entrance. The active nav link's underline is now a single shared element that slides to its new position instead of snapping, re-colouring to a per-tab accent — a discrete cycle through the existing base16 chromatic slots, not a continuous hue rotation. Both are gated on the data-motion/prefers-reduced-motion plumbing lib/motion-apply.ts already had wired and waiting for a first real consumer.
This commit is contained in:
parent
213e0a1b89
commit
6be1bbd4f7
3 changed files with 179 additions and 29 deletions
|
|
@ -8,7 +8,17 @@
|
|||
menu: a narrow viewport (phone or a tiled desktop window) wraps onto
|
||||
a second line instead of overflowing — "don't break, don't
|
||||
over-invest" is the explicit scope here, not full mobile navigation
|
||||
redesign. */
|
||||
redesign.
|
||||
|
||||
Page-switch animation (see Shell.tsx's file-top comment for the JS
|
||||
half): both the nav indicator's slide and the page-body pop follow
|
||||
the same three-rule motion-guard shape `lib/motion-apply.ts`'s own
|
||||
doc comment specifies — a base rule,
|
||||
`@media (prefers-reduced-motion: reduce)` to respect the OS default,
|
||||
`:root[data-motion='reduce']` as the explicit override (same
|
||||
specificity as the media rule, so source order after it wins), then
|
||||
`:root[data-motion='allow']` last to re-enable despite an OS-level
|
||||
reduce preference. */
|
||||
.shell-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
|
@ -27,6 +37,7 @@
|
|||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 1em;
|
||||
position: relative; /* anchor for .shell-nav-indicator */
|
||||
}
|
||||
/* Holds `SettingsMenu` + `LinksMenu` — one `margin-left: auto` on the
|
||||
wrapper, not one on each child (see Shell.tsx's comment for why two
|
||||
|
|
@ -60,15 +71,64 @@
|
|||
color: var(--fg);
|
||||
}
|
||||
.shell-nav-link-text {
|
||||
padding-bottom: 0.25em;
|
||||
border-bottom: 2px solid transparent;
|
||||
padding-bottom: 0.25em; /* gap between the text and .shell-nav-indicator below it */
|
||||
}
|
||||
.shell-nav-link-active {
|
||||
color: var(--fg);
|
||||
border-bottom-color: var(--purple);
|
||||
}
|
||||
/* Shared underline, one element instead of one border-bottom per link
|
||||
— see Shell.tsx's file-top comment. `left`/`top`/`width` are set
|
||||
inline per-render from a real measurement; only the *transition*
|
||||
between those values lives here. */
|
||||
.shell-nav-indicator {
|
||||
position: absolute;
|
||||
height: 2px;
|
||||
border-radius: 1px;
|
||||
transition:
|
||||
left 200ms ease,
|
||||
top 200ms ease,
|
||||
width 200ms ease,
|
||||
background-color 200ms ease;
|
||||
}
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.shell-nav-indicator {
|
||||
transition: none;
|
||||
}
|
||||
}
|
||||
:root[data-motion='reduce'] .shell-nav-indicator {
|
||||
transition: none;
|
||||
}
|
||||
:root[data-motion='allow'] .shell-nav-indicator {
|
||||
transition:
|
||||
left 200ms ease,
|
||||
top 200ms ease,
|
||||
width 200ms ease,
|
||||
background-color 200ms ease;
|
||||
}
|
||||
.shell-body {
|
||||
max-width: 60em;
|
||||
margin: 0 auto;
|
||||
padding: 1.5em 1.25em;
|
||||
animation: shell-page-enter 220ms cubic-bezier(0.34, 1.56, 0.64, 1);
|
||||
}
|
||||
@keyframes shell-page-enter {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: scale(0.97);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.shell-body {
|
||||
animation: none;
|
||||
}
|
||||
}
|
||||
:root[data-motion='reduce'] .shell-body {
|
||||
animation: none;
|
||||
}
|
||||
:root[data-motion='allow'] .shell-body {
|
||||
animation: shell-page-enter 220ms cubic-bezier(0.34, 1.56, 0.64, 1);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue