Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Our Websites Need a Lifestyle Change, Not a Diet

Ryan Townsend
September 09, 2024

Our Websites Need a Lifestyle Change, Not a Diet

Ryan Townsend

September 09, 2024
Tweet

More Decks by Ryan Townsend

Other Decks in Programming

Transcript

  1. twnsnd.com/smashingconf 25% of all websites block visual feedback for more

    than 200ms after interactions on at least ¼ of all page views. source data: HTTPArchive, June 2024 Data
  2. twnsnd.com/smashingconf – Jeremy Wagner, Google “90% of a user’s time

    on a page is spent after it loads” source: web.dev/inp
  3. twnsnd.com/smashingconf 25% of all websites block visual feedback for more

    than 200ms after interactions on at least ¼ of all page views. source data: HTTPArchive, June 2024 Data
  4. twnsnd.com/smashingconf 25% of all websites block visual feedback for more

    than 200ms after interactions on at least ¼ of all page views. source data: HTTPArchive, June 2024 Data
  5. twnsnd.com/smashingconf 60% fail using Angular 57% fail using Next.js 39%

    fail using Vue.js 37% fail using React source: HTTPArchive, June 2024 Data
  6. twnsnd.com/smashingconf 85% pass using HTMX 82% pass using Eleventy 78%

    pass using Astro source: HTTPArchive, June 2024 Data
  7. twnsnd.com/smashingconf 25% of all websites block visual feedback for more

    than 200ms after interactions on at least ¼ of all page views. source data: HTTPArchive, June 2024 Data
  8. twnsnd.com/smashingconf 25% of all websites block visual feedback for more

    than 200ms after interactions on at least ¼ of all page views. source data: HTTPArchive, June 2024 Data
  9. twnsnd.com/smashingconf 100ms: the standard for ‘instant’ since the 60s source:

    Response time in man-computer conversational transactions – Robert Miller, 1968
  10. twnsnd.com/smashingconf “Respond to user actions within [100ms] and users feel

    like the result is immediate. Any longer, and the connection between action and reaction is broken.” source: web.dev/articles/rail
  11. twnsnd.com/smashingconf 25% of all websites block visual feedback for more

    than 200ms after interactions on at least ¼ of all page views. source data: HTTPArchive, June 2024 Data
  12. twnsnd.com/smashingconf 25% of all websites block visual feedback for more

    than 200ms after interactions on at least ¼ of all page views. source data: HTTPArchive, June 2024 Data
  13. twnsnd.com/smashingconf 25% of all websites block visual feedback in response

    to interaction for more than 200ms on at least ¼ of all page views. source data: HTTPArchive, June 2024 Data
  14. twnsnd.com/smashingconf • INP is recorded as the worst interaction on

    a given page* • The distribution of interactions on each page isn’t measured • This doesn’t necessarily give us a picture of what’s felt across a session * except on pages with >50 interactions, then the worst 1-in-50 is ignored
  15. twnsnd.com/smashingconf { " na m e ": " I N

    P ", " va l u e ": 1112, " r a ti ng ": " p o or ", " delt a": 1112, " ent r ie s ": [ { " na m e ": " po inter up ", " en try Typ e ": " e ve n t ", "sta rt T ime ": 3587.0999999046326, " du ra t i on ": 1112, " i nter a ction Id": 2992, "pr o ces s i ngS t a r t": 4667.200000047684, "pr o ces s i ng E nd ": 4667.200000047684, " c anc e l ab le": t r ue }, { " na m e ": "c li ck ", " en try Typ e ": " e ve n t ", "sta rt T ime ": 3587.0999999046326, " du ra t i on ": 1112, " i nter a ction Id": 2992, "pr o ces s i ngS t a r t": 4667.299999952316, "pr o ces s i ng E nd ": 4673.099999904633, " c anc e l ab le": t r ue } ], " i d": "v4 - 1723489247044 - 6849887513273", " n a v i ga tio nTyp e ": " b ac k - f o r wa rd", "a t tr ib uti o n ": { " i nter ac t io nT a r ge t ": "div. c o n tai ner _r ib bo n __ ca r d s -w ra pp er >div. c o n tai ner _r ib bo n __ ca r o us el - b utton - n e xt . c a r o us e l - but to n - n e xt ", " i nter ac t io nT a r ge t E l e m e nt ": {}, " i nter ac t io nTyp e ": " po inte r ", " i nter ac t io n Ti me ": 3587.0999999046326, " n e x t Pa intT ime ": 4699.099999904633, "pr o cesse d E v e n tEn t r i es ": [ { " na m e ": " p o inter up ", " ent r y Typ e ": " e ve n t ", "sta r t T ime ": 3587.0999999046326, " du ra tion ": 1112, " i nter acti on Id": 2992, "pr o ces s i n gSt a r t": 4667.200000047684, "pr o ces s i n g E n d ": 4667.200000047684, " c anc e l ab le": t r ue }, { " na m e ": " m o useu p ", " ent r y Typ e ": " e ve n t ", "sta r t T ime ": 3587.0999999046326, " du ra tion ": 1112, " i nter acti on Id": 0, "pr o ces s i n gSt a r t": 4667.200000047684, "pr o ces s i n g E n d ": 4667.200000047684, " c anc e l ab le": t r ue }, { " na m e ": "c lic k ", " ent r y Typ e ": " e ve n t ", "sta r t T ime ": 3587.0999999046326, " du ra tion ": 1112, " i nter acti on Id": 2992, "pr o ces s i n gSt a r t": 4667.299999952316, "pr o ces s i n g E n d ": 4673.099999904633, " c anc e l ab le": t r ue } ], " lo ngA ni m atio n F r a m e E nt r ie s ": [ { " na m e ": " l o n g - an im a ti on -f r a m e ", " ent r y Typ e ": " lo n g - an imati on -f ra m e ", "sta r t T ime ": 3516.7999999523163, " du ra tion ": 1160, " re nd er S t a r t": 4673.099999904633, "styl e An d L a yo u t S t a r t": 4673.200000047684, "fi r s t UI Ev e n tT im es t a m p ": 3537.0999999046326, "bl o c k i ng D u ra t io n ": 1099, " scr i pts": [ { " na m e ": " s cr i p t ", " entry Typ e ": " s cr i p t ", "sta rt T im e ": 3519, " du ra t ion ": 15, " in vok er ": "h t t ps :// re g is tr y. api . c n n . i o/ b un d l es / fa v e / to p i ns t a nc e -f0567b4c/ to p i ns t a nc e ", " in vok e r T yp e ": "cl a s s ic- scri p t ", "w in d o w At tr i bu t io n ": " sel f", " e xec u tio n St a r t": 3519, " f or c e dSt yl e An d L a yo utD u ra tion ": 0, " p aus e D u ra ti on ": 0, " s o u r c eUR L ": "h tt ps :// reg is tr y. api . c n n . i o/ b un d l es / fa v e / top i n s t a nc e -f0567b4c/ t op i ns t a nc e ", " s o u r c eFun ct io n N a m e ": "", " s o u r c e C h a r P o s i t i on ": 0 }, { " na m e ": " s cr i p t ", " entry Typ e ": " s cr i p t ", "sta rt T im e ": 3534.0999999046326, " du ra t ion ": 1130, " in vok er ": " S C R IP T [src=h ttps :// reg is tr y. ap i . c n n . i o/ b un d l es / f a v e / t op i ns t a nc e -f0567b4c/ top i ns t a nc e ]. o n l o a d ", " in vok e r T yp e ": " e ve nt -l i st e n e r ", "w in d o w At tr i bu t io n ": " sel f", " e xec u tio n St a r t": 3534.0999999046326, " f or c e dSt yl e An d L a yo utD u ra tion ": 0, " p aus e D u ra ti on ": 0, " s o u r c eUR L ": "", " s o u r c eFun ct io n N a m e ": "", " s o u r c e C h a r P o s i t i on ": - 1 } ] } ], " in p u tDe l ay ": 1080.1000001430511, "pr o ces s i ng D u ra ti o n ": 5.8999998569488525, "pr e se n ta tion D e l a y ": 26, " lo a d St ate": " dom - c o n t e nt - lo ade d " } }
  16. twnsnd.com/smashingconf b u tton . a d d E v

    e nt L i st e n e r ("c l ic k ", () => { // q ueu e ou r b eha v i o ur ( b a s ic e x a m p le) se tT i m e o ut ( f et ch N ewC on t en t , 0 ) })
  17. twnsnd.com/smashingconf – Barry Pollard, Google “Guys, come on... please don’t

    make us change INP to Interaction to Next Meaningful Paint”/s
  18. twnsnd.com/smashingconf In p ut De l ay Re nd er

    Pa in t Ma in Th r e a d Bu s y Pr o ces s i ng T ime
  19. twnsnd.com/smashingconf b u tton . a d d E v

    e nt L i st e n e r ("c l ic k ", ( e ve n t ) => { // U I fe edbac k : di s a b le o ur bu tt on e ve nt . t ar ge t .di s a b l e d = t ru e // q ueu e ou r b eha v i o ur ( b a s ic e x a m p le) se tT i m e o ut ( f et ch N ewC on t en t , 0 ) })
  20. twnsnd.com/smashingconf b u tton . a d d E v

    e nt L i st e n e r ("c l ic k ", a syn c ( e ve nt ) => { // U I fe edbac k : di s a b le o ur bu tt on e ve nt . t ar ge t .di s a b l e d = t ru e // ca l l our b eha v i ou r a sync h r o nou s ly a w ai t f et ch N ewCon t en t () })
  21. twnsnd.com/smashingconf b u tton . a d d E v

    e nt L i st e n e r ("c l ic k ", ( e ve n t ) => { // U I fe edbac k : di s a b le o ur bu tt on e ve nt . t ar ge t .di s a b l e d = t ru e // q ueu e ou r b eha v i o ur ( b a d e x a m p le) re que s t Anim atio n F r a m e ( f et ch N ew Co n t en t ) })
  22. twnsnd.com/smashingconf source: High Performance Browser Networking — Ilya Grigorik, 2013

    Delay User Perception 0-100ms Instant 100-300ms Small perceptible delay 300-1000ms Machine is working 1000+ms Likely mental context switch 10,000+ms Task is abandoned
  23. twnsnd.com/smashingconf In p ut De l ay 1 Re nd

    er 1 Pa in t 1 T as k 2 T as k 1 Re nd er 2 Pa in t 2
  24. twnsnd.com/smashingconf a syn c R e m o v e

    () { // U I fe edbac k : hi de o ur el e m e nt t his.style.di sp l a y = " n on e " // q ueu e re m o v al wh e n the m a in t hr e a d i s i dle re que s tI dl e C allb ac k ( t his. re m o v e ) }
  25. twnsnd.com/smashingconf r e que st I dl e C a

    llb ac k ( ca ll b ac k , { t i me o u t : 1000 })
  26. twnsnd.com/smashingconf d o W o rk() s e tT ime

    o ut (() => { do M o r e W o rk() se tT i m e o ut (() => { do E v e n M o r e W o rk() }, 0 ) }, 0 )
  27. twnsnd.com/smashingconf T as k 1A T as k 3 Re

    nd er 2 Pa in t 2 T as k 1B Re nd er 1 Pa in t 1
  28. twnsnd.com/smashingconf d o W o rk() a w ai t

    s ched u le r .yield() d o M o r e W o rk() a w ai t s ched u le r .yield() d o E v e n M o r e W o rk() NEXT RELEASE! explainer: github.com/WICG/scheduling-apis/blob/main/explainers/yield-and-continuation.md
  29. twnsnd.com/smashingconf yiel d T o M a in() { if

    (" s ched u le r " i n w in d ow && "yield" i n s ched u le r ) r eturn s ched u le r .yield() r eturn new Pr omis e ( r e s o l v e => { se tT ime o u t ( re s o l ve , 0 ) }) } a w ai t yiel d T o M a in() explainer: github.com/WICG/scheduling-apis/blob/main/explainers/yield-and-continuation.md
  30. twnsnd.com/smashingconf – DIDOMI “We observed a nearly 60-70% improvement in

    average INP score for CMP related actions [when yielding on consent]” source: youtu.be/Fyabr0GX3cU
  31. twnsnd.com/smashingconf w i n d ow . a d d

    E v e nt L i st e n e r (" re s i ze ", () => { // cl e ar a n y ex is tin g t im er if (w i n d ow . res i z e Tim er ) { cl e a rT ime o ut (w i n d o w . res i z e Ti me r ) } // sta r t a new t i mer w in d o w . res i z e Time r = s e tT im e o u t (() => { // . . .do so m e thi ng }, 1000 ) })
  32. twnsnd.com/smashingconf cl a s s T h ro t t

    le d S ea r c hF o r m e x t e nd s H TML E le m e n t { c onne c t ed C al lb ac k () { t his. in p ut = t his. q u e r y S e l ec to r (" in p u t ") t his. in p ut . a d d E v e n t L i st e n er (" in p ut ", t his) } ca llb ac k () { t his.t imer = n ul l // d o so m e thi n g w i t h ` t his. curr en t Va l u e ` } h an d l e E v e nt ( e ve n t ) { // store o ur c ur r e n t va l ue on e v e ry e ve n t t his. curre n t Va l u e = e ve n t . t ar ge t . va l u e // if d o n 't a l re ad y h av e a call b ac k q ueue d , e nqueu e o n e if (! t his.t i me r ) { t his.t im er = s e tT ime o u t (() => t his. c a l l b ac k (), 1000 ) } } }
  33. twnsnd.com/smashingconf a d d E v e n t L

    i st e n er (" i n p u t ", h an d leI n p ut , { d ebounc e : 200 }) a d d E v e n t L i st e n er (" p o inte rmo v e ", h an d l e P o inte rMo v e , { t hro t t l e: t rue }) source: github.com/WICG/webcomponents/issues/939
  34. twnsnd.com/smashingconf a syn c u pd a t eF i

    l t ers() { // 1. if we h av e an i n-pr o g r e s s fil t e r u pd a te, a b or t it if ( t his. a b ortContr o l l e r ) t his. a b ortCo nt r o ll e r . a b or t() // 2. i n i t i a lize a f r e s h a b or t c o ntr o l l e r t his. a b ortC ontr o ll e r = n ew A b or tC ontr o ll e r () // 3. get t he si g n al f ro m the c o n tr o ll e r c on s t a b ort S ig n al = t his. a b or tC on tr o ll e r .si g n al // 4. p as s the si g n a l t o o ur f et c h re que s t c on s t re spons e = a w a i t f et ch ("/ pa th/ t o/ e ndp o in t ", { si g n a l : a b o r t S i g n a l }) // 5. re a d out the r e spons e a n d p a r se th e HTM L c on s t re sponseTe xt = a w a i t re spons e . te xt () c on s t re spons e D oc = a w ai t new Pr o mise (( r e s o l v e ) => { re s o l ve ( n ew DOMPa r s er (). pa r s e F r omSt r in g ( re sponseTe xt , " te x t /h t m l ")) }) // 6. yield t o the m a in t hr e a d a w ai t s ched u le r .yield() // 7. c hec k the a b or t si g n al be fore u pd a t i ng th e DOM a b or t S ig n al . t hr owIfA b ort ed () // 8. u pd a te the DOM w it h the r e spons e // . . . } see: developer.mozilla.org/en-US/docs/Web/API/AbortController
  35. twnsnd.com/smashingconf Debounce Throttle Abort Throttle & Abort Time 💀 💀

    💀 Longest delays, quietest CPU/network Short delays, wasted CPU/network No delay, wasted CPU/network Shorter delays, quieter CPU/network 💀 💀 💀
  36. twnsnd.com/smashingconf // 1. on ly r e g iste r

    on d e v i c e s t h a t d o n 't s upp o r t s c ro l l-t im eli ne if (! C S S . s upp or t s("( sc ro l l-t imel i n e : -- na m e i n l i n e )")) { c u s t omEle m e n ts. def in e (" sc ro l l- s had ow ", S c ro l l S h ad ow ) } // 2. on ly r e g iste r on d e v i c e s s upp or t ing the W e b S ha re A PI if (" s h a re" i n n a v i g a to r && " c a n S h a re" i n n a v i g a to r ) { c u s t omEle m e n ts. def in e (" s h a re- b u t t on ", Sh a r e B u t t o n ) } // 3. on ly d e fi ne on l ar g er d e v i c e s if (w i n d ow . m a t c h M ed ia("(m i n-w i dth: 768 px ")) { c u s t omEle m e n ts. def in e (" d es k t op -s l ide r ", De s k t o p S l ide r ) }
  37. twnsnd.com/smashingconf YOU CAN HAVE A LITTLE 🐌 SLOW JAVASCRIPT 🐌

    YOU CAN HAVE A LOT OF 🏎 FAST JAVASCRIPT 🏎 BUT, GENERALLY: Mo’ JavaScript, mo’ problems
  38. twnsnd.com/smashingconf • Can you fi rst-party your data? e.g. instead

    of using JavaScript to inject product ratings, store them in product data and SSG/ESR/SSR them • Can you wait for an interaction? e.g. don't load customer reviews until the visitor actually opens the tab or scrolls near to the element • Do we still need these scripts? e.g. unnecessary poly fi lls and unused third-parties
  39. twnsnd.com/smashingconf • Avoid unnecessary, repetitive re-rendering • Limit the scope

    of render tree invalidations • Reduce elements requiring paint
  40. twnsnd.com/smashingconf <div> </ div> <div> </ div> <div> </ div>

    <div> </ div> <div> </ div> <div> </ div> <div> </ div> <div> </ div> < ! -- . . . --> AVOID TOO MANY SIBLINGS
  41. twnsnd.com/smashingconf : root { -- bo r der - rad

    i us : 5 px ; } // a v o id t his: d oc u m ent . d oc u m ent E le m ent .style. se tPr o p ert y('-- bo r der - rad i us ', '3 px ') AVOID MODIFYING GLOBAL CSS VARIABLES
  42. twnsnd.com/smashingconf < my - co m p one nt >

    < te m p late s had owrootmo d e =" o pe n "> <style> p { co lo r : red } </ style> <! -- S S R 'd c o n t ent he re: --> <p>I'll be red</ p> </te m p late> </my - co m p one nt > <p>I'll be bl a c k</ p> ENCAPSULATE WITH DECLARATIVE SHADOW DOM
  43. twnsnd.com/smashingconf . po t enti a ll y-o ffs c

    r e e n -el e m e nt { /* on ly re nd er c o n t en ts wh e n w i t hin/ n e ar in g t he vi e w por t */ c o n t en t -visib i li t y: a u to; /* de f ault s t o 100x500 px pl a ceh o l der g a p : */ c o n tain- i n t r ins ic-si z e : au to 100 px a u to 500 px ; } DEFER LAYOUT/PAINT OF OFF-SCREEN ELEMENTS
  44. twnsnd.com/smashingconf f u n ctio n n a v i

    ga t eWi thT r a n si t i on ( da ta) { // f a l lb ac k if (! d oc u m e nt .sta rt Vi e w T r a n si t io n ) { u pd a t eDOM ( da ta) r eturn } d oc u m e nt .sta rt Vi e w T r a n si t io n (() => u pd a t e DOM ( d a ta)) } see: developer.chrome.com/docs/web-platform/view-transitions LEVEL 1
  45. twnsnd.com/smashingconf <style> @vi ew -t ra n si t ion

    { n a v i gation : au to; } </ style> <div cl a s s ="pr o du ct "> < i mg src=" . . . " a l t=" . . . " style="vi ew -t ra n si t ion - na m e : pr o du c t - i m a g e -12345" / > </ div> see: developer.chrome.com/docs/web-platform/view-transitions LEVEL 2
  46. twnsnd.com/smashingconf < s cri p t t y p e

    =" s pec u la t i o n rul es "> { "prer e nd e r ": [ { "u r l s": ["/ n e xt "] }, { " wh e re": { "hre f _ m a t c he s ": "/pr o du c ts/*" }, " e ag e r n es s ": " m o d e rate" } ] } </ scr i p t > see: developer.chrome.com/docs/web-platform/prerender-pages
  47. twnsnd.com/smashingconf @ k e yf ra m e s s

    hr i nk l o g o { 0% { t ra n s fo r m: s c a le(1.33 ) ; } 100% { t ra n s fo r m: s c a le(1); } } @ s upp ort s ( a ni m at ion -t i melin e : vi e w ()) { . lo g o { a nima tio n : au to l in e ar s hri nk l o g o b o th; a nima tio n -t im eli n e : vi ew (); a nima tio n - r a ng e : en tr y c a lc(100 d v h - 5 p x ) ent r y c a lc(100 d v h + 5 p x ); } } see: scroll-driven-animations.style
  48. twnsnd.com/smashingconf <div> <style> @ s cop e { : s

    cop e { di sp l ay : g ri d } img { g ri d-ar e a : a v a ta r } h2 { g r i d-ar ea : na m e } p { g r i d-ar ea : e m ai l } } < / style> < img src=" a v a ta r . j pg " w i dth="100" he i ght ="100" lo a d i ng =" la z y " a l t=" R y a n 's a v a ta r " /> <h2 > Ry a n T o w n sen d< / h2 > <p> r y a n @ tw n sn d . com </ p> </ div>
  49. twnsnd.com/smashingconf < button ty p e =" button " comma

    n d =" s h ow - mo d al " comma n d fo r =" mobil e- me n u "> Op e n Me n u </button >
  50. twnsnd.com/smashingconf < b utton ty p e =" butto n

    " comma n d ="st ep - do w n " co mma n d fo r =" q ua n t i t y" a ri a- l a b el =" Dec r e m en t " > &m in u s; < / butt on > < i n p ut t y p e =" nu m ber " i d=" q ua n t i t y" m i n="0" max ="10" st e p ="1" / > < b utton ty p e =" butto n " comma n d ="st ep - up " co mm a n d fo r =" q ua nt i t y" a ri a- l a b el =" Inc r e m en t " > &pl u s; < /b utto n > 5 - +
  51. twnsnd.com/smashingconf • Collect and analyse your own data • Consider

    the uninitialised state • Prioritise visual feedback by yielding • Minimise work to be done • Abort no-longer-needed work • Use the platform where we can