Slide 1
One Step Ahead Tim Kadlec @tkadlec.bsky.social linkedin.com/in/tkadlec/
Slide 2
Slide 3
Slide 4
100ms = 1%
+
100ms = +.7%
Slide 5
100ms = 1%
100ms = 1% +
+
100ms = +.7%
Slide 6
100ms = 1%
100ms = 1.1% 100ms = 1% +
+
+
100ms = +.7%
Slide 7
Slide 8
Slide 9
Expectations are not constant.
Slide 10
1
Jevons Paradox The more we have of a resource, the more we consume.
Slide 11
Slide 12
Slide 13
Slide 14
2
Hedonistic Treadmill
r
As resources become more powe ful, our expectations increase, diminishing our satisfaction.
Slide 15
Slide 16
Slide 17
r
r
Expectations are lea ned through expe ience.
Slide 18
Slide 19
Slide 20
Slide 21
Slide 22
Slide 23
Slide 24
Slide 25
Slide 26
r
Core Web Vitals are a better sta ting point than a finish line.
Slide 27
Slide 28
r
A retu n to the browser.
Slide 29
Slide 30
Slide 31
@view-transition { navigation: auto; }
Slide 32
Slide 33
Slide 34
<img
style=“ view-transition-name: u1-1748427501199-wemjtfslp” …
Slide 35
<img
style=“ view-transition-name: u1-1748427501199-wemjtfslp” …
Slide 36
<card-flip style=“ view-transition-name: u1-1748427416318-5btave6zs” … >
Slide 37
<card-flip style=“ view-transition-name: u1-1748427416318-5btave6zs” … >
Slide 38
Slide 39
Slide 40
Slide 41
Slide 42
Slide 43
<script type=”speculationrules”> { “prerender”: [{ “where”: { “href_matches”: “/*” }, “eagerness”: “moderate” }] } </script>
Slide 44
<script type=”speculationrules”> { “prerender”: [{ “where”: { “href_matches”: “/*” }, “eagerness”: “moderate” }] } </script>
What action to take
Slide 45
What action to take prefetch Fetch the HTML document.
prerender Fetch, render and load the page in an invisible tab.
Slide 46
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.
Slide 47
<script type=”speculationrules”> { “prerender”: [{ “where”: { “href_matches”: “/*” }, “eagerness”: “moderate” }] } </script>
What to apply it to
Slide 48
What to apply it to urls A list of specific URL’s
where
r
A se ies of conditions to check against the document
Slide 49
{
}
“prefetch”:[{ “urls”:[ “/page-two”, “/page-three” ] }]
Slide 50
{ “prefetch”: [ { “where”: { “and”: [ { “href_matches”: “/*” }, { “not”: {“href_matches”: “/logout”}} ] } } ] }
Slide 51
{ “prefetch”: [ { “where”: { “and”: [ { “href_matches”: “/*” }, { “not”: {“href_matches”: “/logout”}} ] } } ] }
Slide 52
{ “prefetch”: [ { “where”: { “and”: [ { “href_matches”: “/*” }, { “not”: {“href_matches”: “/logout”}} ] } } ] }
Slide 53
<script type=”speculationrules”> { “prerender”: [{ “where”: { “href_matches”: “/*” }, “eagerness”: “moderate” }] } </script>
When to apply it
Slide 54
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
Slide 55
immediate As soon as possible
eager For now, same as immediate
moderate Hovered over for 200ms, or pointerdown event
conservative Pointer or touch down
Slide 56
r
Using eage ness gives us a natural progression.
Slide 57
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
Slide 58
<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/
Slide 59
<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>
Slide 60
<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>
Slide 61
<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>
Slide 62
<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”>
Slide 63
<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”>
Slide 64
<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>
Slide 65
<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”
},
Slide 66
<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>
Slide 67
<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”
}
Slide 68
<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>
Slide 69
<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>
Slide 70
Clear-Site-Data: prefetchCache, prerenderCache
Slide 71
<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>
Slide 72
<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>
Slide 73
Slide 74
Slide 75
Slide 76
Slide 77
Slide 78
Slide 79
prefetch
prerender
immediate/eager
50
10
moderate/conservative
2
2
Slide 80
Slide 81
Slide 82
Slide 83
Slide 84
Slide 85
The fundamentals still matter.
Slide 86
Slide 87
Slide 88
Slide 89
Slide 90
@view-transition { navigation: auto; } <card-flip style=“ view-transition-name: u1-1748427416318-5btave6zs” … > <img style=“ view-transition-name: u1-1748427501199-wemjtfslp” … >
Slide 91
<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>
Slide 92
:
:
:
HTML 2 CSS 3 JSON 30
Slide 93
Slide 94
@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; }
Slide 95
Slide 96
Slide 97
r
Betting on the browser is our best chance at long-te m success.
Slide 98
r
The best abstractions are the ones that are willing to fade away when they’re no longer necessa y.
Slide 99
r
r
r
r
Good pe fo mance requires a uthless obsession for the user expe ience.
Slide 100
Slide 101
Thank you Tim Kadlec @tkadlec.bsky.social linkedin.com/in/tkadlec/