250 lines
7.1 KiB
CSS
250 lines
7.1 KiB
CSS
/* ============================================================================
|
|||
|
|
HOME MOTION - the reveals the redesigned home page runs
|
||
|
|
----------------------------------------------------------------------------
|
||
|
|
Every animation in the Claude Design draft ("SHARP-ER Home v2 Dark"), carried
|
||
|
|
over as classes so the page modules stay free of inline keyframes and raw
|
||
|
|
values. Durations and easings are the motion tokens from semantic.css; the
|
||
|
|
few long, decorative loops are expressed as multiples of them rather than as
|
||
|
|
bare seconds, so retiming the system retimes the page.
|
||
|
|
|
||
|
|
TWO KINDS OF MOTION LIVE HERE
|
||
|
|
* entrance - time based, runs once from the server-rendered HTML
|
||
|
|
(.sh-rise, .sh-bar-grow), staggered with .sh-rise--2..5
|
||
|
|
* scroll - scroll-driven (`animation-timeline`), so a band reveals as
|
||
|
|
it enters the viewport and the top progress bar tracks the
|
||
|
|
document
|
||
|
|
|
||
|
|
WHY THE @supports GUARD: scroll-driven timelines ship in Chromium only. The
|
||
|
|
from-state (opacity 0) is applied ONLY where `animation-timeline: view()` is
|
||
|
|
understood, so Safari and Firefox render every band at rest, fully visible,
|
||
|
|
instead of holding an invisible element forever.
|
||
|
|
|
||
|
|
REDUCED MOTION: app.css already collapses every animation to 0.01ms. The
|
||
|
|
scroll block is additionally gated on `prefers-reduced-motion: no-preference`
|
||
|
|
so a reduced-motion visitor never gets a timeline-driven opacity ramp at all.
|
||
|
|
|
||
|
|
DIRECTION: `transform-origin` and `linear-gradient()` have no logical
|
||
|
|
keywords, so the progress bar and the brand stripe carry an explicit RTL
|
||
|
|
override. These are the only physical directions in the file, and they exist
|
||
|
|
because the property itself has no logical form - every box edge elsewhere is
|
||
|
|
still ps-/pe-/start-/end-.
|
||
|
|
========================================================================= */
|
||
|
|
|
||
|
|
@layer theme, base, components, utilities;
|
||
|
|
|
||
|
|
@layer components {
|
||
|
|
/* -------------------------------------------------------------------------
|
||
|
|
ENTRANCE - hero content, first paint
|
||
|
|
---------------------------------------------------------------------- */
|
||
|
|
.sh-rise {
|
||
|
|
animation: sh-rise var(--motion-duration-reveal) var(--motion-ease-entrance)
|
||
|
|
var(--sh-rise-delay, 0ms) both;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* The hero's cascade. The step is the system's fast duration, so the whole
|
||
|
|
stagger retimes with the token instead of drifting on hard-coded ms. */
|
||
|
|
.sh-rise--2 {
|
||
|
|
--sh-rise-delay: calc(var(--motion-duration-fast) * 0.75);
|
||
|
|
}
|
||
|
|
|
||
|
|
.sh-rise--3 {
|
||
|
|
--sh-rise-delay: calc(var(--motion-duration-fast) * 1.5);
|
||
|
|
}
|
||
|
|
|
||
|
|
.sh-rise--4 {
|
||
|
|
--sh-rise-delay: calc(var(--motion-duration-fast) * 2.25);
|
||
|
|
}
|
||
|
|
|
||
|
|
.sh-rise--5 {
|
||
|
|
--sh-rise-delay: calc(var(--motion-duration-fast) * 3);
|
||
|
|
}
|
||
|
|
|
||
|
|
/* The brand stripe under the hero picture, drawn on from the inline start. */
|
||
|
|
.sh-bar-grow {
|
||
|
|
transform-origin: left center;
|
||
|
|
animation: sh-bar-grow var(--motion-duration-reveal)
|
||
|
|
var(--motion-ease-entrance) calc(var(--motion-duration-fast) * 4) both;
|
||
|
|
}
|
||
|
|
|
||
|
|
[dir="rtl"] .sh-bar-grow {
|
||
|
|
transform-origin: right center;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* -------------------------------------------------------------------------
|
||
|
|
AMBIENT LOOPS - decorative, never carrying meaning
|
||
|
|
---------------------------------------------------------------------- */
|
||
|
|
|
||
|
|
/* The two soft washes behind the hero. Long, slow drifts: 22.5x and 30x the
|
||
|
|
reveal duration (18s / 24s at the shipped token). */
|
||
|
|
.sh-glow {
|
||
|
|
animation: sh-glow calc(var(--motion-duration-reveal) * 22.5) ease-in-out
|
||
|
|
infinite;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* A highlight travelling across the hero picture. */
|
||
|
|
.sh-sheen {
|
||
|
|
background-size: 240% 100%;
|
||
|
|
animation: sh-sheen calc(var(--motion-duration-reveal) * 8) linear infinite;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* The live dot in the hero status pill. */
|
||
|
|
.sh-pulse {
|
||
|
|
animation: sh-pulse calc(var(--motion-duration-reveal) * 2.75) ease-in-out
|
||
|
|
infinite;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* -------------------------------------------------------------------------
|
||
|
|
INTERACTION
|
||
|
|
---------------------------------------------------------------------- */
|
||
|
|
|
||
|
|
/* Tiles that rise a little under the pointer. No shadow: the draft's hover
|
||
|
|
glow is deliberately left out. */
|
||
|
|
.sh-lift {
|
||
|
|
transition:
|
||
|
|
transform var(--motion-duration-base) var(--motion-ease-standard),
|
||
|
|
border-color var(--motion-duration-base) var(--motion-ease-standard),
|
||
|
|
background-color var(--motion-duration-base) var(--motion-ease-standard);
|
||
|
|
}
|
||
|
|
|
||
|
|
@media (hover: hover) {
|
||
|
|
.sh-lift:hover {
|
||
|
|
transform: translateY(-5px);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/* An FAQ answer opening under its question. */
|
||
|
|
.sh-answer {
|
||
|
|
animation: sh-answer var(--motion-duration-base) var(--motion-ease-entrance)
|
||
|
|
both;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* -------------------------------------------------------------------------
|
||
|
|
SCROLL DRIVEN - Chromium today, inert and harmless elsewhere
|
||
|
|
---------------------------------------------------------------------- */
|
||
|
|
@media (prefers-reduced-motion: no-preference) {
|
||
|
|
@supports (animation-timeline: view()) {
|
||
|
|
.sh-reveal {
|
||
|
|
animation: sh-rise linear both;
|
||
|
|
animation-timeline: view();
|
||
|
|
animation-range: entry 0% entry 70%;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Cards and pictures: the same reveal with a touch of scale. */
|
||
|
|
.sh-reveal--in {
|
||
|
|
animation-name: sh-reveal-in;
|
||
|
|
animation-range: entry 0% entry 80%;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
@supports (animation-timeline: scroll()) {
|
||
|
|
/* The reading-progress rule across the top of the page. It is painted
|
||
|
|
ONLY inside this @supports block: without a scroll timeline the bar
|
||
|
|
would sit there permanently at full width, which is a rule the design
|
||
|
|
never asked for. */
|
||
|
|
.sh-progress {
|
||
|
|
background-image: linear-gradient(
|
||
|
|
to right,
|
||
|
|
var(--surface-accent),
|
||
|
|
var(--text-accent) 55%,
|
||
|
|
var(--surface-danger)
|
||
|
|
);
|
||
|
|
transform-origin: left center;
|
||
|
|
animation: sh-bar-grow linear both;
|
||
|
|
animation-timeline: scroll();
|
||
|
|
}
|
||
|
|
|
||
|
|
[dir="rtl"] .sh-progress {
|
||
|
|
background-image: linear-gradient(
|
||
|
|
to left,
|
||
|
|
var(--surface-accent),
|
||
|
|
var(--text-accent) 55%,
|
||
|
|
var(--surface-danger)
|
||
|
|
);
|
||
|
|
transform-origin: right center;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
@keyframes sh-rise {
|
||
|
|
from {
|
||
|
|
opacity: 0;
|
||
|
|
transform: translateY(26px);
|
||
|
|
}
|
||
|
|
|
||
|
|
to {
|
||
|
|
opacity: 1;
|
||
|
|
transform: none;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
@keyframes sh-reveal-in {
|
||
|
|
from {
|
||
|
|
opacity: 0;
|
||
|
|
transform: translateY(20px) scale(0.985);
|
||
|
|
}
|
||
|
|
|
||
|
|
to {
|
||
|
|
opacity: 1;
|
||
|
|
transform: none;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
@keyframes sh-bar-grow {
|
||
|
|
from {
|
||
|
|
transform: scaleX(0);
|
||
|
|
}
|
||
|
|
|
||
|
|
to {
|
||
|
|
transform: scaleX(1);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
@keyframes sh-glow {
|
||
|
|
0%,
|
||
|
|
100% {
|
||
|
|
opacity: 0.5;
|
||
|
|
transform: translate3d(-4%, 0, 0) scale(1);
|
||
|
|
}
|
||
|
|
|
||
|
|
50% {
|
||
|
|
opacity: 0.85;
|
||
|
|
transform: translate3d(6%, -3%, 0) scale(1.12);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
@keyframes sh-sheen {
|
||
|
|
from {
|
||
|
|
background-position: -160% 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
to {
|
||
|
|
background-position: 260% 0;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
@keyframes sh-pulse {
|
||
|
|
0%,
|
||
|
|
100% {
|
||
|
|
opacity: 1;
|
||
|
|
transform: scale(1);
|
||
|
|
}
|
||
|
|
|
||
|
|
50% {
|
||
|
|
opacity: 0.35;
|
||
|
|
transform: scale(0.72);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
@keyframes sh-answer {
|
||
|
|
from {
|
||
|
|
opacity: 0;
|
||
|
|
transform: translateY(-8px);
|
||
|
|
}
|
||
|
|
|
||
|
|
to {
|
||
|
|
opacity: 1;
|
||
|
|
transform: none;
|
||
|
|
}
|
||
|
|
}
|