Safari & Testing

2026 CSS linear() easing for static HTML keyframes and transitions with Safari WebKit timing rehearsal on Mac

MacHTML Lab2026.05.0828 min read

Marketing teams still ship oversized cubic-bezier tuples copied from decade-old blog posts, then wonder why hero easing feels “sticky” on Safari while Chrome looks fine. The linear() easing function—pairs of progress stops expressed as percentages—lets you describe piecewise curves that would otherwise require chained keyframes or JavaScript springs. In 2026, static HTML exporters can keep bundles small by encoding nuanced deceleration directly inside animation-timing-function and transition-timing-function, but only if you reconcile the curve with scroll-driven timelines, prefers-reduced-motion, and WebKit’s compositor scheduling. This article walks through authoring rules, a browser matrix, and a rehearsal checklist on Apple Silicon. Treat easing as part of accessibility: if motion makes users queasy, no clever curve saves the experience.

Pair it with scroll-driven animations for static HTML when your easing must align with scroll ranges, and with scroll-padding for fixed headers so anchor-driven motion does not overshoot under sticky navigation.

Syntax recap and stop spacing

Each stop lists an output progress value followed by an input percentage. Duplicate percentages create hard corners; smooth ramps need at least three interior stops for asymmetric ease-out shapes. Keep total stops between five and nine: beyond that, DevTools graphs become unreadable and designers cannot diff changes in code review.

.hero-card {
  transition: transform 420ms
    linear(0, 0.12 8%, 0.32 24%, 0.87 68%, 1);
}

Validate that the final stop reaches 1 at 100% input; partial curves produce visible “early finishes” where elements snap the last few pixels without an obvious keyframe.

When linear() beats cubic-bezier

Needcubic-bezierlinear()Notes
Single gentle easeExcellentOverkillStick to classic curves for simple fades.
Multi-stage marketing overshootAwkwardNaturalEncode bounce as explicit stops.
Scroll-synced pacingRigidFlexibleMatch scroll percentages to input axis.

Coupling with scroll timelines

When animation-timeline tracks a named scroll timeline, treat the easing input axis as scroll progress, not time. Misaligned curves make sections feel like they accelerate twice—once from scroll velocity and once from easing—so dampen mid-curve slopes when scroll ranges are short (under 400px of scrollport travel).

prefers-reduced-motion strategies

Duplicate rules inside @media (prefers-reduced-motion: reduce) with durations capped at 120 ms for opacity transforms, or swap to linear(0, 1) instant finishes for transforms that would otherwise parallax. Never gate only on OS setting—some users keep motion enabled but run Safari Low Power Mode, which coalesces frames; watch for shimmer effects that look like bugs.

@supports layering and fallbacks

Ship the cubic-bezier line first, then override:

@supports (animation-timing-function: linear(0, 1)) {
  .hero-card { animation-timing-function: linear(0, 0.2 15%, 1); }
}

Keep both declarations in the same layer order so minifiers do not reorder them unpredictably.

Safari WebKit rehearsal checklist

  1. Record a screen capture at 120 fps if hardware allows; inspect frame pacing for micro-stutters.
  2. Toggle Reduce motion in macOS Accessibility and reload without cache.
  3. Stress test with Low Power Mode enabled on a laptop battery profile.
  4. Compare Technology Preview versus stable Safari for compositor regressions.

Compositor and jank discipline

Animating transform and opacity stays on the compositor; mixing filter blur with linear() easing can force main-thread blends on older GPUs. If marketing insists on blur-in, cap concurrent animated blurs to two elements per viewport and stagger starts by 40 ms.

Long static pages should mark offscreen sections with containment already described in your scroll-linked guides—pairing containment with easing prevents scroll-linked animations from fighting layout thrash when new fonts stream in.

Design handoff: translating Figma motion to linear()

Design tools export bezier handles, not linear stop lists. Ask designers for a normalized progress graph (0–100% time on the X axis, eased progress on Y). Fit linear() stops to that graph with least-squares intuition: start by anchoring the inflection points they label “brand snap” and “settle,” then interpolate midpoints until the visual delta drops below one CSS pixel of travel at the worst-case viewport width.

Store the approved curve as a design-token string in your component library README so future refactors do not silently swap easing when someone prettifies CSS. Version the string with a date comment—motion is part of brand equity.

Finally, schedule an annual motion audit: replay archived screen recordings against the current site to catch unintended drift when webfonts or line heights change—those invisible edits alter perceived timing even when linear() stops stay numerically identical.

Breakpoint matrix for static marketing breakpoints

Curves that feel crisp at 1440px wide can feel sluggish on 390px phones because the same percentage-based travel covers fewer pixels per frame. Maintain two easing tokens per component family—--ease-hero-wide and --ease-hero-narrow—and switch them at a container query threshold near 720px when you ship modern layout. Document the swap in your static HTML generator so translators cannot accidentally delete one branch.

will-change discipline for layered hero animations

Sprinkling will-change: transform across every card feels proactive but can exhaust GPU tile memory on Intel-era MacBook Air hardware still common in education buyers. Limit concurrent will-change hints to three elements in the viewport and clear hints after animations complete by toggling a class in animationend handlers. Pair that cleanup with linear() curves so you are not stacking compositor promotions unnecessarily.

prefers-reduced-data and motion coupling

Users who enable Low Data Mode on iOS Safari may still want subtle motion but cannot afford large LCP video cross-fades. Offer a third token set with shorter distances and no blur—linear() makes it easy to share the same stop structure with scaled output values. Document which assets swap when reduced data triggers so marketing does not ship invisible text contrast regressions.

FAQ

Does linear() replace cubic-bezier?

No—use each where it shines; cubic-bezier remains the default for simple transitions.

Is linear() safe with scroll-driven animations?

Yes with bounded scroll ranges and tested compositor behavior.

What about prefers-reduced-motion?

Shorten durations or remove spatial motion entirely.

How many stops should a linear() curve use?

Target five to nine stops for maintainability.

Easing is subjective work: designers sign off on curves by feel, which means you need the same display pipeline they use. A rented Mac mini with Apple Silicon reproduces Safari’s frame pacing, P3 gamma, and Night Shift tinting—details that change perceived “smoothness” even when mathematically identical curves run on a Linux screenshot bot. At roughly $16.9 per day, you can keep a QA host online through a launch week, capture side-by-side recordings, and shut the machine down when approvals land—no idle tower under a desk.

Elastic rental also helps agencies juggling multiple static clients: isolate each customer’s motion system on its own macOS profile without risking cross-contamination of font caches or preview ports.

Rehearse motion curves on real Safari hardware

Rent a cloud Mac mini to validate linear() easing, scroll timelines, and reduced-motion fallbacks on Apple Silicon before shipping static HTML.

Safari motion QA
From $16.9/Day