One Step Ahead Tim Kadlec @tkadlec.bsky.social linkedin.com/in/tkadlec/

100 150ms

100ms = 1%

100ms = 1% + 100ms = +.7%

100ms = 1% 100ms = 1% + + 100ms = +.7%

100ms = 1% 100ms = 1.1% 100ms = 1% + + + 100ms = +.7%

Expectations are not constant.

1 Jevons Paradox The more we have of a resource, the more we consume.

2 Hedonistic Treadmill r As resources become more powe ful, our expectations increase, diminishing our satisfaction.

r r Expectations are lea ned through expe ience.

r Oppo tunity.

r Core Web Vitals are a better sta ting point than a finish line.

r A retu n to the browser.

@view-transition { navigation: auto; }

<img

style=“ view-transition-name: u1-1748427501199-wemjtfslp” …

<img

style=“ view-transition-name: u1-1748427501199-wemjtfslp” …

<card-flip style=“ view-transition-name: u1-1748427416318-5btave6zs” … >

<card-flip style=“ view-transition-name: u1-1748427416318-5btave6zs” … >

<script type=”speculationrules”> { “prerender”: [{ “where”: { “href_matches”: “/*” }, “eagerness”: “moderate” }] } </script>

<script type=”speculationrules”> { “prerender”: [{ “where”: { “href_matches”: “/*” }, “eagerness”: “moderate” }] } </script>

What action to take

What action to take prefetch Fetch the HTML document. prerender Fetch, render and load the page in an invisible tab.

What action to take prefetch Less isk, less reward. Avoid prefetching pages that modify state. prerender r r More isk, more reward. Analytics & ads gotchas.

<script type=”speculationrules”> { “prerender”: [{ “where”: { “href_matches”: “/*” }, “eagerness”: “moderate” }] } </script>

What to apply it to

What to apply it to urls A list of specific URL’s where r A se ies of conditions to check against the document

{ } “prefetch”:[{ “urls”:[ “/page-two”, “/page-three” ] }]

{ “prefetch”: [ { “where”: { “and”: [ { “href_matches”: “/*” }, { “not”: {“href_matches”: “/logout”}} ] } } ] }

{ “prefetch”: [ { “where”: { “and”: [ { “href_matches”: “/*” }, { “not”: {“href_matches”: “/logout”}} ] } } ] }

{ “prefetch”: [ { “where”: { “and”: [ { “href_matches”: “/*” }, { “not”: {“href_matches”: “/logout”}} ] } } ] }

<script type=”speculationrules”> { “prerender”: [{ “where”: { “href_matches”: “/*” }, “eagerness”: “moderate” }] } </script>

When to apply it

immediate ASAP in idle time ASAP in idle time eager Experiment: Delay on hover (10ms) Planned: Eager Viewport Heuristics moderate Hovered over for 200ms, or pointerdown event Moderate viewport heuristics or pointerdown event Pointer or touch down Pointer or touch down conservative

immediate As soon as possible eager For now, same as immediate moderate Hovered over for 200ms, or pointerdown event conservative Pointer or touch down

r Using eage ness gives us a natural progression.

immediate ASAP in idle time ASAP in idle time eager Experiment: Delay on hover (10ms) Planned: Eager Viewport Heuristics moderate Hovered over for 200ms, or pointerdown event Moderate viewport heuristics or pointerdown event Pointer or touch down Pointer or touch down conservative

<script type=speculationrules> { “prefetch”: [ { “where”: { “selector_matches”: “[data-prefetch=”]” }, “eagerness”: “immediate” }, { “where”: { “and”: [ { “href_matches”: “/*” }, { “not”: { “selector_matches”: “[data-prefetch=false]” } } ] }, “eagerness”: “moderate” } ], “prerender”: [ { “where”: { “selector_matches”: “[data-prefetch=prerender]” }, “eagerness”: “immediate” }, { “where”: { “selector_matches”: “[data-prefetch=”]” }, “eagerness”: “moderate” } ] } </script>

https://csswizardry.com/2024/12/a-layered-approach-to-speculation-rules/

<script type=speculationrules> { “prefetch”: [ { “where”: { “selector_matches”: “[data-prefetch=”]” }, “eagerness”: “immediate” }, { “where”: { “and”: [ { “href_matches”: “/*” }, { “not”: { “selector_matches”: “[data-prefetch=false]” } } ] }, “eagerness”: “moderate” } ], “prerender”: [ { “where”: { “selector_matches”: “[data-prefetch=prerender]” }, “eagerness”: “immediate” }, { “where”: { “selector_matches”: “[data-prefetch=”]” }, “eagerness”: “moderate” } ] } </script>

<script type=speculationrules> { “prefetch”: [ { “where”: { “selector_matches”: “[data-prefetch=”]” }, “eagerness”: “immediate” }, { “where”: { “and”: [ { “href_matches”: “/*” }, { “not”: { “selector_matches”: “[data-prefetch=false]” } } ] }, “eagerness”: “moderate” } ], “prerender”: [ { “where”: { “selector_matches”: “[data-prefetch=prerender]” }, “eagerness”: “immediate” }, { “where”: { “selector_matches”: “[data-prefetch=”]” }, “eagerness”: “moderate” } ] } </script>

“prefetch”: [ { “where”: { “selector_matches”: “[data-prefetch=”]” }, “eagerness”: “immediate” }, <a href=”/link” data-prefetch>

<script type=speculationrules> { “prefetch”: [ { “where”: { “selector_matches”: “[data-prefetch=”]” }, “eagerness”: “immediate” }, { “where”: { “and”: [ { “href_matches”: “/*” }, { “not”: { “selector_matches”: “[data-prefetch=false]” } } ] }, “eagerness”: “moderate” } ], “prerender”: [ { “where”: { “selector_matches”: “[data-prefetch=prerender]” }, “eagerness”: “immediate” }, { “where”: { “selector_matches”: “[data-prefetch=”]” }, “eagerness”: “moderate” } ] } </script>

<script type=speculationrules> { “prefetch”: [ { “where”: { “selector_matches”: “[data-prefetch=”]” }, “eagerness”: “immediate” }, { “where”: { “and”: [ { “href_matches”: “/*” }, { “not”: { “selector_matches”: “[data-prefetch=false]” } } ] }, “eagerness”: “moderate” } ], “prerender”: [ { “where”: { “selector_matches”: “[data-prefetch=prerender]” }, “eagerness”: “immediate” }, { “where”: { “selector_matches”: “[data-prefetch=”]” }, “eagerness”: “moderate” } ] } </script>

{ “where”: { “and”: [ { “href_matches”: “/*” }, { “not”: { “selector_matches”: “[data-prefetch=false]” } } ] }, “eagerness”: “moderate” } <a href=”/less-important-link”> <a href=”https://google.com”>

<script type=speculationrules> { “prefetch”: [ { “where”: { “selector_matches”: “[data-prefetch=”]” }, “eagerness”: “immediate” }, { “where”: { “and”: [ { “href_matches”: “/*” }, { “not”: { “selector_matches”: “[data-prefetch=false]” } } ] }, “eagerness”: “moderate” } ], “prerender”: [ { “where”: { “selector_matches”: “[data-prefetch=prerender]” }, “eagerness”: “immediate” }, { “where”: { “selector_matches”: “[data-prefetch=”]” }, “eagerness”: “moderate” } ] } </script>

{ “where”: { “and”: [ { “href_matches”: “/*” }, { “not”: { “selector_matches”: “[data-prefetch=false]” } } ] }, “eagerness”: “moderate” } <a href=”/less-important-link”> <a href=”https://google.com”>

<script type=speculationrules> { “prefetch”: [ { “where”: { “selector_matches”: “[data-prefetch=”]” }, “eagerness”: “immediate” }, { “where”: { “and”: [ { “href_matches”: “/*” }, { “not”: { “selector_matches”: “[data-prefetch=false]” } } ] }, “eagerness”: “moderate” } ], “prerender”: [ { “where”: { “selector_matches”: “[data-prefetch=prerender]” }, “eagerness”: “immediate” }, { “where”: { “selector_matches”: “[data-prefetch=”]” }, “eagerness”: “moderate” } ] } </script>

<script type=speculationrules> { “prefetch”: [ { “where”: { “selector_matches”: “[data-prefetch=”]” }, “eagerness”: “immediate” }, { “where”: { “and”: [ { “href_matches”: “/*” }, { “not”: { “selector_matches”: “[data-prefetch=false]” } } ] }, “eagerness”: “moderate” } ], “prerender”: [ { “where”: { “selector_matches”: “[data-prefetch=prerender]” }, “eagerness”: “immediate” }, { “where”: { “selector_matches”: “[data-prefetch=”]” }, “eagerness”: “moderate” } ] } </script> <a href=”/prerender-me” data-prefetch=”prerender”> { “where”: { “selector_matches”: “[data-prefetch=prerender]” }, “eagerness”: “immediate” },

<script type=speculationrules> { “prefetch”: [ { “where”: { “selector_matches”: “[data-prefetch=”]” }, “eagerness”: “immediate” }, { “where”: { “and”: [ { “href_matches”: “/*” }, { “not”: { “selector_matches”: “[data-prefetch=false]” } } ] }, “eagerness”: “moderate” } ], “prerender”: [ { “where”: { “selector_matches”: “[data-prefetch=prerender]” }, “eagerness”: “immediate” }, { “where”: { “selector_matches”: “[data-prefetch=”]” }, “eagerness”: “moderate” } ] } </script>

<script type=speculationrules> { “prefetch”: [ { “where”: { “selector_matches”: “[data-prefetch=”]” }, “eagerness”: “immediate” }, { “where”: { “and”: [ { “href_matches”: “/*” }, { “not”: { “selector_matches”: “[data-prefetch=false]” } } ] }, “eagerness”: “moderate” } ], “prerender”: [ { “where”: { “selector_matches”: “[data-prefetch=prerender]” }, “eagerness”: “immediate” }, { “where”: { “selector_matches”: “[data-prefetch=”]” }, “eagerness”: “moderate” } ] } </script> <a href=”/link” data-prefetch> { “where”: { “selector_matches”: “[data-prefetch=”]” }, “eagerness”: “moderate” }

<script type=speculationrules> { “prefetch”: [ { “where”: { “selector_matches”: “[data-prefetch=”]” }, “eagerness”: “immediate” }, { “where”: { “and”: [ { “href_matches”: “/*” }, { “not”: { “selector_matches”: “[data-prefetch=false]” } } ] }, “eagerness”: “moderate” } ], “prerender”: [ { “where”: { “selector_matches”: “[data-prefetch=prerender]” }, “eagerness”: “immediate” }, { “where”: { “selector_matches”: “[data-prefetch=”]” }, “eagerness”: “moderate” } ] } </script>

<script type=speculationrules> { “prefetch”: [ { “where”: { “selector_matches”: “[data-prefetch=”]” }, “eagerness”: “immediate” }, { “where”: { “and”: [ { “href_matches”: “/*” }, { “not”: { “selector_matches”: “[data-prefetch=false]” } } ] }, “eagerness”: “moderate” } ], “prerender”: [ { “where”: { “selector_matches”: “[data-prefetch=prerender]” }, “eagerness”: “immediate” }, { “where”: { “selector_matches”: “[data-prefetch=”]” }, “eagerness”: “moderate” } ] } </script>

Clear-Site-Data: prefetchCache, prerenderCache

<script type=”speculationrules”> { “prefetch”: [ { “where”: { “and”: [ { “selector_matches”: “[data-prefetch=”]” }, { “not”: {“href_matches”: “/logout”}} ] }, “eagerness”: “immediate” } ], “prerender”: [ { “where”: { “selector_matches”: “[data-prefetch=prerender]” }, “eagerness”: “immediate” }, { “where”: { “and”: [ { “selector_matches”: “[data-prefetch=”]” }, { “not”: {“href_matches”: “/logout”}} ] }, “eagerness”: “moderate” } ] } </script>

<script type=”speculationrules”> { “prefetch”: [ { “where”: { “and”: [ { “selector_matches”: “[data-prefetch=”]” }, { “not”: {“href_matches”: “/logout”}} ] }, “eagerness”: “immediate” } ], “prerender”: [ { “where”: { “selector_matches”: “[data-prefetch=prerender]” }, “eagerness”: “immediate” }, { “where”: { “and”: [ { “selector_matches”: “[data-prefetch=”]” }, { “not”: {“href_matches”: “/logout”}} ] }, “eagerness”: “moderate” } ] } </script>

prefetch prerender immediate/eager 50 10 moderate/conservative 2 2

The fundamentals still matter.

@view-transition { navigation: auto; } <card-flip style=“ view-transition-name: u1-1748427416318-5btave6zs” … > <img style=“ view-transition-name: u1-1748427501199-wemjtfslp” … >

<script type=”speculationrules”> { “prefetch”: [ { “where”: { “and”: [ { “selector_matches”: “[data-prefetch=”]” }, { “not”: {“href_matches”: “/logout”}} ] }, “eagerness”: “immediate” } ], “prerender”: [ { “where”: { “selector_matches”: “[data-prefetch=prerender]” }, “eagerness”: “immediate” }, { “where”: { “and”: [ { “selector_matches”: “[data-prefetch=”]” }, { “not”: {“href_matches”: “/logout”}} ] }, “eagerness”: “moderate” } ] } </script>

: : : HTML 2 CSS 3 JSON 30

@keyframes spin-out { 0% { opacity: 1; transform: rotate(0deg) scale(1); } 100% { opacity: 0; transform: rotate(180deg) scale(0); } } @keyframes pop-in { 0% { opacity: 0; transform: scale(0.5) rotate(-180deg); } 100% { opacity: 1; transform: scale(1) rotate(0deg); } } /* Apply the animations to the view transition pseudo-elements */ ::view-transition-old(vitaly) { animation: 300ms ease-out forwards spin-out; } ::view-transition-new(vitaly) { animation: 500ms cubic-bezier(0.175, 0.885, 0.32, 1.275) forwards pop-in; }

: CSS 10

Freakin’ awesome.

r Betting on the browser is our best chance at long-te m success.

r The best abstractions are the ones that are willing to fade away when they’re no longer necessa y.

r r r r Good pe fo mance requires a uthless obsession for the user expe ience.

Let’s get to work.

Thank you Tim Kadlec @tkadlec.bsky.social linkedin.com/in/tkadlec/