site: motion & delight pass — media fades in, cards lift, grids ripple
- Images/videos fade in on load instead of popping (cached media renders instantly; nothing is hidden without JS) - Cross-document view transitions: soft cross-fade between pages where supported - Masonry reveals ripple: initial grid and "Load more" batches stagger in per-tile instead of blinking on at once - Card hover: gentle -2px lift + slow image zoom on hero, home/grid tiles, and find cards; find-card lift switched to `translate` so it composes with the masonry's inline transform (it was silently dead before), and its transition list unified with .masonry-tile's to end their cascade tie - Tactile press/lift on "Load more" and the external CTA button - Accent-tinted ::selection, visible :focus-visible rings, smooth anchor scrolling; global prefers-reduced-motion catch-all disables it all Claude-Session: https://claude.ai/code/session_01ADR9r5rw5NuvvnCxfYbMuc
This commit is contained in:
@@ -212,9 +212,35 @@ const year = new Date().getFullYear();
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
body { transition: none; }
|
||||
}
|
||||
a { color: var(--accent); text-decoration: none; }
|
||||
a { color: var(--accent); text-decoration: none; transition: color 0.15s ease; }
|
||||
a:hover { text-decoration: underline; }
|
||||
|
||||
/* Cross-document view transitions — a soft cross-fade between pages in
|
||||
browsers that support it; everyone else just navigates. */
|
||||
@view-transition { navigation: auto; }
|
||||
|
||||
::selection { background: color-mix(in srgb, var(--accent) 24%, var(--bg)); }
|
||||
|
||||
:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
|
||||
|
||||
html { scroll-behavior: smooth; }
|
||||
|
||||
/* Media fade-in: the script at the foot of <body> tags not-yet-loaded
|
||||
images/videos with .fx-fade and flips .fx-in when each finishes, so
|
||||
media eases in instead of popping. Cached media never gets the class
|
||||
(no flash on repeat views); without JS nothing is ever hidden. */
|
||||
.fx-fade { opacity: 0; }
|
||||
.fx-fade.fx-in { opacity: 1; transition: opacity 0.55s ease; }
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
html { scroll-behavior: auto; }
|
||||
@view-transition { navigation: none; }
|
||||
*, *::before, *::after {
|
||||
transition-duration: 0.01ms !important;
|
||||
animation-duration: 0.01ms !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Masthead */
|
||||
.masthead {
|
||||
margin-bottom: 2rem;
|
||||
@@ -420,7 +446,16 @@ const year = new Date().getFullYear();
|
||||
font-weight: 600;
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
.external a:hover { text-decoration: none; opacity: 0.9; }
|
||||
.external a {
|
||||
transition: opacity 0.18s ease, translate 0.18s ease, box-shadow 0.18s ease;
|
||||
}
|
||||
.external a:hover {
|
||||
text-decoration: none;
|
||||
opacity: 0.92;
|
||||
translate: 0 -1px;
|
||||
box-shadow: 0 6px 16px -8px color-mix(in srgb, var(--accent) 70%, transparent);
|
||||
}
|
||||
.external a:active { translate: 0 0; }
|
||||
.external .aff-badge {
|
||||
display: inline-block;
|
||||
margin-left: 0.6rem;
|
||||
@@ -519,6 +554,27 @@ const year = new Date().getFullYear();
|
||||
.colophon dt:first-child { margin-top: 0; }
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
// Fade media in on load instead of popping — see .fx-fade in the global
|
||||
// styles. Only media still in flight is tagged, so cached images render
|
||||
// instantly with no flash.
|
||||
if (!window.matchMedia('(prefers-reduced-motion: reduce)').matches) {
|
||||
for (const img of document.querySelectorAll('img')) {
|
||||
if (img.complete) continue;
|
||||
const reveal = () => img.classList.add('fx-in');
|
||||
img.classList.add('fx-fade');
|
||||
img.addEventListener('load', reveal, { once: true });
|
||||
img.addEventListener('error', reveal, { once: true });
|
||||
}
|
||||
for (const video of document.querySelectorAll('video')) {
|
||||
if (video.readyState >= 2) continue;
|
||||
const reveal = () => video.classList.add('fx-in');
|
||||
video.classList.add('fx-fade');
|
||||
video.addEventListener('loadeddata', reveal, { once: true });
|
||||
video.addEventListener('error', reveal, { once: true });
|
||||
}
|
||||
}
|
||||
</script>
|
||||
{import.meta.env.DEV && <EditConfirm />}
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user