<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>Cassondra Roberts</title>
  <subtitle>Personal blog by Cassondra Roberts — writing on front-end architecture, design systems, &amp; the web platform.</subtitle>
  <link href="https://castastrophe.blog/feed.xml" rel="self" />
  <link href="https://castastrophe.blog/" />
  <updated>2026-09-30T00:00:00.000Z</updated>
  <id>https://castastrophe.blog/</id>
  <rights>&#169; 2026 Cassondra Roberts</rights>
  <icon>https://castastrophe.blog/favicon.svg</icon>
  <author>
    <name>Cassondra Roberts</name>
  </author>
  <entry>
    <title>How Firefox renders web components</title>
    <link href="https://castastrophe.blog/guides/gecko-web-components-rendering/" />
    <updated>2026-09-30T00:00:00.000Z</updated>
    <id>https://castastrophe.blog/guides/gecko-web-components-rendering/</id>
    <content type="html">&lt;p&gt;Specs say what should happen; this is what Firefox actually does. I followed a custom element through Gecko&#39;s source, from the registry to the reactions stack to the flattened tree Stylo styles, and drew each step as a figure you can step through and replay.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://castastrophe.blog/guides/gecko-web-components-rendering/&quot;&gt;Read the guide on the site&lt;/a&gt;; its figures are interactive.&lt;/p&gt;</content>
    <summary>How Firefox turns a custom element into pixels, traced through Gecko&#39;s own source, from the registry to the reactions stack to the flattened tree Stylo styles.</summary>
    <category term="guides" />
    <category term="web components" />
    <category term="rendering" />
    <category term="firefox" />
  </entry>
  <entry>
    <title>Animating a favicon browsers refuse to animate</title>
    <link href="https://castastrophe.blog/blog/animating-a-favicon/" />
    <updated>2026-09-29T00:00:00.000Z</updated>
    <id>https://castastrophe.blog/blog/animating-a-favicon/</id>
    <content type="html"
      >&lt;p&gt;The favicon on this site is a bourbon tumbler. As you scroll, it either twirls — the drink sloshing against each change of direction — or you take a sip — tipping toward the title, holding a mouthful, and coming back a little emptier before you top it back up.&lt;/p&gt; &lt;p&gt;None of that is supposed to be possible. &lt;strong&gt;Browsers do not animate SVG favicons.&lt;/strong&gt; Whether you try an &lt;code&gt;&amp;lt;animate&amp;gt;&lt;/code&gt; element in
      &lt;code&gt;favicon.svg&lt;/code&gt;, or a CSS animation, or a &lt;a href=&quot;https://www.w3.org/TR/REC-smil/smil-timing.html&quot;&gt;SMIL timeline&lt;/a&gt;, the tab will render the first frame and ignore the rest. The favicon is chrome, not content, and the chrome is not a renderer.&lt;/p&gt; &lt;p&gt;...So how does this work?&lt;/p&gt; &lt;p&gt;It cheats. Every frame is drawn as an SVG string, rasterized through a canvas to a PNG data URL at build-of-the-page time, and then the
      &lt;code&gt;href&lt;/code&gt; of every &lt;code&gt;link[rel=&amp;quot;icon&amp;quot;]&lt;/code&gt; is swapped through those frames on a timer. The browser thinks the icon is changing, because it is. It just has no idea it is watching an animation.&lt;/p&gt; &lt;p class=&quot;callout&quot;&gt;The whole thing is about 370 lines in one module, with no dependencies, and it is skipped entirely under `prefers-reduced-motion`. This post is the interesting half: the physics shortcuts, the timing decisions,
      and the optimizations that keep it from being a silly thing to ship (though let&#39;s be honest, it is a little silly).&lt;/p&gt; &lt;h2 id=&quot;the-trick-is-lag%2C-not-simulation&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;#the-trick-is-lag%2C-not-simulation&quot;&gt;#&lt;/a&gt; The trick is lag, not simulation&lt;/h2&gt; &lt;p&gt;The obvious way to animate liquid in a glass is to &lt;em&gt;simulate&lt;/em&gt; liquid in a glass.
      &lt;strong&gt;Don&#39;t&lt;/strong&gt;. At the size this renders — 16 pixels, maybe 20 — a fluid solver would be several hundred lines producing a result indistinguishable from what can ultimately be done with only four lines of arithmetic.&lt;/p&gt; &lt;p&gt;Here is the entire physics model: &lt;strong&gt;every part of the drink is handed the glass&#39;s angle from a moment ago.&lt;/strong&gt; Not its own velocity, not a spring, not a force. Just the same curve, read late.&lt;/p&gt; &lt;figure&gt;
      &lt;svg viewBox=&quot;-14 0 604 216&quot; role=&quot;img&quot; aria-label=&quot;Three stacked timelines over the same swing. The glass angle rises and falls first. The liquid surface traces the identical curve shifted 40 milliseconds later, and the ice cube traces it again 150 milliseconds later, so the cube is still rising when the glass has already turned back.&quot;&gt; &lt;defs&gt; &lt;path id=&quot;lag-curve&quot; d=&quot;M70 40 C 130 40, 150 6, 200 6 S 270 62, 330 40 S 420 26, 500 34&quot;/&gt;
      &lt;/defs&gt; &lt;g font-size=&quot;13&quot; fill=&quot;var(--static-slate)&quot;&gt; &lt;text x=&quot;0&quot; y=&quot;44&quot;&gt;glass&lt;/text&gt; &lt;text x=&quot;0&quot; y=&quot;114&quot;&gt;liquid&lt;/text&gt; &lt;text x=&quot;0&quot; y=&quot;184&quot;&gt;ice&lt;/text&gt; &lt;/g&gt; &lt;g fill=&quot;none&quot; stroke-width=&quot;3&quot; stroke-linecap=&quot;round&quot;&gt; &lt;use href=&quot;#lag-curve&quot; stroke=&quot;var(--static-magenta)&quot;/&gt; &lt;use href=&quot;#lag-curve&quot;
      stroke=&quot;var(--static-orange)&quot; transform=&quot;translate(19 70)&quot;/&gt; &lt;use href=&quot;#lag-curve&quot; stroke=&quot;var(--static-cyan)&quot; transform=&quot;translate(72 140)&quot;/&gt; &lt;/g&gt; &lt;g stroke=&quot;var(--static-slate)&quot; stroke-width=&quot;1&quot; stroke-dasharray=&quot;3 3&quot; opacity=&quot;0.5&quot;&gt; &lt;path d=&quot;M200 6 V 198&quot;/&gt; &lt;path d=&quot;M219 76 V 198&quot;/&gt; &lt;path d=&quot;M272 146 V 198&quot;/&gt; &lt;/g&gt; &lt;g
      font-size=&quot;11&quot; fill=&quot;var(--static-slate)&quot;&gt; &lt;text x=&quot;223&quot; y=&quot;211&quot;&gt;+40ms&lt;/text&gt; &lt;text x=&quot;278&quot; y=&quot;211&quot;&gt;+150ms&lt;/text&gt; &lt;/g&gt; &lt;/svg&gt; &lt;figcaption&gt;One curve, read three times, drawn to scale. The surface trails the glass by 40ms because liquid answers fast; the cube trails by 150ms because mass answers late. Every bit of apparent physics comes out of that offset.&lt;/figcaption&gt; &lt;/figure&gt;
      &lt;p&gt;In code, that is unglamorous (which is the point):&lt;/p&gt; &lt;figure class=&quot;code-block&quot; data-lang=&quot;js&quot; data-edge=&quot;yellow&quot;&gt;&lt;div class=&quot;code-block-head&quot;&gt;&lt;figcaption&gt;&lt;span class=&quot;code-block-file&quot;&gt;pages/scripts/favicon-motion.js&lt;/span&gt;&lt;span class=&quot;code-block-lang&quot;&gt;JavaScript&lt;/span&gt;&lt;/figcaption&gt;&lt;div class=&quot;code-block-tools&quot;&gt;&lt;button type=&quot;button&quot;
      class=&quot;code-btn code-copy&quot; hidden&gt;Copy&lt;/button&gt;&lt;/div&gt;&lt;/div&gt;&lt;pre class=&quot;shiki&quot; tabindex=&quot;0&quot; role=&quot;region&quot; aria-label=&quot;Code sample: pages/scripts/favicon-motion.js&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-keyword&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;tk-function&quot;&gt; twirlFrame&lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt; =&lt;/span&gt;&lt;span&gt; (t) &lt;/span&gt;&lt;span
      class=&quot;tk-keyword&quot;&gt;=&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-function&quot;&gt; frame&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;tk-function&quot;&gt;twirlAngle&lt;/span&gt;&lt;span&gt;(t), &lt;/span&gt;&lt;span class=&quot;tk-function&quot;&gt;ice&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;tk-function&quot;&gt;twirlAngle&lt;/span&gt;&lt;span&gt;(t), &lt;/span&gt;&lt;span
      class=&quot;tk-function&quot;&gt;twirlAngle&lt;/span&gt;&lt;span&gt;(t &lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt;-&lt;/span&gt;&lt;span&gt; ICE_LAG)) &lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;tk-function&quot;&gt; liquid&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;tk-function&quot;&gt;twirlAngle&lt;/span&gt;&lt;span&gt;(t &lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt;-&lt;/span&gt;&lt;span&gt;
      LIQUID_LAG)));&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;p&gt;The cube also gets a second, subtler cue: it leans by whatever it still owes the glass — the difference between where the glass is and where the cube thinks it is. When the glass snaps back through center, the cube is still tilted the old way for a beat. Nobody consciously notices that. Everybody notices its absence, because without it the cube reads as painted on.&lt;/p&gt; &lt;h2
      id=&quot;the-drink-has-to-stay-level%2C-and-it-has-to-(preferably)-not-spill&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;#the-drink-has-to-stay-level%2C-and-it-has-to-(preferably)-not-spill&quot;&gt;#&lt;/a&gt; The drink has to stay level, and it has to (preferably) not spill&lt;/h2&gt; &lt;p&gt;Rotating a glass is trivial. Rotating a glass full of something is where it gets really interesting, because the liquid&#39;s surface is the one thing in the drawing that
      must &lt;strong&gt;ignore&lt;/strong&gt; the rotation — it lies horizontal no matter what the glass does.&lt;/p&gt; &lt;p&gt;The first attempt rotated the resting liquid shape backwards by the same angle the glass turned. That works for about eight degrees and then falls apart: the resting shape is a 45-pixel-tall &amp;quot;rectangle&amp;quot; (with a little curve for effect), and once it counter-rotates far enough one of its corners lifts off the bottom of the glass and you can see the floor through
      your bourbon.&lt;/p&gt; &lt;p&gt;What works is to stop thinking of it as a shape at all. The drink is an oversized rectangle, counter-rotated out of the glass&#39;s frame and clipped to it. There is always more rectangle than glass, so there is nothing to run out of.&lt;/p&gt; &lt;p&gt;That leaves one real problem, and it was the part I enjoyed most.&lt;/p&gt; &lt;h3&gt;Enter cosine&lt;/h3&gt; &lt;p&gt;If you hold the surface at a fixed height and tilt the glass, the glass
      &lt;strong&gt;gains&lt;/strong&gt; drink. The wedge that appears on the low side is bigger than the wedge that disappears on the high side, and the level visibly climbs through the sip. It looks like the glass is filling itself while you drink from it.&lt;/p&gt; &lt;p&gt;The area of liquid in a tilted container, while the surface still meets both walls, is:&lt;/p&gt; &lt;figure class=&quot;code-block&quot; data-lang=&quot;text&quot;&gt;&lt;div
      class=&quot;code-block-head&quot;&gt;&lt;figcaption&gt;&lt;span class=&quot;code-block-lang&quot;&gt;Text&lt;/span&gt;&lt;/figcaption&gt;&lt;div class=&quot;code-block-tools&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;code-btn code-copy&quot; hidden&gt;Copy&lt;/button&gt;&lt;/div&gt;&lt;/div&gt;&lt;pre class=&quot;shiki&quot; tabindex=&quot;0&quot; role=&quot;region&quot; aria-label=&quot;Code sample: Text&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;area = 2a · d /
      cos(θ)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;p&gt;where &lt;code&gt;a&lt;/code&gt; is the half-width and &lt;code&gt;d&lt;/code&gt; is the surface&#39;s perpendicular distance from the pivot. Hold &lt;code&gt;d&lt;/code&gt; constant and the area grows with the tilt. Scale &lt;code&gt;d&lt;/code&gt; by the cosine instead — &lt;code&gt;d = d₀ · cos(θ)&lt;/code&gt; — and the cosines cancel. The area is &lt;code&gt;2a · d₀&lt;/code&gt; at every angle. (Yes, I absolutely had
      to look these up!)&lt;/p&gt; &lt;figure&gt; &lt;svg viewBox=&quot;0 0 520 232&quot; role=&quot;img&quot; aria-label=&quot;Two tumblers. The upright one holds a level drink at depth d-nought below the base pivot. The second is the same tumbler rotated, with the surface still horizontal in the world but now only d-nought times the cosine of the tilt from the pivot; the shaded area inside the glass is the same in both.&quot;&gt; &lt;defs&gt; &lt;path id=&quot;tumbler&quot; d=&quot;M-55 -130 H 55 L 50 -10
      Q 50 0 40 0 H -40 Q -50 0 -50 -10 Z&quot;/&gt; &lt;clipPath id=&quot;clip-upright&quot;&gt;&lt;use href=&quot;#tumbler&quot; transform=&quot;translate(130 172)&quot;/&gt;&lt;/clipPath&gt; &lt;clipPath id=&quot;clip-tilted&quot;&gt;&lt;use href=&quot;#tumbler&quot; transform=&quot;translate(370 172) rotate(22)&quot;/&gt;&lt;/clipPath&gt; &lt;/defs&gt; &lt;g fill=&quot;var(--static-orange)&quot; opacity=&quot;0.7&quot;&gt; &lt;rect x=&quot;0&quot; y=&quot;120&quot; width=&quot;260&quot;
      height=&quot;120&quot; clip-path=&quot;url(#clip-upright)&quot;/&gt; &lt;rect x=&quot;250&quot; y=&quot;123.8&quot; width=&quot;270&quot; height=&quot;120&quot; clip-path=&quot;url(#clip-tilted)&quot;/&gt; &lt;/g&gt; &lt;g fill=&quot;none&quot; stroke=&quot;var(--static-ink)&quot; stroke-width=&quot;3&quot; stroke-linejoin=&quot;round&quot;&gt; &lt;use href=&quot;#tumbler&quot; transform=&quot;translate(130 172)&quot;/&gt; &lt;use href=&quot;#tumbler&quot; transform=&quot;translate(370 172)
      rotate(22)&quot;/&gt; &lt;/g&gt; &lt;g stroke=&quot;var(--static-magenta)&quot; stroke-width=&quot;2&quot; stroke-dasharray=&quot;5 4&quot;&gt; &lt;path d=&quot;M130 120 V 172&quot;/&gt; &lt;path d=&quot;M370 123.8 V 172&quot;/&gt; &lt;/g&gt; &lt;g fill=&quot;var(--static-magenta)&quot;&gt; &lt;circle cx=&quot;130&quot; cy=&quot;172&quot; r=&quot;3&quot;/&gt; &lt;circle cx=&quot;370&quot; cy=&quot;172&quot; r=&quot;3&quot;/&gt; &lt;/g&gt; &lt;g font-size=&quot;13&quot;
      fill=&quot;var(--static-magenta)&quot; text-anchor=&quot;middle&quot;&gt; &lt;text x=&quot;130&quot; y=&quot;112&quot;&gt;d₀&lt;/text&gt; &lt;text x=&quot;370&quot; y=&quot;112&quot;&gt;d₀·cos θ&lt;/text&gt; &lt;/g&gt; &lt;g font-size=&quot;12&quot; fill=&quot;var(--static-slate)&quot;&gt; &lt;text x=&quot;86&quot; y=&quot;206&quot;&gt;upright&lt;/text&gt; &lt;text x=&quot;300&quot; y=&quot;206&quot;&gt;tilted — same volume&lt;/text&gt; &lt;/g&gt; &lt;/svg&gt; &lt;figcaption&gt;The drink is a
      world-horizontal slab clipped to the glass — which is exactly how the code draws it. Only its distance from the pivot changes, and shrinking that by the cosine of the tilt is what stops the glass inventing bourbon.&lt;/figcaption&gt; &lt;/figure&gt; &lt;p&gt;That formula is also what sets the deepest the glass is allowed to tip. It only holds while the surface still touches both walls — past roughly 27° the raised corner runs dry, the trapezoid becomes a triangle, and the maths silently stops
      describing the picture. The sip tips 24°, with the margin deliberate rather than lucky.&lt;/p&gt; &lt;h2 id=&quot;timing%2C-part-one%3A-realism&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;#timing%2C-part-one%3A-realism&quot;&gt;#&lt;/a&gt; Timing, part one: realism&lt;/h2&gt; &lt;p&gt;Two numbers took the longest to settle on, and neither of them was related to the physics.&lt;/p&gt; &lt;p&gt;&lt;strong&gt;The hold.&lt;/strong&gt; The first sip tipped, touched,
      and came straight back — 580ms at full tilt. On replay, it read to me as flinching, not drinking. I slowed it to 1280ms, which is more than half the sequence, and that gave us a sip instead of a sniff.&lt;/p&gt; &lt;p&gt;&lt;strong&gt;The drift.&lt;/strong&gt; Adding the longer hold introduced a new problem: the liquid and the cube settle within about 150ms of arriving, and then nothing moves. A frozen frame for over a second does not read as a pause, it reads as a stall — your eye assumes the
      animation broke. So the glass drifts 1.5° across the hold. It is far too small to consciously see and it is the entire difference between a held breath and a crash.&lt;/p&gt; &lt;p&gt;In truth, &lt;strong&gt;stillness in an animation has to be active.&lt;/strong&gt; If the thing has stopped moving entirely, nobody can tell the difference between a pause and a bug.&lt;/p&gt; &lt;h2 id=&quot;timing%2C-part-two%3A-readability-at-16-pixels&quot; tabindex=&quot;-1&quot;&gt;&lt;a
      class=&quot;header-anchor&quot; href=&quot;#timing%2C-part-two%3A-readability-at-16-pixels&quot;&gt;#&lt;/a&gt; Timing, part two: readability at 16 pixels&lt;/h2&gt; &lt;p&gt;Everything described above is invisible if the drawing does not survive the size it is displayed at, and a favicon is displayed at a size where basically nothing survives.&lt;/p&gt; &lt;p&gt;The original mark had the drink filling about a fifth of the glass interior. At 16 pixels, that is a band about two and a half pixels tall —
      enough to see if you already know it is there, not enough to read as &lt;em&gt;liquid&lt;/em&gt;. I raised the resting level to about a third of the interior, which roughly doubled it, and that was the single largest improvement to the whole effect.&lt;/p&gt; &lt;p&gt;It also cost more than it looked like it should, because of this constraint:&lt;/p&gt; &lt;blockquote&gt; &lt;p&gt;The last frame of the fill animation has to be pixel-identical to the static favicon, or the icon visibly jumps the moment
      the animation ends and the real file is restored.&lt;/p&gt; &lt;/blockquote&gt; &lt;p&gt;The animation&#39;s frames come from a script. The resting icon comes from a file. Raising the level meant moving both — the script constant, the SVG&#39;s path data, and both PNG fallbacks regenerated from the SVG so all three stay the same drawing. Four files that now move together or not at all.&lt;/p&gt; &lt;p&gt;And the tail on that: browsers keep favicons in their own cache, which a normal reload does not
      clear. Change the artwork without versioning the URL and returning visitors get the animation at the new level and the resting icon at the old one — which looks &lt;em&gt;exactly&lt;/em&gt; like an animation bug, and is not one. The icon hrefs carry a &lt;code&gt;?v=&lt;/code&gt; for that reason.&lt;/p&gt; &lt;h2 id=&quot;the-trade-offs&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;#the-trade-offs&quot;&gt;#&lt;/a&gt; The trade-offs&lt;/h2&gt; &lt;p&gt;This is an
      ornament. An ornament is allowed to cost approximately nothing, so most of the engineering went into making it cheap.&lt;/p&gt; &lt;h3&gt;Every frame is retained, so the frame rate is a memory setting&lt;/h3&gt; &lt;p&gt;The frames are PNG data URLs held in an array for the life of the page. Three sequences come to about 120 frames. At 30fps it was 147, and the difference is pure retained string. I dropped it to &lt;strong&gt;24fps&lt;/strong&gt;, which took roughly a fifth off both the rasterizing at
      load and the memory held afterwards, and at 16 pixels I cannot tell the two apart. I chose that frame rate for bytes, not smoothness.&lt;/p&gt; &lt;h3&gt;One canvas, not one per frame&lt;/h3&gt; &lt;p&gt;The naive rasterizer allocates a canvas per frame. There is no reason for that — one shared canvas does fine, as long as you clear it:&lt;/p&gt; &lt;figure class=&quot;code-block&quot; data-lang=&quot;js&quot; data-edge=&quot;yellow&quot;&gt;&lt;div
      class=&quot;code-block-head&quot;&gt;&lt;figcaption&gt;&lt;span class=&quot;code-block-file&quot;&gt;pages/scripts/favicon-motion.js&lt;/span&gt;&lt;span class=&quot;code-block-lang&quot;&gt;JavaScript&lt;/span&gt;&lt;/figcaption&gt;&lt;div class=&quot;code-block-tools&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;code-btn code-copy&quot; hidden&gt;Copy&lt;/button&gt;&lt;/div&gt;&lt;/div&gt;&lt;pre class=&quot;shiki&quot; tabindex=&quot;0&quot; role=&quot;region&quot; aria-label=&quot;Code
      sample: pages/scripts/favicon-motion.js&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-keyword&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;tk-function&quot;&gt; rasterise&lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt; =&lt;/span&gt;&lt;span&gt; (svg) &lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt;=&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-keyword&quot;&gt; new&lt;/span&gt;&lt;span
      class=&quot;tk-function&quot;&gt; Promise&lt;/span&gt;&lt;span&gt;((resolve, reject) &lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt;=&gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-keyword&quot;&gt; const&lt;/span&gt;&lt;span&gt; image &lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt; new&lt;/span&gt;&lt;span class=&quot;tk-function&quot;&gt;
      Image&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; image.&lt;/span&gt;&lt;span class=&quot;tk-function&quot;&gt;onload&lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt; =&lt;/span&gt;&lt;span&gt; () &lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt;=&gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line highlighted&quot;&gt;&lt;span&gt; ctx.&lt;/span&gt;&lt;span
      class=&quot;tk-function&quot;&gt;clearRect&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;tk-number&quot;&gt;0&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span class=&quot;tk-number&quot;&gt;0&lt;/span&gt;&lt;span&gt;, SIZE, SIZE); &lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; ctx.&lt;/span&gt;&lt;span class=&quot;tk-function&quot;&gt;drawImage&lt;/span&gt;&lt;span&gt;(image, &lt;/span&gt;&lt;span class=&quot;tk-number&quot;&gt;0&lt;/span&gt;&lt;span&gt;,
      &lt;/span&gt;&lt;span class=&quot;tk-number&quot;&gt;0&lt;/span&gt;&lt;span&gt;, SIZE, SIZE);&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-function&quot;&gt; resolve&lt;/span&gt;&lt;span&gt;(canvas.&lt;/span&gt;&lt;span class=&quot;tk-function&quot;&gt;toDataURL&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;&quot;image/png&quot;&lt;/span&gt;&lt;span&gt;));&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;
      };&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; image.onerror &lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt;=&lt;/span&gt;&lt;span&gt; reject;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; image.src &lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt; `data:image/svg+xml;charset=utf-8,${&lt;/span&gt;&lt;span class=&quot;tk-function&quot;&gt;encodeURIComponent&lt;/span&gt;&lt;span
      class=&quot;tk-string&quot;&gt;(svg)&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;}`&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; });&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;p&gt;That &lt;code&gt;clearRect&lt;/code&gt; is not defensive tidiness, it is load-bearing. The mark never covers the full square, so without it every frame composites over the one before and the icon turns into a smear. I checked: remove the
      clear and 116 of 117 frames come out different.&lt;/p&gt; &lt;h3&gt;The fill sequence is a lookup table&lt;/h3&gt; &lt;p&gt;The best optimization here was noticing something already existed. The page-load fill animates the drink from empty to full — which means it contains a frame for &lt;em&gt;every level in between&lt;/em&gt;.&lt;/p&gt; &lt;p&gt;So nothing else ever renders a partly-filled glass. It asks the fill which frame matches the level it wants:&lt;/p&gt; &lt;ul&gt;
      &lt;li&gt;&lt;strong&gt;Draining while the tab is hidden&lt;/strong&gt; is the fill sequence read backwards.&lt;/li&gt; &lt;li&gt;&lt;strong&gt;Refilling when you come back&lt;/strong&gt; is the fill played forward from wherever you left off.&lt;/li&gt; &lt;li&gt;&lt;strong&gt;The sip&#39;s top-up&lt;/strong&gt; hands straight to the fill at the frame matching what the mouthful took.&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;Three behaviors, no new frames, and the ice sinking as the level drops came free — the
      fill already animated the cube settling as the drink rose, so running it backwards drops the cube accurately without adding an extra line.&lt;/p&gt; &lt;h3&gt;You cannot animate a hidden tab, so don&#39;t&lt;/h3&gt; &lt;p&gt;The most interesting constraint. A favicon is the one part of a page still visible after you switch away, which is the argument for doing something there. The argument against doing it with &lt;em&gt;animation&lt;/em&gt; is that hidden tabs have no frame rate to spend: timers
      clamp to roughly a second, and Chrome cuts them to once a minute after five. A two-second sequence would take nearly a minute of lurching and then stall.&lt;/p&gt; &lt;p&gt;So the background behavior is not an animation at all. The level drops one swallow every 30 seconds, four swallows to empty, and then the timer stops — one timer per swallow, not a polling loop, and all the background work is over inside two minutes. The five-minute throttle never even applies.&lt;/p&gt; &lt;p&gt;Designing
      &lt;em&gt;for&lt;/em&gt; the constraint got a better result than fighting it would have. A glass that is emptier when you come back is a nicer idea than a stuttering 1fps rock, and it costs a rounding error.&lt;/p&gt; &lt;h3&gt;A rotation needs more room than you think&lt;/h3&gt; &lt;p&gt;This one cost me an afternoon: the glass nearly fills its own viewBox, so rotating it at all pushes a corner outside the frame. The original 8° tilt had been quietly clipping its own corners for months. Everything
      tilted now goes through a fit pass that scales down only as far as the overflow demands and nudges what is left back inside — and is the identity at 0°, so the resting frame still matches the static file exactly.&lt;/p&gt; &lt;p&gt;Its side effect turned out to be a feature. The nudge slides the glass against the tilt, which converts a pivot at the base into an effective pivot around the middle — which is what tipping a glass toward your mouth actually looks like.&lt;/p&gt; &lt;h2
      id=&quot;what-it-degrades-to&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;#what-it-degrades-to&quot;&gt;#&lt;/a&gt; What it degrades to&lt;/h2&gt; &lt;ul&gt; &lt;li&gt;&lt;strong&gt;Reduced motion&lt;/strong&gt;: the whole module is behind &lt;code&gt;prefers-reduced-motion: no-preference&lt;/code&gt;. Nothing renders, nothing rasterizes.&lt;/li&gt; &lt;li&gt;&lt;strong&gt;Safari&lt;/strong&gt;: does not repaint favicons on &lt;code&gt;href&lt;/code&gt; changes, so
      none of this is visible there. It fails silently and costs one wasted rasterizing pass.&lt;/li&gt; &lt;li&gt;&lt;strong&gt;No canvas&lt;/strong&gt;: guarded, skipped.&lt;/li&gt; &lt;li&gt;&lt;strong&gt;Sizeless SVG&lt;/strong&gt;: worth knowing if you build something similar — an SVG with only a &lt;code&gt;viewBox&lt;/code&gt; and no &lt;code&gt;width&lt;/code&gt;/&lt;code&gt;height&lt;/code&gt; is a long-standing rasterizing hazard outside Chromium. The frames carry explicit dimensions
      now.&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;The static favicon is always the real file, and the animation is always additive. If every part of this fails, you have an icon of a glass of bourbon, which was the requirement in the first place.&lt;/p&gt; &lt;h2 id=&quot;was-it-worth-it&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;#was-it-worth-it&quot;&gt;#&lt;/a&gt; Was it worth it&lt;/h2&gt; &lt;p&gt;Absolutely not, and I would do it again. It is a tab icon that nobody
      asked for, that Safari cannot see, that is invisible to anyone with reduced motion on, and that occupies about 250 square pixels of anyone&#39;s screen.&lt;/p&gt; &lt;p&gt;But the constraints were genuinely interesting — a rendering target that refuses to render, a frame budget measured in kilobytes of string, a background state with no frames at all — and every one of them turned out to have a cleaner answer than the brute-force version. That is the kind of problem that is fun precisely
      &lt;em&gt;because&lt;/em&gt; the stakes are zero.&lt;/p&gt; &lt;p&gt;Scroll, and mine is having a drink.&lt;/p&gt;
    </content>
    <summary>Browsers will not animate an SVG favicon, so this one is 120 pre-rendered PNGs swapped through a link tag at 24fps. Here is how the drink stays level, why the ice is always 150ms late, and every trade-off that made it cheap enough to ship.</summary>
    <category term="blog" />
    <category term="animation" />
    <category term="svg" />
    <category term="performance" />
  </entry>
  <entry>
    <title>Introducing envoy: environment setup, handled</title>
    <link href="https://castastrophe.blog/blog/introducing-envoy/" />
    <updated>2026-03-26T00:00:00.000Z</updated>
    <id>https://castastrophe.blog/blog/introducing-envoy/</id>
    <content type="html"
      >&lt;p&gt;Here&#39;s what nobody tells you about onboarding: the hardest part isn&#39;t the architecture docs, the branching strategy, or even getting the right access. It&#39;s &lt;code&gt;.env&lt;/code&gt;.&lt;/p&gt; &lt;p&gt;Every project I&#39;ve ever worked on starts the same way. Copy &lt;code&gt;.env.example&lt;/code&gt; to &lt;code&gt;.env&lt;/code&gt;. Open Notion, or 1Password, or a Slack thread from six months ago. Track down the values. Paste them in. Repeat. In a monorepo, this ritual
      scales badly — five packages, five &lt;code&gt;.env.example&lt;/code&gt; files, five rounds of the same scavenger hunt. It&#39;s not hard work. It&#39;s just tedious work, and tedious work is the kind that gets skipped, done wrong, or turned into a half-finished onboarding doc that nobody updates.&lt;/p&gt; &lt;p&gt;I built &lt;strong&gt;&lt;a href=&quot;https://github.com/allonsy-studio/envoy&quot;&gt;envoy&lt;/a&gt;&lt;/strong&gt; as a way to eliminate it.&lt;/p&gt; &lt;h2
      id=&quot;the-problem-with-.env-management&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;#the-problem-with-.env-management&quot;&gt;#&lt;/a&gt; The problem with &lt;code&gt;.env&lt;/code&gt; management&lt;/h2&gt; &lt;p&gt;Let&#39;s be honest about what &lt;code&gt;.env&lt;/code&gt; setup actually is: a key lookup exercise. You have a template (&lt;code&gt;.env.example&lt;/code&gt;) that tells you what keys a project needs, and you have the real values stored
      somewhere on your machine or in your secrets manager. The only &amp;quot;work&amp;quot; involved is matching those two things together — and yet we do it manually, every time, for every project.&lt;/p&gt; &lt;p&gt;There are a few ways this tends to go wrong in practice.&lt;/p&gt; &lt;p&gt;&lt;strong&gt;The incomplete setup.&lt;/strong&gt; A contributor clones the repo, misses a value in the &lt;code&gt;.env.example&lt;/code&gt;, and spends the next hour debugging why a feature isn&#39;t working. The
      error message points nowhere near the actual problem.&lt;/p&gt; &lt;p&gt;&lt;strong&gt;The stale example file.&lt;/strong&gt; Someone adds a new required key to the codebase and forgets to document it in &lt;code&gt;.env.example&lt;/code&gt;. Now the example file lies. Every new contributor is set up to fail before they write a single line of code.&lt;/p&gt; &lt;p&gt;&lt;strong&gt;The monorepo multiplication problem.&lt;/strong&gt; Package A needs &lt;code&gt;DATABASE_URL&lt;/code&gt;. Package B needs
      &lt;code&gt;API_KEY&lt;/code&gt;. Package C needs both plus three others. You&#39;re now maintaining five different &lt;code&gt;.env&lt;/code&gt; files by hand and hoping none of them drift out of sync. In practice, they always drift.&lt;/p&gt; &lt;p&gt;The solution to all three is the same: treat &lt;code&gt;.env&lt;/code&gt; setup as a deterministic process that can be automated, not a manual ritual that depends on tribal knowledge.&lt;/p&gt; &lt;h2 id=&quot;how-envoy-works&quot;
      tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;#how-envoy-works&quot;&gt;#&lt;/a&gt; How envoy works&lt;/h2&gt; &lt;p&gt;The premise is straightforward. You probably already have a &lt;code&gt;~/.env&lt;/code&gt; on your machine — or you will after using envoy for five minutes — with the real values: API keys, database URLs, tokens, secrets. The values that follow you from project to project.&lt;/p&gt; &lt;p&gt;&lt;strong&gt;envoy&lt;/strong&gt; reads your
      &lt;code&gt;.env.example&lt;/code&gt; as a template, pulls matching keys from &lt;code&gt;~/.env&lt;/code&gt;, and writes a complete &lt;code&gt;.env&lt;/code&gt; file alongside it. Comments, blank lines, and keys you haven&#39;t defined yet all survive the process exactly as written.&lt;/p&gt; &lt;figure class=&quot;code-block&quot; data-lang=&quot;shellsession&quot; data-edge=&quot;magenta&quot;&gt;&lt;div class=&quot;code-block-head&quot;&gt;&lt;figcaption&gt;&lt;span
      class=&quot;code-block-lang&quot;&gt;Terminal&lt;/span&gt;&lt;/figcaption&gt;&lt;div class=&quot;code-block-tools&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;code-btn code-copy&quot; hidden&gt;Copy&lt;/button&gt;&lt;/div&gt;&lt;/div&gt;&lt;pre class=&quot;shiki&quot; tabindex=&quot;0&quot; role=&quot;region&quot; aria-label=&quot;Code sample: Terminal&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;$ yarn dlx @allons-y/envoy&lt;/span&gt;&lt;/span&gt;&lt;span
      class=&quot;line output&quot;&gt;&lt;span&gt;✨ Created /your/project/.env&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;p&gt;That&#39;s the whole thing. No config files, no network calls, no global state. Just a template and a source of truth, merged together.&lt;/p&gt; &lt;blockquote&gt; &lt;p&gt;&lt;strong&gt;Why &lt;code&gt;~/.env&lt;/code&gt; instead of a secrets manager?&lt;/strong&gt; The short answer: zero dependencies. envoy works offline, doesn&#39;t require an
      account, and integrates with whatever secrets workflow you already have. Pull values from 1Password or Vault into &lt;code&gt;~/.env&lt;/code&gt; as a sync step — that&#39;s a separate concern and envoy doesn&#39;t try to own it.&lt;/p&gt; &lt;/blockquote&gt; &lt;h2 id=&quot;the-details-that-matter&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;#the-details-that-matter&quot;&gt;#&lt;/a&gt; The details that matter&lt;/h2&gt; &lt;h3&gt;Safe by default&lt;/h3&gt;
      &lt;p&gt;envoy will never overwrite an existing &lt;code&gt;.env&lt;/code&gt; without your permission. If a &lt;code&gt;.env&lt;/code&gt; already exists, it skips that file and moves on. This is the behavior you want — running envoy repeatedly on an established project should be a no-op, not a destructive operation.&lt;/p&gt; &lt;p&gt;When you &lt;em&gt;do&lt;/em&gt; want to overwrite — say, you&#39;ve added keys and need to regenerate — pass &lt;code&gt;--force&lt;/code&gt;:&lt;/p&gt; &lt;figure
      class=&quot;code-block&quot; data-lang=&quot;bash&quot; data-edge=&quot;magenta&quot;&gt;&lt;div class=&quot;code-block-head&quot;&gt;&lt;figcaption&gt;&lt;span class=&quot;code-block-lang&quot;&gt;Shell&lt;/span&gt;&lt;/figcaption&gt;&lt;div class=&quot;code-block-tools&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;code-btn code-copy&quot; hidden&gt;Copy&lt;/button&gt;&lt;/div&gt;&lt;/div&gt;&lt;pre class=&quot;shiki&quot; tabindex=&quot;0&quot; role=&quot;region&quot; aria-label=&quot;Code
      sample: Shell&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-function&quot;&gt;yarn&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt; dlx&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt; @allons-y/envoy&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt; --force&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;h3&gt;Monorepo-aware&lt;/h3&gt; &lt;p&gt;envoy recursively walks your project directory, finds every
      &lt;code&gt;.env.example&lt;/code&gt; it can, and processes each one. It skips &lt;code&gt;node_modules&lt;/code&gt; automatically. You don&#39;t have to tell it where your packages live or configure a list of paths — it just finds them.&lt;/p&gt; &lt;figure class=&quot;code-block&quot; data-lang=&quot;bash&quot; data-edge=&quot;magenta&quot;&gt;&lt;div class=&quot;code-block-head&quot;&gt;&lt;figcaption&gt;&lt;span class=&quot;code-block-lang&quot;&gt;Shell&lt;/span&gt;&lt;/figcaption&gt;&lt;div
      class=&quot;code-block-tools&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;code-btn code-copy&quot; hidden&gt;Copy&lt;/button&gt;&lt;/div&gt;&lt;/div&gt;&lt;pre class=&quot;shiki&quot; tabindex=&quot;0&quot; role=&quot;region&quot; aria-label=&quot;Code sample: Shell&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-comment&quot;&gt;# Finds and processes all of these:&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span
      class=&quot;tk-comment&quot;&gt;# packages/api/.env.example&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-comment&quot;&gt;# packages/web/.env.example&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-comment&quot;&gt;# packages/workers/.env.example&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;h3&gt;Non-destructive key handling&lt;/h3&gt; &lt;p&gt;If a key exists in &lt;code&gt;.env.example&lt;/code&gt; but
      &lt;em&gt;not&lt;/em&gt; in &lt;code&gt;~/.env&lt;/code&gt;, envoy falls back to the example value instead of dropping the key entirely. This matters: you don&#39;t want a missing global key to silently produce a broken &lt;code&gt;.env&lt;/code&gt;. You want the key present with a placeholder, so the next person knows it needs a real value.&lt;/p&gt; &lt;h3&gt;Preview before writing&lt;/h3&gt; &lt;p&gt;Not sure what envoy is going to write? Run it with &lt;code&gt;--dry-run&lt;/code&gt;
      first:&lt;/p&gt; &lt;figure class=&quot;code-block&quot; data-lang=&quot;bash&quot; data-edge=&quot;magenta&quot;&gt;&lt;div class=&quot;code-block-head&quot;&gt;&lt;figcaption&gt;&lt;span class=&quot;code-block-lang&quot;&gt;Shell&lt;/span&gt;&lt;/figcaption&gt;&lt;div class=&quot;code-block-tools&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;code-btn code-copy&quot; hidden&gt;Copy&lt;/button&gt;&lt;/div&gt;&lt;/div&gt;&lt;pre class=&quot;shiki&quot; tabindex=&quot;0&quot;
      role=&quot;region&quot; aria-label=&quot;Code sample: Shell&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-function&quot;&gt;yarn&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt; dlx&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt; @allons-y/envoy&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt; --dry-run&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;p&gt;You&#39;ll see a full diff of what would be created or changed — nothing touches
      the filesystem. This is especially useful the first time you run it on an existing monorepo.&lt;/p&gt; &lt;hr /&gt; &lt;h2 id=&quot;the-postinstall-hook&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;#the-postinstall-hook&quot;&gt;#&lt;/a&gt; The postinstall hook&lt;/h2&gt; &lt;p&gt;This is the highest-value use case by a significant margin. Wire envoy into your project&#39;s &lt;code&gt;postinstall&lt;/code&gt; script:&lt;/p&gt; &lt;figure
      class=&quot;code-block&quot; data-lang=&quot;json&quot;&gt;&lt;div class=&quot;code-block-head&quot;&gt;&lt;figcaption&gt;&lt;span class=&quot;code-block-lang&quot;&gt;JSON&lt;/span&gt;&lt;/figcaption&gt;&lt;div class=&quot;code-block-tools&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;code-btn code-copy&quot; hidden&gt;Copy&lt;/button&gt;&lt;/div&gt;&lt;/div&gt;&lt;pre class=&quot;shiki&quot; tabindex=&quot;0&quot; role=&quot;region&quot; aria-label=&quot;Code sample:
      JSON&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-function&quot;&gt; &quot;scripts&quot;&lt;/span&gt;&lt;span&gt;: {&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-function&quot;&gt; &quot;postinstall&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;&quot;envoy&quot;&lt;/span&gt;&lt;/span&gt;&lt;span
      class=&quot;line&quot;&gt;&lt;span&gt; }&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;p&gt;Now every contributor who clones the repo and runs &lt;code&gt;npm install&lt;/code&gt; or &lt;code&gt;yarn&lt;/code&gt; gets a populated &lt;code&gt;.env&lt;/code&gt; automatically. No onboarding doc to write. No values to chase down in Slack. No &amp;quot;why doesn&#39;t this work&amp;quot; messages on day
      one.&lt;/p&gt; &lt;blockquote&gt; &lt;p&gt;&lt;strong&gt;If your package is published to npm&lt;/strong&gt;, use &lt;a href=&quot;https://github.com/typicode/pinst&quot;&gt;pinst&lt;/a&gt; to strip the postinstall hook from your tarball. You don&#39;t want consumers running envoy when they install your library — only developers working on the project itself should trigger it.&lt;/p&gt; &lt;figure class=&quot;code-block&quot; data-lang=&quot;json&quot;&gt;&lt;div
      class=&quot;code-block-head&quot;&gt;&lt;figcaption&gt;&lt;span class=&quot;code-block-lang&quot;&gt;JSON&lt;/span&gt;&lt;/figcaption&gt;&lt;div class=&quot;code-block-tools&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;code-btn code-copy&quot; hidden&gt;Copy&lt;/button&gt;&lt;/div&gt;&lt;/div&gt;&lt;pre class=&quot;shiki&quot; tabindex=&quot;0&quot; role=&quot;region&quot; aria-label=&quot;Code sample: JSON&quot;&gt;&lt;code&gt;&lt;span
      class=&quot;line&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-function&quot;&gt; &quot;scripts&quot;&lt;/span&gt;&lt;span&gt;: {&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-function&quot;&gt; &quot;postinstall&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;&quot;envoy&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;span
      class=&quot;line&quot;&gt;&lt;span class=&quot;tk-function&quot;&gt; &quot;prepublishOnly&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;&quot;pinst --disable&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-function&quot;&gt; &quot;postpublish&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;&quot;pinst --enable&quot;&lt;/span&gt;&lt;/span&gt;&lt;span
      class=&quot;line&quot;&gt;&lt;span&gt; }&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/blockquote&gt; &lt;p&gt;The practical effect: onboarding goes from &amp;quot;follow these twelve steps in the right order&amp;quot; to &amp;quot;clone the repo and run install.&amp;quot; That&#39;s not a small improvement in developer experience — that&#39;s the difference between a smooth first day and a frustrated
      one.&lt;/p&gt; &lt;h2 id=&quot;the-mcp-tool&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;#the-mcp-tool&quot;&gt;#&lt;/a&gt; The MCP tool&lt;/h2&gt; &lt;p&gt;If you&#39;re running Claude Desktop or Claude Code, envoy exposes a &lt;code&gt;copy_env&lt;/code&gt; tool through an MCP server. The same options available in the CLI — &lt;code&gt;--force&lt;/code&gt;, &lt;code&gt;--dry-run&lt;/code&gt;, and a target path — are available directly from your AI
      tools.&lt;/p&gt; &lt;figure class=&quot;code-block&quot; data-lang=&quot;bash&quot; data-edge=&quot;magenta&quot;&gt;&lt;div class=&quot;code-block-head&quot;&gt;&lt;figcaption&gt;&lt;span class=&quot;code-block-lang&quot;&gt;Shell&lt;/span&gt;&lt;/figcaption&gt;&lt;div class=&quot;code-block-tools&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;code-btn code-copy&quot; hidden&gt;Copy&lt;/button&gt;&lt;/div&gt;&lt;/div&gt;&lt;pre class=&quot;shiki&quot; tabindex=&quot;0&quot;
      role=&quot;region&quot; aria-label=&quot;Code sample: Shell&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-comment&quot;&gt;# Start the MCP server&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-function&quot;&gt;envoy&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt; --mcp&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;p&gt;This means you can ask Claude to set up your environment as part of a broader
      workflow — scaffolding a new package, bootstrapping a project, or running a setup checklist — without breaking out to the terminal. The tool validates paths, respects the &lt;code&gt;--force&lt;/code&gt; flag, and returns the same output you&#39;d see from the CLI.&lt;/p&gt; &lt;blockquote&gt; &lt;p&gt;&lt;strong&gt;Practical example:&lt;/strong&gt; You&#39;re scaffolding a new package in your monorepo. Claude creates the directory structure, writes the &lt;code&gt;package.json&lt;/code&gt; and
      &lt;code&gt;tsconfig.json&lt;/code&gt;, and calls &lt;code&gt;copy_env&lt;/code&gt; to generate the &lt;code&gt;.env&lt;/code&gt; — all in the same workflow, without manual steps between.&lt;/p&gt; &lt;/blockquote&gt; &lt;h2 id=&quot;configuring-your-~%2F.env&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;#configuring-your-~%2F.env&quot;&gt;#&lt;/a&gt; Configuring your &lt;code&gt;~/.env&lt;/code&gt;&lt;/h2&gt; &lt;p&gt;For envoy to do its job, you need a
      &lt;code&gt;~/.env&lt;/code&gt; with your real values. If you&#39;re starting from scratch, the pattern is simple: collect every key you use across all your projects and put the real values here.&lt;/p&gt; &lt;figure class=&quot;code-block&quot; data-lang=&quot;bash&quot; data-edge=&quot;magenta&quot; data-line-numbers&gt;&lt;div class=&quot;code-block-head&quot;&gt;&lt;figcaption&gt;&lt;span class=&quot;code-block-lang&quot;&gt;Shell&lt;/span&gt;&lt;/figcaption&gt;&lt;div
      class=&quot;code-block-tools&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;code-btn code-copy&quot; hidden&gt;Copy&lt;/button&gt;&lt;/div&gt;&lt;/div&gt;&lt;pre class=&quot;shiki&quot; tabindex=&quot;0&quot; role=&quot;region&quot; aria-label=&quot;Code sample: Shell&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-comment&quot;&gt;# ~/.env&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span
      class=&quot;tk-comment&quot;&gt;# Shared database&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;DATABASE_URL&lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;postgresql://localhost:5432/dev&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-comment&quot;&gt;# API keys&lt;/span&gt;&lt;/span&gt;&lt;span
      class=&quot;line&quot;&gt;&lt;span&gt;STRIPE_SECRET_KEY&lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;sk_test_...&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;OPENAI_API_KEY&lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;sk-...&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;GITHUB_TOKEN&lt;/span&gt;&lt;span
      class=&quot;tk-keyword&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;ghp_...&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-comment&quot;&gt;# Internal services&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;INTERNAL_API_BASE_URL&lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt;=&lt;/span&gt;&lt;span
      class=&quot;tk-string&quot;&gt;http://localhost:3001&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;p&gt;If you&#39;re already using a secrets manager, the recommended pattern is to sync into &lt;code&gt;~/.env&lt;/code&gt; as part of your shell startup or a manual pull command. envoy reads whatever is there — it doesn&#39;t care how it got there.&lt;/p&gt; &lt;h2 id=&quot;dos-and-don&#39;ts&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot;
      href=&quot;#dos-and-don&#39;ts&quot;&gt;#&lt;/a&gt; Dos and don&#39;ts&lt;/h2&gt; &lt;h3&gt;Do&lt;/h3&gt; &lt;ul&gt; &lt;li&gt;&lt;strong&gt;Add envoy to &lt;code&gt;postinstall&lt;/code&gt;&lt;/strong&gt; so environment setup happens automatically on &lt;code&gt;npm install&lt;/code&gt; or &lt;code&gt;yarn&lt;/code&gt;&lt;/li&gt; &lt;li&gt;&lt;strong&gt;Commit &lt;code&gt;.env.example&lt;/code&gt; with sensible placeholder values&lt;/strong&gt; — the fallback behavior depends on those placeholders
      being meaningful&lt;/li&gt; &lt;li&gt;&lt;strong&gt;Use &lt;code&gt;--dry-run&lt;/code&gt;&lt;/strong&gt; the first time you run envoy on an existing project&lt;/li&gt; &lt;li&gt;&lt;strong&gt;Keep &lt;code&gt;~/.env&lt;/code&gt; as your source of truth&lt;/strong&gt; for shared values across projects&lt;/li&gt; &lt;li&gt;&lt;strong&gt;Use &lt;code&gt;pinst&lt;/code&gt;&lt;/strong&gt; if your package is published to npm&lt;/li&gt; &lt;/ul&gt; &lt;h3&gt;Don&#39;t&lt;/h3&gt; &lt;ul&gt;
      &lt;li&gt;&lt;strong&gt;Don&#39;t commit real values to &lt;code&gt;.env.example&lt;/code&gt;&lt;/strong&gt; — that file belongs in source control; treat it like documentation, not secrets&lt;/li&gt; &lt;li&gt;&lt;strong&gt;Don&#39;t run envoy without &lt;code&gt;--force&lt;/code&gt; expecting it to update an existing &lt;code&gt;.env&lt;/code&gt;&lt;/strong&gt; — safe-by-default means it won&#39;t&lt;/li&gt; &lt;li&gt;&lt;strong&gt;Don&#39;t rely on envoy to sync secrets from a remote
      source&lt;/strong&gt; — that&#39;s a separate concern; handle it at the &lt;code&gt;~/.env&lt;/code&gt; layer&lt;/li&gt; &lt;li&gt;&lt;strong&gt;Don&#39;t use envoy as a replacement for proper secrets management in CI&lt;/strong&gt; — it&#39;s a local developer tool, not a deployment pipeline&lt;/li&gt; &lt;/ul&gt; &lt;h2 id=&quot;try-it&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;#try-it&quot;&gt;#&lt;/a&gt; Try it&lt;/h2&gt; &lt;p&gt;No installation
      required:&lt;/p&gt; &lt;figure class=&quot;code-block&quot; data-lang=&quot;bash&quot; data-edge=&quot;magenta&quot;&gt;&lt;div class=&quot;code-block-head&quot;&gt;&lt;figcaption&gt;&lt;span class=&quot;code-block-lang&quot;&gt;Shell&lt;/span&gt;&lt;/figcaption&gt;&lt;div class=&quot;code-block-tools&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;code-btn code-copy&quot; hidden&gt;Copy&lt;/button&gt;&lt;/div&gt;&lt;/div&gt;&lt;pre class=&quot;shiki&quot; tabindex=&quot;0&quot;
      role=&quot;region&quot; aria-label=&quot;Code sample: Shell&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-function&quot;&gt;npx&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt; @allons-y/envoy&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt; --dry-run&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;p&gt;The source is small, readable, and fully tested. Apache 2.0.&lt;/p&gt; &lt;p&gt;→ GitHub: &lt;a
      href=&quot;https://github.com/allonsy-studio/envoy&quot;&gt;github.com/allonsy-studio/envoy&lt;/a&gt;&lt;br/&gt; → npm: &lt;a href=&quot;https://www.npmjs.com/package/@allons-y/envoy&quot;&gt;&lt;code&gt;@allons-y/envoy&lt;/code&gt;&lt;/a&gt;&lt;/p&gt; &lt;h2 id=&quot;wrapping-up&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;#wrapping-up&quot;&gt;#&lt;/a&gt; Wrapping up&lt;/h2&gt; &lt;p&gt;The best developer tooling solves problems so completely that you stop
      thinking about them. Environment setup is one of those problems — it&#39;s not interesting, it doesn&#39;t require expertise, and the only thing it produces (when done manually) is friction. envoy makes it automatic, repeatable, and boring in the best way possible.&lt;/p&gt; &lt;p&gt;Set up &lt;code&gt;~/.env&lt;/code&gt; once. Add envoy to &lt;code&gt;postinstall&lt;/code&gt;. Clone any project. Run install. Done.&lt;/p&gt; &lt;p&gt;That&#39;s the goal: the setup ritual disappears, and you get
      straight to the work that matters.&lt;/p&gt;
    </content>
    <summary>Every project starts with the same tedious ritual — copy .env.example, track down the values, paste them in. In a monorepo, that ritual scales badly. Here&#39;s a tool I built to eliminate it entirely.</summary>
    <category term="blog" />
    <category term="tooling" />
    <category term="dx" />
    <category term="monorepo" />
  </entry>
  <entry>
    <title>Performance monitoring for web components</title>
    <link href="https://castastrophe.blog/blog/performance-monitoring-for-web-components/" />
    <updated>2025-11-23T00:00:00.000Z</updated>
    <id>https://castastrophe.blog/blog/performance-monitoring-for-web-components/</id>
    <content type="html"
      >&lt;p&gt;Here&#39;s what nobody tells you about building web components: you can follow all the best practices, optimize your Shadow DOM, and keep your bundle sizes tiny, but if you&#39;re not measuring performance, you&#39;re flying blind. Sure, components perform well on your beefy MacBook Pro, but what about on a mid-range Android device over spotty 3G connection?&lt;/p&gt; &lt;p&gt;The good news? There&#39;s a browser API specifically designed for this. The &lt;a
      href=&quot;https://www.w3.org/TR/user-timing/&quot;&gt;User Timing API&lt;/a&gt; lets you drop performance markers directly into your component lifecycle and see exactly where time is being spent. No third-party libraries, no complex setup, just &lt;a href=&quot;https://caniuse.com/user-timing&quot;&gt;widely-available&lt;/a&gt; native browser capabilities that integrate beautifully with dev tools and &lt;a
      href=&quot;https://developer.chrome.com/docs/lighthouse/performance/user-timings&quot;&gt;Lighthouse&lt;/a&gt;.&lt;/p&gt; &lt;p&gt;This guide will walk you through instrumenting your web components with performance tracking that actually matters; measurements you can act on with insights into the user experience.&lt;/p&gt; &lt;hr /&gt; &lt;h2 id=&quot;why-performance-monitoring-matters&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot;
      href=&quot;#why-performance-monitoring-matters&quot;&gt;#&lt;/a&gt; Why performance monitoring matters&lt;/h2&gt; &lt;p&gt;Performance isn&#39;t just about making things fast, it&#39;s about understanding where your components spend their time. These insights help you make informed decisions about where to spend your user&#39;s attention. Did that fancy animation you added slow down initial render? By how much? Is your component&#39;s first paint happening fast enough that users don&#39;t notice a
      delay? You won&#39;t know unless you track the data.&lt;/p&gt; &lt;p&gt;The web can be unforgiving. Users expect instant responses with limited attention to spare; every millisecond counts. Performance marks show up in your dev tools, giving you visibility into what&#39;s actually happening when your components hit the DOM.&lt;/p&gt; &lt;hr /&gt; &lt;h2 id=&quot;understanding-the-user-timing-api&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot;
      href=&quot;#understanding-the-user-timing-api&quot;&gt;#&lt;/a&gt; Understanding the User Timing API&lt;/h2&gt; &lt;p&gt;Before we dive into implementation, let&#39;s talk about what the User Timing API actually does. It provides high-precision timestamps that are part of the browser&#39;s performance timeline, and it does this through two simple concepts: marks and measures.&lt;/p&gt; &lt;p&gt;&lt;strong&gt;Marks&lt;/strong&gt; are timestamps you place at specific points in your code. Think of them
      as performance breadcrumbs—they tell you when something happened.&lt;/p&gt; &lt;p&gt;&lt;strong&gt;Measures&lt;/strong&gt; calculate the elapsed time between two marks. They&#39;re the answer to &amp;quot;how long did that take?&amp;quot;&lt;/p&gt; &lt;p&gt;The beauty of this API is simplicity. No complex configuration or heavyweight monitoring solutions. You&#39;re just saying &amp;quot;mark this moment&amp;quot; and &amp;quot;measure from here to there.&amp;quot;&lt;/p&gt; &lt;hr /&gt; &lt;h2
      id=&quot;core-implementation-strategy&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;#core-implementation-strategy&quot;&gt;#&lt;/a&gt; Core implementation strategy&lt;/h2&gt; &lt;p&gt;When instrumenting web components, you want to capture the moments that matter most to users. Here&#39;s a few things you could be tracking:&lt;/p&gt; &lt;p&gt;&lt;strong&gt;Component registration&lt;/strong&gt;: When the component is defined and added to the custom elements
      registry&lt;/p&gt; &lt;p&gt;&lt;strong&gt;DOM connection&lt;/strong&gt;: When the component is first inserted into the document&lt;/p&gt; &lt;p&gt;&lt;strong&gt;First render&lt;/strong&gt;: When the component completes its initial render and becomes visible&lt;/p&gt; &lt;p&gt;&lt;strong&gt;Upgrade time&lt;/strong&gt;: The total time from registration to first render&lt;/p&gt; &lt;p&gt;These measurements tell you the complete story of your component&#39;s lifecycle performance. Let&#39;s look at how to
      implement this.&lt;/p&gt; &lt;hr /&gt; &lt;h2 id=&quot;implementation-patterns&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;#implementation-patterns&quot;&gt;#&lt;/a&gt; Implementation patterns&lt;/h2&gt; &lt;h3&gt;Setting up performance marks&lt;/h3&gt; &lt;p&gt;The first step is identifying where to place your marks. For web components, there are natural lifecycle hooks that map perfectly to performance milestones.&lt;/p&gt; &lt;p&gt;When your component connects
      to the DOM, drop a mark:&lt;/p&gt; &lt;figure class=&quot;code-block&quot; data-lang=&quot;js&quot; data-edge=&quot;yellow&quot;&gt;&lt;div class=&quot;code-block-head&quot;&gt;&lt;figcaption&gt;&lt;span class=&quot;code-block-lang&quot;&gt;JavaScript&lt;/span&gt;&lt;/figcaption&gt;&lt;div class=&quot;code-block-tools&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;code-btn code-copy&quot; hidden&gt;Copy&lt;/button&gt;&lt;/div&gt;&lt;/div&gt;&lt;pre class=&quot;shiki&quot;
      tabindex=&quot;0&quot; role=&quot;region&quot; aria-label=&quot;Code sample: JavaScript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-function&quot;&gt;connectedCallback&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; performance.&lt;/span&gt;&lt;span class=&quot;tk-function&quot;&gt;mark&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;`${&lt;/span&gt;&lt;span
      class=&quot;tk-keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;.localName&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;}:connected`&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-comment&quot;&gt; // Trigger initial render&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-keyword&quot;&gt;
      this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span class=&quot;tk-function&quot;&gt;render&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;blockquote&gt; &lt;p&gt;In this and following examples, &lt;code&gt;this.localName&lt;/code&gt; represents a property on the web component that stores it&#39;s tag name, i.e., &lt;code&gt;custom-button&lt;/code&gt;.&lt;/p&gt;
      &lt;/blockquote&gt; &lt;p&gt;After your first render completes, drop another. You&#39;ll need to track whether this is the first render:&lt;/p&gt; &lt;figure class=&quot;code-block&quot; data-lang=&quot;js&quot; data-edge=&quot;yellow&quot; data-line-numbers&gt;&lt;div class=&quot;code-block-head&quot;&gt;&lt;figcaption&gt;&lt;span class=&quot;code-block-lang&quot;&gt;JavaScript&lt;/span&gt;&lt;/figcaption&gt;&lt;div class=&quot;code-block-tools&quot;&gt;&lt;button type=&quot;button&quot;
      class=&quot;code-btn code-copy&quot; hidden&gt;Copy&lt;/button&gt;&lt;/div&gt;&lt;/div&gt;&lt;pre class=&quot;shiki&quot; tabindex=&quot;0&quot; role=&quot;region&quot; aria-label=&quot;Code sample: JavaScript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-function&quot;&gt;render&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-comment&quot;&gt; // Your rendering logic
      here&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-keyword&quot;&gt; this&lt;/span&gt;&lt;span&gt;.shadowRoot.innerHTML &lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt; `&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-string&quot;&gt; &amp;#x3C;button&gt;Click me&amp;#x3C;/button&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span
      class=&quot;tk-string&quot;&gt; `&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-comment&quot;&gt; // Mark first render complete&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-keyword&quot;&gt; if&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt;!this&lt;/span&gt;&lt;span&gt;._hasRendered) {&lt;/span&gt;&lt;/span&gt;&lt;span
      class=&quot;line&quot;&gt;&lt;span class=&quot;tk-keyword&quot;&gt; this&lt;/span&gt;&lt;span&gt;._hasRendered &lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;tk-number&quot;&gt; true&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; performance.&lt;/span&gt;&lt;span class=&quot;tk-function&quot;&gt;mark&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;`${&lt;/span&gt;&lt;span
      class=&quot;tk-keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;.localName&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;}:first-render`&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; }&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;p&gt;For component registration, you&#39;ll want to add a mark when you define the custom
      element:&lt;/p&gt; &lt;figure class=&quot;code-block&quot; data-lang=&quot;js&quot; data-edge=&quot;yellow&quot;&gt;&lt;div class=&quot;code-block-head&quot;&gt;&lt;figcaption&gt;&lt;span class=&quot;code-block-lang&quot;&gt;JavaScript&lt;/span&gt;&lt;/figcaption&gt;&lt;div class=&quot;code-block-tools&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;code-btn code-copy&quot; hidden&gt;Copy&lt;/button&gt;&lt;/div&gt;&lt;/div&gt;&lt;pre class=&quot;shiki&quot; tabindex=&quot;0&quot;
      role=&quot;region&quot; aria-label=&quot;Code sample: JavaScript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;customElements.&lt;/span&gt;&lt;span class=&quot;tk-function&quot;&gt;define&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;&#39;my-component&#39;&lt;/span&gt;&lt;span&gt;, MyComponent);&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;performance.&lt;/span&gt;&lt;span
      class=&quot;tk-function&quot;&gt;mark&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;&#39;my-component:defined&#39;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;h3&gt;Creating meaningful measures&lt;/h3&gt; &lt;p&gt;Now that you have marks in place, you can create measures that tell you how long critical paths are taking. The measure between connection and first render is especially valuable because it represents the
      user-facing initialization time:&lt;/p&gt; &lt;figure class=&quot;code-block&quot; data-lang=&quot;js&quot; data-edge=&quot;yellow&quot; data-line-numbers&gt;&lt;div class=&quot;code-block-head&quot;&gt;&lt;figcaption&gt;&lt;span class=&quot;code-block-lang&quot;&gt;JavaScript&lt;/span&gt;&lt;/figcaption&gt;&lt;div class=&quot;code-block-tools&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;code-btn code-copy&quot; hidden&gt;Copy&lt;/button&gt;&lt;/div&gt;&lt;/div&gt;&lt;pre
      class=&quot;shiki&quot; tabindex=&quot;0&quot; role=&quot;region&quot; aria-label=&quot;Code sample: JavaScript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-function&quot;&gt;render&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-comment&quot;&gt; // Your rendering logic here&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-keyword&quot;&gt;
      this&lt;/span&gt;&lt;span&gt;.shadowRoot.innerHTML &lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt; `&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-string&quot;&gt; &amp;#x3C;button&gt;Click me&amp;#x3C;/button&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-string&quot;&gt; `&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;span
      class=&quot;line&quot;&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-comment&quot;&gt; // Mark and measure first render&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-keyword&quot;&gt; if&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt;!this&lt;/span&gt;&lt;span&gt;._hasRendered) {&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-keyword&quot;&gt;
      this&lt;/span&gt;&lt;span&gt;._hasRendered &lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;tk-number&quot;&gt; true&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; performance.&lt;/span&gt;&lt;span class=&quot;tk-function&quot;&gt;mark&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;`${&lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt;this&lt;/span&gt;&lt;span
      class=&quot;tk-string&quot;&gt;.localName&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;}:first-render`&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; performance.&lt;/span&gt;&lt;span class=&quot;tk-function&quot;&gt;measure&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-string&quot;&gt; `${&lt;/span&gt;&lt;span
      class=&quot;tk-keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;.localName&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;}:upgrade`&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-string&quot;&gt; `${&lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;.localName&lt;/span&gt;&lt;span
      class=&quot;tk-string&quot;&gt;}:connected`&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-string&quot;&gt; `${&lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;.localName&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;}:first-render`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; );&lt;/span&gt;&lt;/span&gt;&lt;span
      class=&quot;line&quot;&gt;&lt;span&gt; }&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;h3&gt;Using consistent naming conventions&lt;/h3&gt; &lt;p&gt;This is crucial: establish a naming convention and stick with it. I recommend using the component&#39;s tag name followed by a colon and the event name:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;&lt;code&gt;component-name:defined&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;&lt;code&gt;component-name:connected&lt;/code&gt;&lt;/li&gt; &lt;li&gt;&lt;code&gt;component-name:first-render&lt;/code&gt;&lt;/li&gt; &lt;li&gt;&lt;code&gt;component-name:upgrade&lt;/code&gt;&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;This pattern makes it easy to filter and analyze your metrics later. When you&#39;re looking at DevTools with dozens of components on the page, clear naming saves you from the cognitive overhead of figuring out which measurement belongs to what.&lt;/p&gt; &lt;hr /&gt;
      &lt;h2 id=&quot;building-reusable-patterns&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;#building-reusable-patterns&quot;&gt;#&lt;/a&gt; Building reusable patterns&lt;/h2&gt; &lt;p&gt;Rather than duplicating this code across every component, there are a few optimization options you can use: create or add it to a base class or use a mixin. Both approaches give you performance tracking for free, once set up, but they serve different architectural needs.&lt;/p&gt;
      &lt;h3&gt;Option 1: Base class&lt;/h3&gt; &lt;p&gt;A base class works well when you want a standardized component foundation. You set it up once, and every component that extends it gets performance tracking ✨automagically✨.&lt;/p&gt; &lt;figure class=&quot;code-block&quot; data-lang=&quot;js&quot; data-edge=&quot;yellow&quot; data-line-numbers&gt;&lt;div class=&quot;code-block-head&quot;&gt;&lt;figcaption&gt;&lt;span
      class=&quot;code-block-lang&quot;&gt;JavaScript&lt;/span&gt;&lt;/figcaption&gt;&lt;div class=&quot;code-block-tools&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;code-btn code-copy&quot; hidden&gt;Copy&lt;/button&gt;&lt;/div&gt;&lt;/div&gt;&lt;pre class=&quot;shiki&quot; tabindex=&quot;0&quot; role=&quot;region&quot; aria-label=&quot;Code sample: JavaScript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-keyword&quot;&gt;export&lt;/span&gt;&lt;span
      class=&quot;tk-keyword&quot;&gt; class&lt;/span&gt;&lt;span class=&quot;tk-function&quot;&gt; PerformanceTrackedElement&lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt; extends&lt;/span&gt;&lt;span&gt; HTMLElement {&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-keyword&quot;&gt; constructor&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-keyword&quot;&gt;
      super&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-keyword&quot;&gt; this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span class=&quot;tk-function&quot;&gt;attachShadow&lt;/span&gt;&lt;span&gt;({ mode: &lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;&#39;open&#39;&lt;/span&gt;&lt;span&gt; });&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-keyword&quot;&gt;
      this&lt;/span&gt;&lt;span&gt;._hasRendered &lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;tk-number&quot;&gt; false&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; }&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-function&quot;&gt; connectedCallback&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/span&gt;&lt;span
      class=&quot;line&quot;&gt;&lt;span class=&quot;tk-comment&quot;&gt; // Only mark the first connection&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-keyword&quot;&gt; if&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt;!this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span class=&quot;tk-function&quot;&gt;hasAttribute&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span
      class=&quot;tk-string&quot;&gt;&#39;data-perf-marked&#39;&lt;/span&gt;&lt;span&gt;)) {&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; performance.&lt;/span&gt;&lt;span class=&quot;tk-function&quot;&gt;mark&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;`${&lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;.localName&lt;/span&gt;&lt;span
      class=&quot;tk-string&quot;&gt;}:connected`&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-keyword&quot;&gt; this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span class=&quot;tk-function&quot;&gt;setAttribute&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;&#39;data-perf-marked&#39;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span
      class=&quot;tk-string&quot;&gt;&#39;&#39;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; }&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-comment&quot;&gt; // Trigger initial render&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-keyword&quot;&gt; this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span
      class=&quot;tk-function&quot;&gt;render&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; }&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-function&quot;&gt; _markFirstRender&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-keyword&quot;&gt; if&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span
      class=&quot;tk-keyword&quot;&gt;!this&lt;/span&gt;&lt;span&gt;._hasRendered) {&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-keyword&quot;&gt; this&lt;/span&gt;&lt;span&gt;._hasRendered &lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;tk-number&quot;&gt; true&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; performance.&lt;/span&gt;&lt;span
      class=&quot;tk-function&quot;&gt;mark&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;`${&lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;.localName&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;}:first-render`&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-comment&quot;&gt; // Measure the
      upgrade time&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; performance.&lt;/span&gt;&lt;span class=&quot;tk-function&quot;&gt;measure&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-string&quot;&gt; `${&lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;.localName&lt;/span&gt;&lt;span
      class=&quot;tk-string&quot;&gt;}:upgrade`&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-string&quot;&gt; `${&lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;.localName&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;}:connected`&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-string&quot;&gt;
      `${&lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;.localName&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;}:first-render`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; );&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; }&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; }&lt;/span&gt;&lt;/span&gt;&lt;span
      class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;button type=&quot;button&quot; class=&quot;code-expand&quot; aria-expanded=&quot;false&quot; hidden&gt;Show all 32 lines&lt;/button&gt;&lt;/figure&gt;&lt;p&gt;Now any component can extend this base class:&lt;/p&gt; &lt;figure class=&quot;code-block&quot; data-lang=&quot;js&quot; data-edge=&quot;yellow&quot; data-line-numbers&gt;&lt;div class=&quot;code-block-head&quot;&gt;&lt;figcaption&gt;&lt;span
      class=&quot;code-block-lang&quot;&gt;JavaScript&lt;/span&gt;&lt;/figcaption&gt;&lt;div class=&quot;code-block-tools&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;code-btn code-copy&quot; hidden&gt;Copy&lt;/button&gt;&lt;/div&gt;&lt;/div&gt;&lt;pre class=&quot;shiki&quot; tabindex=&quot;0&quot; role=&quot;region&quot; aria-label=&quot;Code sample: JavaScript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-keyword&quot;&gt;export&lt;/span&gt;&lt;span
      class=&quot;tk-keyword&quot;&gt; class&lt;/span&gt;&lt;span class=&quot;tk-function&quot;&gt; MyButton&lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt; extends&lt;/span&gt;&lt;span&gt; PerformanceTrackedElement {&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-function&quot;&gt; render&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-keyword&quot;&gt;
      this&lt;/span&gt;&lt;span&gt;.shadowRoot.innerHTML &lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt; `&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-string&quot;&gt; &amp;#x3C;style&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-string&quot;&gt; button {&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-string&quot;&gt; padding:
      0.5rem 1rem;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-string&quot;&gt; border: none;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-string&quot;&gt; border-radius: 4px;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-string&quot;&gt; background: #007bff;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-string&quot;&gt; color:
      white;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-string&quot;&gt; cursor: pointer;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-string&quot;&gt; }&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-string&quot;&gt; &amp;#x3C;/style&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-string&quot;&gt; &amp;#x3C;button&gt;Click
      me&amp;#x3C;/button&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-string&quot;&gt; `&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-comment&quot;&gt; // Mark that first render is complete&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-keyword&quot;&gt; this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span
      class=&quot;tk-function&quot;&gt;_markFirstRender&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; }&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;h3&gt;Option 2: Mixin&lt;/h3&gt; &lt;p&gt;If you already have a base class hierarchy or want more flexibility, a mixin lets you add performance tracking to any class without changing your inheritance chain.
      This is particularly useful when working with existing component libraries or when you need to compose multiple behaviors.&lt;/p&gt; &lt;figure class=&quot;code-block&quot; data-lang=&quot;js&quot; data-edge=&quot;yellow&quot; data-line-numbers&gt;&lt;div class=&quot;code-block-head&quot;&gt;&lt;figcaption&gt;&lt;span class=&quot;code-block-lang&quot;&gt;JavaScript&lt;/span&gt;&lt;/figcaption&gt;&lt;div class=&quot;code-block-tools&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;code-btn
      code-copy&quot; hidden&gt;Copy&lt;/button&gt;&lt;/div&gt;&lt;/div&gt;&lt;pre class=&quot;shiki&quot; tabindex=&quot;0&quot; role=&quot;region&quot; aria-label=&quot;Code sample: JavaScript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-keyword&quot;&gt;export&lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt; const&lt;/span&gt;&lt;span class=&quot;tk-function&quot;&gt; PerformanceTrackingMixin&lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt;
      =&lt;/span&gt;&lt;span&gt; (SuperClass) &lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt;=&gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-keyword&quot;&gt; return&lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt; class&lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt; extends&lt;/span&gt;&lt;span&gt; SuperClass {&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-keyword&quot;&gt;
      constructor&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-keyword&quot;&gt; super&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-keyword&quot;&gt; this&lt;/span&gt;&lt;span&gt;._hasRendered &lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;tk-number&quot;&gt; false&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;span
      class=&quot;line&quot;&gt;&lt;span&gt; }&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-function&quot;&gt; connectedCallback&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-comment&quot;&gt; // Only mark the first connection&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-keyword&quot;&gt; if&lt;/span&gt;&lt;span&gt;
      (&lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt;!this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span class=&quot;tk-function&quot;&gt;hasAttribute&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;&#39;data-perf-marked&#39;&lt;/span&gt;&lt;span&gt;)) {&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; performance.&lt;/span&gt;&lt;span class=&quot;tk-function&quot;&gt;mark&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span
      class=&quot;tk-string&quot;&gt;`${&lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;.localName&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;}:connected`&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-keyword&quot;&gt; this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span class=&quot;tk-function&quot;&gt;setAttribute&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span
      class=&quot;tk-string&quot;&gt;&#39;data-perf-marked&#39;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;&#39;&#39;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; }&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-comment&quot;&gt; // Call parent connectedCallback if it exists&lt;/span&gt;&lt;/span&gt;&lt;span
      class=&quot;line&quot;&gt;&lt;span class=&quot;tk-keyword&quot;&gt; if&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt;super&lt;/span&gt;&lt;span&gt;.connectedCallback) {&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-keyword&quot;&gt; super&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span class=&quot;tk-function&quot;&gt;connectedCallback&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;&lt;span
      class=&quot;line&quot;&gt;&lt;span&gt; }&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; }&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-function&quot;&gt; _markFirstRender&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-keyword&quot;&gt; if&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span
      class=&quot;tk-keyword&quot;&gt;!this&lt;/span&gt;&lt;span&gt;._hasRendered) {&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-keyword&quot;&gt; this&lt;/span&gt;&lt;span&gt;._hasRendered &lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;tk-number&quot;&gt; true&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; performance.&lt;/span&gt;&lt;span
      class=&quot;tk-function&quot;&gt;mark&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;`${&lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;.localName&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;}:first-render`&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-comment&quot;&gt; // Measure the
      upgrade time&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; performance.&lt;/span&gt;&lt;span class=&quot;tk-function&quot;&gt;measure&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-string&quot;&gt; `${&lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;.localName&lt;/span&gt;&lt;span
      class=&quot;tk-string&quot;&gt;}:upgrade`&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-string&quot;&gt; `${&lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;.localName&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;}:connected`&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-string&quot;&gt;
      `${&lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;.localName&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;}:first-render`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; );&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; }&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; }&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;span
      class=&quot;line&quot;&gt;&lt;span class=&quot;tk-function&quot;&gt; disconnectedCallback&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-comment&quot;&gt; // Clean up performance marks&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; performance.&lt;/span&gt;&lt;span class=&quot;tk-function&quot;&gt;clearMarks&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;`${&lt;/span&gt;&lt;span
      class=&quot;tk-keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;.localName&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;}:connected`&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; performance.&lt;/span&gt;&lt;span class=&quot;tk-function&quot;&gt;clearMarks&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;`${&lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt;this&lt;/span&gt;&lt;span
      class=&quot;tk-string&quot;&gt;.localName&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;}:first-render`&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; performance.&lt;/span&gt;&lt;span class=&quot;tk-function&quot;&gt;clearMeasures&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;`${&lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt;this&lt;/span&gt;&lt;span
      class=&quot;tk-string&quot;&gt;.localName&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;}:upgrade`&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-comment&quot;&gt; // Call parent disconnectedCallback if it exists&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-keyword&quot;&gt; if&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span
      class=&quot;tk-keyword&quot;&gt;super&lt;/span&gt;&lt;span&gt;.disconnectedCallback) {&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-keyword&quot;&gt; super&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span class=&quot;tk-function&quot;&gt;disconnectedCallback&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; }&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;
      }&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; };&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;};&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;button type=&quot;button&quot; class=&quot;code-expand&quot; aria-expanded=&quot;false&quot; hidden&gt;Show all 47 lines&lt;/button&gt;&lt;/figure&gt;&lt;p&gt;Apply the mixin to any component class:&lt;/p&gt; &lt;figure class=&quot;code-block&quot; data-lang=&quot;js&quot;
      data-edge=&quot;yellow&quot; data-line-numbers&gt;&lt;div class=&quot;code-block-head&quot;&gt;&lt;figcaption&gt;&lt;span class=&quot;code-block-lang&quot;&gt;JavaScript&lt;/span&gt;&lt;/figcaption&gt;&lt;div class=&quot;code-block-tools&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;code-btn code-copy&quot; hidden&gt;Copy&lt;/button&gt;&lt;/div&gt;&lt;/div&gt;&lt;pre class=&quot;shiki&quot; tabindex=&quot;0&quot; role=&quot;region&quot; aria-label=&quot;Code sample:
      JavaScript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-comment&quot;&gt;// Apply to HTMLElement&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-keyword&quot;&gt;export&lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt; class&lt;/span&gt;&lt;span class=&quot;tk-function&quot;&gt; MyButton&lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt; extends&lt;/span&gt;&lt;span class=&quot;tk-function&quot;&gt;
      PerformanceTrackingMixin&lt;/span&gt;&lt;span&gt;(HTMLElement) {&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-keyword&quot;&gt; constructor&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-keyword&quot;&gt; super&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-keyword&quot;&gt; this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span
      class=&quot;tk-function&quot;&gt;attachShadow&lt;/span&gt;&lt;span&gt;({ mode: &lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;&#39;open&#39;&lt;/span&gt;&lt;span&gt; });&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; }&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-function&quot;&gt; connectedCallback&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/span&gt;&lt;span
      class=&quot;line&quot;&gt;&lt;span class=&quot;tk-keyword&quot;&gt; super&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span class=&quot;tk-function&quot;&gt;connectedCallback&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-keyword&quot;&gt; this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span class=&quot;tk-function&quot;&gt;render&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;
      }&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-function&quot;&gt; render&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-keyword&quot;&gt; this&lt;/span&gt;&lt;span&gt;.shadowRoot.innerHTML &lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt; `&lt;/span&gt;&lt;/span&gt;&lt;span
      class=&quot;line&quot;&gt;&lt;span class=&quot;tk-string&quot;&gt; &amp;#x3C;style&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-string&quot;&gt; button {&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-string&quot;&gt; padding: 0.5rem 1rem;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-string&quot;&gt; border: none;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span
      class=&quot;tk-string&quot;&gt; border-radius: 4px;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-string&quot;&gt; background: #007bff;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-string&quot;&gt; color: white;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-string&quot;&gt; cursor: pointer;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span
      class=&quot;tk-string&quot;&gt; }&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-string&quot;&gt; &amp;#x3C;/style&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-string&quot;&gt; &amp;#x3C;button&gt;Click me&amp;#x3C;/button&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-string&quot;&gt; `&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;span
      class=&quot;line&quot;&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-keyword&quot;&gt; this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span class=&quot;tk-function&quot;&gt;_markFirstRender&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; }&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;button type=&quot;button&quot;
      class=&quot;code-expand&quot; aria-expanded=&quot;false&quot; hidden&gt;Show all 30 lines&lt;/button&gt;&lt;/figure&gt;&lt;p&gt;Or apply the mixin to your existing base class:&lt;/p&gt; &lt;figure class=&quot;code-block&quot; data-lang=&quot;js&quot; data-edge=&quot;yellow&quot;&gt;&lt;div class=&quot;code-block-head&quot;&gt;&lt;figcaption&gt;&lt;span class=&quot;code-block-lang&quot;&gt;JavaScript&lt;/span&gt;&lt;/figcaption&gt;&lt;div class=&quot;code-block-tools&quot;&gt;&lt;button
      type=&quot;button&quot; class=&quot;code-btn code-copy&quot; hidden&gt;Copy&lt;/button&gt;&lt;/div&gt;&lt;/div&gt;&lt;pre class=&quot;shiki&quot; tabindex=&quot;0&quot; role=&quot;region&quot; aria-label=&quot;Code sample: JavaScript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-comment&quot;&gt;// Or apply to your existing base class&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-keyword&quot;&gt;export&lt;/span&gt;&lt;span
      class=&quot;tk-keyword&quot;&gt; class&lt;/span&gt;&lt;span class=&quot;tk-function&quot;&gt; MyCard&lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt; extends&lt;/span&gt;&lt;span class=&quot;tk-function&quot;&gt; PerformanceTrackingMixin&lt;/span&gt;&lt;span&gt;(MyBaseElement) {&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-comment&quot;&gt; // Your implementation&lt;/span&gt;&lt;/span&gt;&lt;span
      class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;h3&gt;Composing multiple mixins&lt;/h3&gt; &lt;p&gt;The beauty of mixins is that you can compose multiple behaviors. If you have other mixins for logging, analytics, or error handling, you can stack them:&lt;/p&gt; &lt;figure class=&quot;code-block&quot; data-lang=&quot;js&quot; data-edge=&quot;yellow&quot;&gt;&lt;div class=&quot;code-block-head&quot;&gt;&lt;figcaption&gt;&lt;span
      class=&quot;code-block-lang&quot;&gt;JavaScript&lt;/span&gt;&lt;/figcaption&gt;&lt;div class=&quot;code-block-tools&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;code-btn code-copy&quot; hidden&gt;Copy&lt;/button&gt;&lt;/div&gt;&lt;/div&gt;&lt;pre class=&quot;shiki&quot; tabindex=&quot;0&quot; role=&quot;region&quot; aria-label=&quot;Code sample: JavaScript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-keyword&quot;&gt;export&lt;/span&gt;&lt;span
      class=&quot;tk-keyword&quot;&gt; class&lt;/span&gt;&lt;span class=&quot;tk-function&quot;&gt; MyComponent&lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt; extends&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-function&quot;&gt; PerformanceTrackingMixin&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-function&quot;&gt; LoggingMixin&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span
      class=&quot;line&quot;&gt;&lt;span class=&quot;tk-function&quot;&gt; AnalyticsMixin&lt;/span&gt;&lt;span&gt;(HTMLElement)&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; )&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; ) {&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-comment&quot;&gt; // Your component with all three behaviors&lt;/span&gt;&lt;/span&gt;&lt;span
      class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;h3&gt;Choosing between base class and mixin&lt;/h3&gt; &lt;p&gt;&lt;strong&gt;Use a base class when:&lt;/strong&gt;&lt;/p&gt; &lt;ul&gt; &lt;li&gt;You&#39;re starting fresh with a new component library&lt;/li&gt; &lt;li&gt;You want a single, consistent foundation for all components&lt;/li&gt; &lt;li&gt;You control the entire inheritance chain&lt;/li&gt; &lt;li&gt;You prefer a simpler mental
      model&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;&lt;strong&gt;Use a mixin when:&lt;/strong&gt;&lt;/p&gt; &lt;ul&gt; &lt;li&gt;You need to add performance tracking to existing components&lt;/li&gt; &lt;li&gt;You&#39;re working with multiple base classes&lt;/li&gt; &lt;li&gt;You want to compose multiple behaviors&lt;/li&gt; &lt;li&gt;You need flexibility to opt-in per component&lt;/li&gt; &lt;/ul&gt; &lt;hr /&gt; &lt;h2 id=&quot;accessing-your-performance-data&quot; tabindex=&quot;-1&quot;&gt;&lt;a
      class=&quot;header-anchor&quot; href=&quot;#accessing-your-performance-data&quot;&gt;#&lt;/a&gt; Accessing your performance data&lt;/h2&gt; &lt;p&gt;Once you&#39;ve instrumented your components, you need to actually look at the data. There are several ways to do this, each useful for different purposes.&lt;/p&gt; &lt;h3&gt;Using the Performance Timeline&lt;/h3&gt; &lt;p&gt;You can retrieve all your marks and measures programmatically:&lt;/p&gt; &lt;figure class=&quot;code-block&quot;
      data-lang=&quot;js&quot; data-edge=&quot;yellow&quot;&gt;&lt;div class=&quot;code-block-head&quot;&gt;&lt;figcaption&gt;&lt;span class=&quot;code-block-lang&quot;&gt;JavaScript&lt;/span&gt;&lt;/figcaption&gt;&lt;div class=&quot;code-block-tools&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;code-btn code-copy&quot; hidden&gt;Copy&lt;/button&gt;&lt;/div&gt;&lt;/div&gt;&lt;pre class=&quot;shiki&quot; tabindex=&quot;0&quot; role=&quot;region&quot; aria-label=&quot;Code sample:
      JavaScript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-comment&quot;&gt;// Get all marks&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-keyword&quot;&gt;const&lt;/span&gt;&lt;span&gt; marks &lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt;=&lt;/span&gt;&lt;span&gt; performance.&lt;/span&gt;&lt;span class=&quot;tk-function&quot;&gt;getEntriesByType&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span
      class=&quot;tk-string&quot;&gt;&#39;mark&#39;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-comment&quot;&gt;// Get all measures&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-keyword&quot;&gt;const&lt;/span&gt;&lt;span&gt; measures &lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt;=&lt;/span&gt;&lt;span&gt; performance.&lt;/span&gt;&lt;span
      class=&quot;tk-function&quot;&gt;getEntriesByType&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;&#39;measure&#39;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-comment&quot;&gt;// Get entries for a specific component&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-keyword&quot;&gt;const&lt;/span&gt;&lt;span&gt;
      buttonMeasures &lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt;=&lt;/span&gt;&lt;span&gt; performance.&lt;/span&gt;&lt;span class=&quot;tk-function&quot;&gt;getEntriesByName&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;&#39;my-button:upgrade&#39;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;h3&gt;Viewing in DevTools&lt;/h3&gt; &lt;p&gt;Performance marks appear in the Performance tab, where you can see them
      visualized on a timeline alongside other browser events. This is incredibly useful for understanding how your component initialization fits into the overall page load sequence.&lt;/p&gt; &lt;p&gt;To view your marks:&lt;/p&gt; &lt;ol&gt; &lt;li&gt;Open Chrome DevTools&lt;/li&gt; &lt;li&gt;Go to the Performance tab&lt;/li&gt; &lt;li&gt;Record a new profile&lt;/li&gt; &lt;li&gt;Look for your marks in the User Timing section&lt;/li&gt; &lt;/ol&gt; &lt;h3&gt;Integration with Lighthouse&lt;/h3&gt;
      &lt;p&gt;Lighthouse extracts User Timing data and displays it in your reports. This means your custom performance marks show up automatically in your Lighthouse audits without any additional configuration.&lt;/p&gt; &lt;hr /&gt; &lt;h2 id=&quot;reporting-to-analytics&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;#reporting-to-analytics&quot;&gt;#&lt;/a&gt; Reporting to analytics&lt;/h2&gt; &lt;p&gt;Performance marks are only useful if you act on them. For production
      monitoring, you&#39;ll want to send these metrics to your analytics platform so you can track performance over time and across different user segments.&lt;/p&gt; &lt;figure class=&quot;code-block&quot; data-lang=&quot;js&quot; data-edge=&quot;yellow&quot; data-line-numbers&gt;&lt;div class=&quot;code-block-head&quot;&gt;&lt;figcaption&gt;&lt;span class=&quot;code-block-lang&quot;&gt;JavaScript&lt;/span&gt;&lt;/figcaption&gt;&lt;div class=&quot;code-block-tools&quot;&gt;&lt;button
      type=&quot;button&quot; class=&quot;code-btn code-copy&quot; hidden&gt;Copy&lt;/button&gt;&lt;/div&gt;&lt;/div&gt;&lt;pre class=&quot;shiki&quot; tabindex=&quot;0&quot; role=&quot;region&quot; aria-label=&quot;Code sample: JavaScript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-function&quot;&gt;_markFirstRender&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-keyword&quot;&gt;
      if&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt;!this&lt;/span&gt;&lt;span&gt;._hasRendered) {&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-keyword&quot;&gt; this&lt;/span&gt;&lt;span&gt;._hasRendered &lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;tk-number&quot;&gt; true&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;
      performance.&lt;/span&gt;&lt;span class=&quot;tk-function&quot;&gt;mark&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;`${&lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;.localName&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;}:first-render`&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span
      class=&quot;tk-keyword&quot;&gt; const&lt;/span&gt;&lt;span&gt; measure &lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt;=&lt;/span&gt;&lt;span&gt; performance.&lt;/span&gt;&lt;span class=&quot;tk-function&quot;&gt;measure&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-string&quot;&gt; `${&lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;.localName&lt;/span&gt;&lt;span
      class=&quot;tk-string&quot;&gt;}:upgrade`&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-string&quot;&gt; `${&lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;.localName&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;}:connected`&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-string&quot;&gt;
      `${&lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;.localName&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;}:first-render`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; );&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-comment&quot;&gt; // Report to your analytics platform&lt;/span&gt;&lt;/span&gt;&lt;span
      class=&quot;line&quot;&gt;&lt;span class=&quot;tk-keyword&quot;&gt; if&lt;/span&gt;&lt;span&gt; (window.analytics) {&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; window.analytics.&lt;/span&gt;&lt;span class=&quot;tk-function&quot;&gt;track&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;&#39;Component Performance&#39;&lt;/span&gt;&lt;span&gt;, {&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; component:
      &lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt;this&lt;/span&gt;&lt;span&gt;.localName,&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; duration: measure.duration,&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; userAgent: navigator.userAgent&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; });&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; }&lt;/span&gt;&lt;/span&gt;&lt;span
      class=&quot;line&quot;&gt;&lt;span&gt; }&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;hr /&gt; &lt;h2 id=&quot;advanced-patterns&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;#advanced-patterns&quot;&gt;#&lt;/a&gt; Advanced patterns&lt;/h2&gt; &lt;h3&gt;Conditional instrumentation&lt;/h3&gt; &lt;p&gt;You probably don&#39;t want performance overhead in production unless
      you&#39;re actively monitoring. Use environment flags to control when instrumentation runs. This works equally well with both the base class and mixin approaches:&lt;/p&gt; &lt;p&gt;&lt;strong&gt;With a base class:&lt;/strong&gt;&lt;/p&gt; &lt;figure class=&quot;code-block&quot; data-lang=&quot;js&quot; data-edge=&quot;yellow&quot; data-line-numbers&gt;&lt;div class=&quot;code-block-head&quot;&gt;&lt;figcaption&gt;&lt;span
      class=&quot;code-block-lang&quot;&gt;JavaScript&lt;/span&gt;&lt;/figcaption&gt;&lt;div class=&quot;code-block-tools&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;code-btn code-copy&quot; hidden&gt;Copy&lt;/button&gt;&lt;/div&gt;&lt;/div&gt;&lt;pre class=&quot;shiki&quot; tabindex=&quot;0&quot; role=&quot;region&quot; aria-label=&quot;Code sample: JavaScript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-keyword&quot;&gt;export&lt;/span&gt;&lt;span
      class=&quot;tk-keyword&quot;&gt; class&lt;/span&gt;&lt;span class=&quot;tk-function&quot;&gt; PerformanceTrackedElement&lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt; extends&lt;/span&gt;&lt;span&gt; HTMLElement {&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-keyword&quot;&gt; constructor&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-keyword&quot;&gt;
      super&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-keyword&quot;&gt; this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span class=&quot;tk-function&quot;&gt;attachShadow&lt;/span&gt;&lt;span&gt;({ mode: &lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;&#39;open&#39;&lt;/span&gt;&lt;span&gt; });&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-keyword&quot;&gt;
      this&lt;/span&gt;&lt;span&gt;._hasRendered &lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;tk-number&quot;&gt; false&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; }&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-function&quot;&gt; connectedCallback&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/span&gt;&lt;span
      class=&quot;line&quot;&gt;&lt;span class=&quot;tk-keyword&quot;&gt; if&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt;this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span class=&quot;tk-function&quot;&gt;shouldTrackPerformance&lt;/span&gt;&lt;span&gt;() &lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt;&amp;#x26;&amp;#x26;&lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt; !this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span
      class=&quot;tk-function&quot;&gt;hasAttribute&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;&#39;data-perf-marked&#39;&lt;/span&gt;&lt;span&gt;)) {&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; performance.&lt;/span&gt;&lt;span class=&quot;tk-function&quot;&gt;mark&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;`${&lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt;this&lt;/span&gt;&lt;span
      class=&quot;tk-string&quot;&gt;.localName&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;}:connected`&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-keyword&quot;&gt; this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span class=&quot;tk-function&quot;&gt;setAttribute&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;&#39;data-perf-marked&#39;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span
      class=&quot;tk-string&quot;&gt;&#39;&#39;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; }&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-keyword&quot;&gt; this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span class=&quot;tk-function&quot;&gt;render&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; }&lt;/span&gt;&lt;/span&gt;&lt;span
      class=&quot;line&quot;&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-function&quot;&gt; shouldTrackPerformance&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-comment&quot;&gt; // Only track in development or for a sample of production users&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-keyword&quot;&gt; const&lt;/span&gt;&lt;span&gt; isDev &lt;/span&gt;&lt;span
      class=&quot;tk-keyword&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt; !&lt;/span&gt;&lt;span&gt;window.location.hostname.&lt;/span&gt;&lt;span class=&quot;tk-function&quot;&gt;includes&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;&#39;production.com&#39;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-keyword&quot;&gt; return&lt;/span&gt;&lt;span&gt; isDev &lt;/span&gt;&lt;span
      class=&quot;tk-keyword&quot;&gt;||&lt;/span&gt;&lt;span&gt; Math.&lt;/span&gt;&lt;span class=&quot;tk-function&quot;&gt;random&lt;/span&gt;&lt;span&gt;() &lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span class=&quot;tk-number&quot;&gt; 0.1&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; }&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span
      class=&quot;tk-function&quot;&gt; _markFirstRender&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-keyword&quot;&gt; if&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt;!this&lt;/span&gt;&lt;span&gt;._hasRendered &lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt;&amp;#x26;&amp;#x26;&lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt; this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span
      class=&quot;tk-function&quot;&gt;shouldTrackPerformance&lt;/span&gt;&lt;span&gt;()) {&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-keyword&quot;&gt; this&lt;/span&gt;&lt;span&gt;._hasRendered &lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;tk-number&quot;&gt; true&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; performance.&lt;/span&gt;&lt;span
      class=&quot;tk-function&quot;&gt;mark&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;`${&lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;.localName&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;}:first-render`&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; performance.&lt;/span&gt;&lt;span
      class=&quot;tk-function&quot;&gt;measure&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-string&quot;&gt; `${&lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;.localName&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;}:upgrade`&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-string&quot;&gt;
      `${&lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;.localName&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;}:connected`&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-string&quot;&gt; `${&lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;.localName&lt;/span&gt;&lt;span
      class=&quot;tk-string&quot;&gt;}:first-render`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; );&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; }&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; }&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;button type=&quot;button&quot; class=&quot;code-expand&quot; aria-expanded=&quot;false&quot;
      hidden&gt;Show all 34 lines&lt;/button&gt;&lt;/figure&gt;&lt;p&gt;&lt;strong&gt;With a mixin:&lt;/strong&gt;&lt;/p&gt; &lt;figure class=&quot;code-block&quot; data-lang=&quot;js&quot; data-edge=&quot;yellow&quot; data-line-numbers&gt;&lt;div class=&quot;code-block-head&quot;&gt;&lt;figcaption&gt;&lt;span class=&quot;code-block-lang&quot;&gt;JavaScript&lt;/span&gt;&lt;/figcaption&gt;&lt;div class=&quot;code-block-tools&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;code-btn code-copy&quot;
      hidden&gt;Copy&lt;/button&gt;&lt;/div&gt;&lt;/div&gt;&lt;pre class=&quot;shiki&quot; tabindex=&quot;0&quot; role=&quot;region&quot; aria-label=&quot;Code sample: JavaScript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-keyword&quot;&gt;export&lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt; const&lt;/span&gt;&lt;span class=&quot;tk-function&quot;&gt; PerformanceTrackingMixin&lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt; =&lt;/span&gt;&lt;span&gt;
      (SuperClass) &lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt;=&gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-keyword&quot;&gt; return&lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt; class&lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt; extends&lt;/span&gt;&lt;span&gt; SuperClass {&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-keyword&quot;&gt;
      constructor&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-keyword&quot;&gt; super&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-keyword&quot;&gt; this&lt;/span&gt;&lt;span&gt;._hasRendered &lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;tk-number&quot;&gt; false&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;span
      class=&quot;line&quot;&gt;&lt;span&gt; }&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-function&quot;&gt; connectedCallback&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-keyword&quot;&gt; if&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt;this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span
      class=&quot;tk-function&quot;&gt;shouldTrackPerformance&lt;/span&gt;&lt;span&gt;() &lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt;&amp;#x26;&amp;#x26;&lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt; !this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span class=&quot;tk-function&quot;&gt;hasAttribute&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;&#39;data-perf-marked&#39;&lt;/span&gt;&lt;span&gt;)) {&lt;/span&gt;&lt;/span&gt;&lt;span
      class=&quot;line&quot;&gt;&lt;span&gt; performance.&lt;/span&gt;&lt;span class=&quot;tk-function&quot;&gt;mark&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;`${&lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;.localName&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;}:connected`&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span
      class=&quot;tk-keyword&quot;&gt; this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span class=&quot;tk-function&quot;&gt;setAttribute&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;&#39;data-perf-marked&#39;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;&#39;&#39;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; }&lt;/span&gt;&lt;/span&gt;&lt;span
      class=&quot;line&quot;&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-keyword&quot;&gt; if&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt;super&lt;/span&gt;&lt;span&gt;.connectedCallback) {&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-keyword&quot;&gt; super&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span
      class=&quot;tk-function&quot;&gt;connectedCallback&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; }&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; }&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-function&quot;&gt; shouldTrackPerformance&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span
      class=&quot;tk-keyword&quot;&gt; const&lt;/span&gt;&lt;span&gt; isDev &lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt; !&lt;/span&gt;&lt;span&gt;window.location.hostname.&lt;/span&gt;&lt;span class=&quot;tk-function&quot;&gt;includes&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;&#39;production.com&#39;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span
      class=&quot;tk-keyword&quot;&gt; return&lt;/span&gt;&lt;span&gt; isDev &lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt;||&lt;/span&gt;&lt;span&gt; Math.&lt;/span&gt;&lt;span class=&quot;tk-function&quot;&gt;random&lt;/span&gt;&lt;span&gt;() &lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span class=&quot;tk-number&quot;&gt; 0.1&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; }&lt;/span&gt;&lt;/span&gt;&lt;span
      class=&quot;line&quot;&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-function&quot;&gt; _markFirstRender&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-keyword&quot;&gt; if&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt;!this&lt;/span&gt;&lt;span&gt;._hasRendered &lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt;&amp;#x26;&amp;#x26;&lt;/span&gt;&lt;span
      class=&quot;tk-keyword&quot;&gt; this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span class=&quot;tk-function&quot;&gt;shouldTrackPerformance&lt;/span&gt;&lt;span&gt;()) {&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-keyword&quot;&gt; this&lt;/span&gt;&lt;span&gt;._hasRendered &lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;tk-number&quot;&gt; true&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;span
      class=&quot;line&quot;&gt;&lt;span&gt; performance.&lt;/span&gt;&lt;span class=&quot;tk-function&quot;&gt;mark&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;`${&lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;.localName&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;}:first-render`&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;span
      class=&quot;line&quot;&gt;&lt;span&gt; performance.&lt;/span&gt;&lt;span class=&quot;tk-function&quot;&gt;measure&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-string&quot;&gt; `${&lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;.localName&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;}:upgrade`&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;span
      class=&quot;line&quot;&gt;&lt;span class=&quot;tk-string&quot;&gt; `${&lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;.localName&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;}:connected`&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-string&quot;&gt; `${&lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt;this&lt;/span&gt;&lt;span
      class=&quot;tk-string&quot;&gt;.localName&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;}:first-render`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; );&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; }&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; }&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-function&quot;&gt;
      disconnectedCallback&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; performance.&lt;/span&gt;&lt;span class=&quot;tk-function&quot;&gt;clearMarks&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;`${&lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;.localName&lt;/span&gt;&lt;span
      class=&quot;tk-string&quot;&gt;}:connected`&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; performance.&lt;/span&gt;&lt;span class=&quot;tk-function&quot;&gt;clearMarks&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;`${&lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;.localName&lt;/span&gt;&lt;span
      class=&quot;tk-string&quot;&gt;}:first-render`&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; performance.&lt;/span&gt;&lt;span class=&quot;tk-function&quot;&gt;clearMeasures&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;`${&lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;.localName&lt;/span&gt;&lt;span
      class=&quot;tk-string&quot;&gt;}:upgrade`&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-keyword&quot;&gt; if&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt;super&lt;/span&gt;&lt;span&gt;.disconnectedCallback) {&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-keyword&quot;&gt;
      super&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span class=&quot;tk-function&quot;&gt;disconnectedCallback&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; }&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; }&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; };&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;};&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;button
      type=&quot;button&quot; class=&quot;code-expand&quot; aria-expanded=&quot;false&quot; hidden&gt;Show all 47 lines&lt;/button&gt;&lt;/figure&gt;&lt;h3&gt;Measuring nested component trees&lt;/h3&gt; &lt;p&gt;When you have components that render other components, you can track the entire composition tree:&lt;/p&gt; &lt;figure class=&quot;code-block&quot; data-lang=&quot;js&quot; data-edge=&quot;yellow&quot;&gt;&lt;div class=&quot;code-block-head&quot;&gt;&lt;figcaption&gt;&lt;span
      class=&quot;code-block-lang&quot;&gt;JavaScript&lt;/span&gt;&lt;/figcaption&gt;&lt;div class=&quot;code-block-tools&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;code-btn code-copy&quot; hidden&gt;Copy&lt;/button&gt;&lt;/div&gt;&lt;/div&gt;&lt;pre class=&quot;shiki&quot; tabindex=&quot;0&quot; role=&quot;region&quot; aria-label=&quot;Code sample: JavaScript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;performance.&lt;/span&gt;&lt;span
      class=&quot;tk-function&quot;&gt;mark&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;&#39;page-header:composition-start&#39;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-comment&quot;&gt;// Render happens here with nested components&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;span
      class=&quot;line&quot;&gt;&lt;span&gt;performance.&lt;/span&gt;&lt;span class=&quot;tk-function&quot;&gt;mark&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;&#39;page-header:composition-end&#39;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;performance.&lt;/span&gt;&lt;span
      class=&quot;tk-function&quot;&gt;measure&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-string&quot;&gt; &#39;page-header:full-composition&#39;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-string&quot;&gt; &#39;page-header:composition-start&#39;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-string&quot;&gt;
      &#39;page-header:composition-end&#39;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;h3&gt;Tracking lazy-loaded components&lt;/h3&gt; &lt;p&gt;For components loaded dynamically, track the time from import to render:&lt;/p&gt; &lt;figure class=&quot;code-block&quot; data-lang=&quot;js&quot; data-edge=&quot;yellow&quot; data-line-numbers&gt;&lt;div
      class=&quot;code-block-head&quot;&gt;&lt;figcaption&gt;&lt;span class=&quot;code-block-lang&quot;&gt;JavaScript&lt;/span&gt;&lt;/figcaption&gt;&lt;div class=&quot;code-block-tools&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;code-btn code-copy&quot; hidden&gt;Copy&lt;/button&gt;&lt;/div&gt;&lt;/div&gt;&lt;pre class=&quot;shiki&quot; tabindex=&quot;0&quot; role=&quot;region&quot; aria-label=&quot;Code sample: JavaScript&quot;&gt;&lt;code&gt;&lt;span
      class=&quot;line&quot;&gt;&lt;span&gt;performance.&lt;/span&gt;&lt;span class=&quot;tk-function&quot;&gt;mark&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;&#39;my-component:import-start&#39;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-keyword&quot;&gt;const&lt;/span&gt;&lt;span&gt; { MyComponent } &lt;/span&gt;&lt;span
      class=&quot;tk-keyword&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt; await&lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt; import&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;&#39;./my-component.js&#39;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;performance.&lt;/span&gt;&lt;span
      class=&quot;tk-function&quot;&gt;mark&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;&#39;my-component:import-end&#39;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;performance.&lt;/span&gt;&lt;span class=&quot;tk-function&quot;&gt;measure&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-string&quot;&gt;
      &#39;my-component:lazy-load&#39;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-string&quot;&gt; &#39;my-component:import-start&#39;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-string&quot;&gt; &#39;my-component:import-end&#39;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;span
      class=&quot;line&quot;&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;customElements.&lt;/span&gt;&lt;span class=&quot;tk-function&quot;&gt;define&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;&#39;my-component&#39;&lt;/span&gt;&lt;span&gt;, MyComponent);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;hr /&gt; &lt;h2 id=&quot;cleaning-up-marks-and-measures&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot;
      href=&quot;#cleaning-up-marks-and-measures&quot;&gt;#&lt;/a&gt; Cleaning up marks and measures&lt;/h2&gt; &lt;p&gt;Performance entries accumulate in the browser&#39;s performance buffer. Use clearMarks and clearMeasures to clean up once data is no longer needed.&lt;/p&gt; &lt;figure class=&quot;code-block&quot; data-lang=&quot;js&quot; data-edge=&quot;yellow&quot;&gt;&lt;div class=&quot;code-block-head&quot;&gt;&lt;figcaption&gt;&lt;span
      class=&quot;code-block-lang&quot;&gt;JavaScript&lt;/span&gt;&lt;/figcaption&gt;&lt;div class=&quot;code-block-tools&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;code-btn code-copy&quot; hidden&gt;Copy&lt;/button&gt;&lt;/div&gt;&lt;/div&gt;&lt;pre class=&quot;shiki&quot; tabindex=&quot;0&quot; role=&quot;region&quot; aria-label=&quot;Code sample: JavaScript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-comment&quot;&gt;// Clear specific
      marks&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;performance.&lt;/span&gt;&lt;span class=&quot;tk-function&quot;&gt;clearMarks&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;&#39;my-component:connected&#39;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;performance.&lt;/span&gt;&lt;span class=&quot;tk-function&quot;&gt;clearMarks&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span
      class=&quot;tk-string&quot;&gt;&#39;my-component:first-render&#39;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-comment&quot;&gt;// Clear specific measures&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;performance.&lt;/span&gt;&lt;span class=&quot;tk-function&quot;&gt;clearMeasures&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span
      class=&quot;tk-string&quot;&gt;&#39;my-component:upgrade&#39;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-comment&quot;&gt;// Or clear all marks and measures&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;performance.&lt;/span&gt;&lt;span class=&quot;tk-function&quot;&gt;clearMarks&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;&lt;span
      class=&quot;line&quot;&gt;&lt;span&gt;performance.&lt;/span&gt;&lt;span class=&quot;tk-function&quot;&gt;clearMeasures&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;p&gt;For components that might be added and removed from the DOM multiple times, clear marks in the &lt;code&gt;disconnectedCallback&lt;/code&gt;:&lt;/p&gt; &lt;figure class=&quot;code-block&quot; data-lang=&quot;js&quot; data-edge=&quot;yellow&quot;&gt;&lt;div
      class=&quot;code-block-head&quot;&gt;&lt;figcaption&gt;&lt;span class=&quot;code-block-lang&quot;&gt;JavaScript&lt;/span&gt;&lt;/figcaption&gt;&lt;div class=&quot;code-block-tools&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;code-btn code-copy&quot; hidden&gt;Copy&lt;/button&gt;&lt;/div&gt;&lt;/div&gt;&lt;pre class=&quot;shiki&quot; tabindex=&quot;0&quot; role=&quot;region&quot; aria-label=&quot;Code sample: JavaScript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span
      class=&quot;tk-function&quot;&gt;disconnectedCallback&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; performance.&lt;/span&gt;&lt;span class=&quot;tk-function&quot;&gt;clearMarks&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;`${&lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;.localName&lt;/span&gt;&lt;span
      class=&quot;tk-string&quot;&gt;}:connected`&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; performance.&lt;/span&gt;&lt;span class=&quot;tk-function&quot;&gt;clearMarks&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;`${&lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;.localName&lt;/span&gt;&lt;span
      class=&quot;tk-string&quot;&gt;}:first-render`&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; performance.&lt;/span&gt;&lt;span class=&quot;tk-function&quot;&gt;clearMeasures&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;`${&lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;.localName&lt;/span&gt;&lt;span
      class=&quot;tk-string&quot;&gt;}:upgrade`&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;hr /&gt; &lt;h2 id=&quot;what-to-measure-(and-what-to-skip)&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;#what-to-measure-(and-what-to-skip)&quot;&gt;#&lt;/a&gt; What to measure (and what to skip)&lt;/h2&gt; &lt;p&gt;Not everything needs a performance mark.
      Focus on the moments that impact user experience:&lt;/p&gt; &lt;h3&gt;Measure these&lt;/h3&gt; &lt;ul&gt; &lt;li&gt;&lt;strong&gt;Initial connection to DOM&lt;/strong&gt;: Users are waiting for the component to appear&lt;/li&gt; &lt;li&gt;&lt;strong&gt;First render complete&lt;/strong&gt;: The component is now visible and interactive&lt;/li&gt; &lt;li&gt;&lt;strong&gt;Component upgrade time&lt;/strong&gt;: Total time from definition to first paint&lt;/li&gt; &lt;li&gt;&lt;strong&gt;Critical async
      operations&lt;/strong&gt;: Data fetching, heavy computations&lt;/li&gt; &lt;li&gt;&lt;strong&gt;User interactions&lt;/strong&gt;: Click handlers, form submissions, animations&lt;/li&gt; &lt;/ul&gt; &lt;h3&gt;Skip these&lt;/h3&gt; &lt;ul&gt; &lt;li&gt;&lt;strong&gt;Every update cycle&lt;/strong&gt;: Too noisy, adds overhead&lt;/li&gt; &lt;li&gt;&lt;strong&gt;Trivial getters and setters&lt;/strong&gt;: Not worth the measurement cost&lt;/li&gt; &lt;li&gt;&lt;strong&gt;Internal helper
      methods&lt;/strong&gt;: Focus on user-facing milestones&lt;/li&gt; &lt;li&gt;&lt;strong&gt;Style recalculations&lt;/strong&gt;: The browser already tracks these&lt;/li&gt; &lt;/ul&gt; &lt;hr /&gt; &lt;h2 id=&quot;real-world-example&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;#real-world-example&quot;&gt;#&lt;/a&gt; Real-world example&lt;/h2&gt; &lt;p&gt;Let&#39;s put this all together with a complete example of a button component that tracks its
      performance:&lt;/p&gt; &lt;figure class=&quot;code-block&quot; data-lang=&quot;js&quot; data-edge=&quot;yellow&quot; data-line-numbers&gt;&lt;div class=&quot;code-block-head&quot;&gt;&lt;figcaption&gt;&lt;span class=&quot;code-block-lang&quot;&gt;JavaScript&lt;/span&gt;&lt;/figcaption&gt;&lt;div class=&quot;code-block-tools&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;code-btn code-copy&quot; hidden&gt;Copy&lt;/button&gt;&lt;/div&gt;&lt;/div&gt;&lt;pre class=&quot;shiki&quot;
      tabindex=&quot;0&quot; role=&quot;region&quot; aria-label=&quot;Code sample: JavaScript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-keyword&quot;&gt;export&lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt; class&lt;/span&gt;&lt;span class=&quot;tk-function&quot;&gt; PerformanceTrackedButton&lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt; extends&lt;/span&gt;&lt;span&gt; HTMLElement {&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span
      class=&quot;tk-keyword&quot;&gt; constructor&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-keyword&quot;&gt; super&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-keyword&quot;&gt; this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span class=&quot;tk-function&quot;&gt;attachShadow&lt;/span&gt;&lt;span&gt;({ mode: &lt;/span&gt;&lt;span
      class=&quot;tk-string&quot;&gt;&#39;open&#39;&lt;/span&gt;&lt;span&gt; });&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-keyword&quot;&gt; this&lt;/span&gt;&lt;span&gt;._hasRendered &lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;tk-number&quot;&gt; false&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; }&lt;/span&gt;&lt;/span&gt;&lt;span
      class=&quot;line&quot;&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-keyword&quot;&gt; static&lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt; get&lt;/span&gt;&lt;span class=&quot;tk-function&quot;&gt; observedAttributes&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-keyword&quot;&gt; return&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;span
      class=&quot;tk-string&quot;&gt;&#39;label&#39;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;&#39;disabled&#39;&lt;/span&gt;&lt;span&gt;];&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; }&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-function&quot;&gt; connectedCallback&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/span&gt;&lt;span
      class=&quot;line&quot;&gt;&lt;span class=&quot;tk-keyword&quot;&gt; if&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt;!this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span class=&quot;tk-function&quot;&gt;hasAttribute&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;&#39;data-perf-marked&#39;&lt;/span&gt;&lt;span&gt;)) {&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; performance.&lt;/span&gt;&lt;span
      class=&quot;tk-function&quot;&gt;mark&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;`${&lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;.localName&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;}:connected`&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-keyword&quot;&gt; this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span
      class=&quot;tk-function&quot;&gt;setAttribute&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;&#39;data-perf-marked&#39;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;&#39;&#39;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; }&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-keyword&quot;&gt; this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span
      class=&quot;tk-function&quot;&gt;render&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; }&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-function&quot;&gt; attributeChangedCallback&lt;/span&gt;&lt;span&gt;(name, oldValue, newValue) {&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-keyword&quot;&gt; if&lt;/span&gt;&lt;span&gt;
      (oldValue &lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt;!==&lt;/span&gt;&lt;span&gt; newValue) {&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-keyword&quot;&gt; this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span class=&quot;tk-function&quot;&gt;render&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; }&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;
      }&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-function&quot;&gt; render&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-keyword&quot;&gt; const&lt;/span&gt;&lt;span&gt; label &lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt; this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span
      class=&quot;tk-function&quot;&gt;getAttribute&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;&#39;label&#39;&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt;||&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt; &#39;Click me&#39;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-keyword&quot;&gt; const&lt;/span&gt;&lt;span&gt; disabled &lt;/span&gt;&lt;span
      class=&quot;tk-keyword&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt; this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span class=&quot;tk-function&quot;&gt;hasAttribute&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;&#39;disabled&#39;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-keyword&quot;&gt;
      this&lt;/span&gt;&lt;span&gt;.shadowRoot.innerHTML &lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt; `&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-string&quot;&gt; &amp;#x3C;style&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-string&quot;&gt; :host {&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-string&quot;&gt; display:
      inline-block;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-string&quot;&gt; }&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-string&quot;&gt; button {&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-string&quot;&gt; padding: 0.5rem 1rem;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-string&quot;&gt; border:
      none;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-string&quot;&gt; border-radius: 4px;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-string&quot;&gt; background: var(--button-bg, #007bff);&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-string&quot;&gt; color: white;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-string&quot;&gt; cursor:
      pointer;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-string&quot;&gt; font-family: inherit;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-string&quot;&gt; font-size: inherit;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-string&quot;&gt; }&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-string&quot;&gt;
      button:disabled {&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-string&quot;&gt; opacity: 0.5;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-string&quot;&gt; cursor: not-allowed;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-string&quot;&gt; }&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-string&quot;&gt;
      button:not(:disabled):hover {&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-string&quot;&gt; background: var(--button-hover-bg, #0056b3);&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-string&quot;&gt; }&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-string&quot;&gt; &amp;#x3C;/style&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-string&quot;&gt;
      &amp;#x3C;button ${&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;disabled &lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt; &#39;disabled&#39;&lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt; :&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt; &#39;&#39;}&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-string&quot;&gt; ${&lt;/span&gt;&lt;span
      class=&quot;tk-string&quot;&gt;label&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-string&quot;&gt; &amp;#x3C;/button&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-string&quot;&gt; `&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-comment&quot;&gt; // Mark first
      render complete&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-keyword&quot;&gt; if&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt;!this&lt;/span&gt;&lt;span&gt;._hasRendered) {&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-keyword&quot;&gt; this&lt;/span&gt;&lt;span&gt;._hasRendered &lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;tk-number&quot;&gt;
      true&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; performance.&lt;/span&gt;&lt;span class=&quot;tk-function&quot;&gt;mark&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;`${&lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;.localName&lt;/span&gt;&lt;span
      class=&quot;tk-string&quot;&gt;}:first-render`&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-keyword&quot;&gt; const&lt;/span&gt;&lt;span&gt; measure &lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt;=&lt;/span&gt;&lt;span&gt; performance.&lt;/span&gt;&lt;span class=&quot;tk-function&quot;&gt;measure&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span
      class=&quot;line&quot;&gt;&lt;span class=&quot;tk-string&quot;&gt; `${&lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;.localName&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;}:upgrade`&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-string&quot;&gt; `${&lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt;this&lt;/span&gt;&lt;span
      class=&quot;tk-string&quot;&gt;.localName&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;}:connected`&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-string&quot;&gt; `${&lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;.localName&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;}:first-render`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;
      );&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-comment&quot;&gt; // Log in development&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-keyword&quot;&gt; if&lt;/span&gt;&lt;span&gt; (window.location.hostname &lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt;===&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt; &#39;localhost&#39;&lt;/span&gt;&lt;span&gt;)
      {&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; console.&lt;/span&gt;&lt;span class=&quot;tk-function&quot;&gt;log&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;`${&lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;.localName&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;} upgraded in ${&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;measure.duration.&lt;/span&gt;&lt;span
      class=&quot;tk-function&quot;&gt;toFixed&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;tk-number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;}ms`&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; }&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; }&lt;/span&gt;&lt;/span&gt;&lt;span
      class=&quot;line&quot;&gt;&lt;span&gt; }&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;customElements.&lt;/span&gt;&lt;span class=&quot;tk-function&quot;&gt;define&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;&#39;perf-button&#39;&lt;/span&gt;&lt;span&gt;, PerformanceTrackedButton);&lt;/span&gt;&lt;/span&gt;&lt;span
      class=&quot;line&quot;&gt;&lt;span&gt;performance.&lt;/span&gt;&lt;span class=&quot;tk-function&quot;&gt;mark&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;&#39;perf-button:defined&#39;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;button type=&quot;button&quot; class=&quot;code-expand&quot; aria-expanded=&quot;false&quot; hidden&gt;Show all 81 lines&lt;/button&gt;&lt;/figure&gt;&lt;hr /&gt; &lt;h2 id=&quot;common-pitfalls&quot;
      tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;#common-pitfalls&quot;&gt;#&lt;/a&gt; Common pitfalls&lt;/h2&gt; &lt;h3&gt;Forgetting to check for existing marks&lt;/h3&gt; &lt;p&gt;If a component disconnects and reconnects, you&#39;ll create duplicate marks unless you guard against it. Always check if the mark already exists or use a flag to track marking state.&lt;/p&gt; &lt;h3&gt;Over-instrumenting&lt;/h3&gt; &lt;p&gt;Resist the urge to mark everything. Too many marks
      create noise and make it harder to find the signals that matter. Focus on initialization and critical user interactions.&lt;/p&gt; &lt;h3&gt;Ignoring browser support&lt;/h3&gt; &lt;p&gt;The User Timing API is supported in all major browsers, but always check if the &lt;code&gt;performance&lt;/code&gt; object exists before calling its methods:&lt;/p&gt; &lt;figure class=&quot;code-block&quot; data-lang=&quot;js&quot; data-edge=&quot;yellow&quot;&gt;&lt;div
      class=&quot;code-block-head&quot;&gt;&lt;figcaption&gt;&lt;span class=&quot;code-block-lang&quot;&gt;JavaScript&lt;/span&gt;&lt;/figcaption&gt;&lt;div class=&quot;code-block-tools&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;code-btn code-copy&quot; hidden&gt;Copy&lt;/button&gt;&lt;/div&gt;&lt;/div&gt;&lt;pre class=&quot;shiki&quot; tabindex=&quot;0&quot; role=&quot;region&quot; aria-label=&quot;Code sample: JavaScript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span
      class=&quot;tk-keyword&quot;&gt;if&lt;/span&gt;&lt;span&gt; (window.performance &lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt;&amp;#x26;&amp;#x26;&lt;/span&gt;&lt;span&gt; performance.mark) {&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; performance.&lt;/span&gt;&lt;span class=&quot;tk-function&quot;&gt;mark&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span
      class=&quot;tk-string&quot;&gt;&#39;my-component:connected&#39;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;h3&gt;Not testing on real devices&lt;/h3&gt; &lt;p&gt;Your laptop is fast. Your users&#39; phones might not be. Always test your instrumented components on representative devices to see what the real performance looks like.&lt;/p&gt; &lt;hr /&gt; &lt;h2
      id=&quot;dos-and-don&#39;ts&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;#dos-and-don&#39;ts&quot;&gt;#&lt;/a&gt; Dos and don&#39;ts&lt;/h2&gt; &lt;h3&gt;Do&lt;/h3&gt; &lt;ul&gt; &lt;li&gt;&lt;strong&gt;Track component lifecycle milestones&lt;/strong&gt;: connection, first render, upgrade time&lt;/li&gt; &lt;li&gt;&lt;strong&gt;Use consistent naming conventions&lt;/strong&gt; across your component library&lt;/li&gt; &lt;li&gt;&lt;strong&gt;Report metrics to
      analytics&lt;/strong&gt; for real-world performance tracking&lt;/li&gt; &lt;li&gt;&lt;strong&gt;Clean up marks and measures&lt;/strong&gt; when they&#39;re no longer needed&lt;/li&gt; &lt;li&gt;&lt;strong&gt;Choose the right pattern&lt;/strong&gt;: use a base class for new libraries, a mixin for existing ones&lt;/li&gt; &lt;li&gt;&lt;strong&gt;Compose mixins&lt;/strong&gt; when you need multiple behaviors&lt;/li&gt; &lt;li&gt;&lt;strong&gt;Always call super methods&lt;/strong&gt; in mixins to maintain
      the inheritance chain&lt;/li&gt; &lt;/ul&gt; &lt;h3&gt;Don&#39;t&lt;/h3&gt; &lt;ul&gt; &lt;li&gt;&lt;strong&gt;Mark every render cycle&lt;/strong&gt;: too expensive and noisy&lt;/li&gt; &lt;li&gt;&lt;strong&gt;Forget to check for performance API availability&lt;/strong&gt;: defensive coding matters&lt;/li&gt; &lt;li&gt;&lt;strong&gt;Ignore the data&lt;/strong&gt;: measure, analyze, act&lt;/li&gt; &lt;li&gt;&lt;strong&gt;Over-complicate your naming&lt;/strong&gt;: keep it simple and
      descriptive&lt;/li&gt; &lt;li&gt;&lt;strong&gt;Skip testing on slower devices&lt;/strong&gt;: that&#39;s where performance matters most&lt;/li&gt; &lt;li&gt;&lt;strong&gt;Forget to call super methods in mixins&lt;/strong&gt;: breaks the inheritance chain&lt;/li&gt; &lt;/ul&gt; &lt;hr /&gt; &lt;h2 id=&quot;wrapping-up&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;#wrapping-up&quot;&gt;#&lt;/a&gt; Wrapping up&lt;/h2&gt; &lt;p&gt;Performance monitoring doesn&#39;t have
      to be complicated. The User Timing API gives you exactly what you need to understand how your web components perform in the wild. By instrumenting key lifecycle moments and reporting metrics that matter, you&#39;ll have the insights you need to make data-driven decisions about optimization.&lt;/p&gt; &lt;p&gt;The pattern is straightforward: mark important moments, measure the time between them, and act on what you learn. Whether you choose a base class or a mixin depends on your architecture—base
      classes work great for new projects with a clean slate, while mixins shine when you need to add tracking to existing components or compose multiple behaviors. Either way, you set it up once and suddenly your entire component library has performance visibility without any per-component boilerplate.&lt;/p&gt; &lt;p&gt;Start simple; track connection and first render. See what the numbers tell you. Then expand to track the interactions and operations that matter most to your users. The data will surprise
      you, and that&#39;s exactly the point. You can&#39;t optimize what you can&#39;t measure, and now you can measure everything that matters.&lt;/p&gt; &lt;p&gt;Six months from now when you&#39;re investigating a performance regression or optimizing for a new market with slower network speeds, you&#39;ll be grateful you have this data. Your users will be grateful too, even if they never know you&#39;re tracking it. Build fast components, measure to prove they&#39;re fast, and keep measuring to make sure
      they stay that way.&lt;/p&gt;
    </content>
    <summary>Web components are great for encapsulation and reusability, but how do you know if they&#39;re performing well? This post shows how to upgrade your components with the User Timing API to get actionable insights.</summary>
    <category term="blog" />
    <category term="web components" />
    <category term="performance" />
    <category term="tooling" />
  </entry>
  <entry>
    <title>A Storybook format that scales</title>
    <link href="https://castastrophe.blog/blog/a-storybook-format-that-scales/" />
    <updated>2025-10-31T00:00:00.000Z</updated>
    <id>https://castastrophe.blog/blog/a-storybook-format-that-scales/</id>
    <content type="html"
      >&lt;p&gt;Here&#39;s the thing about &lt;a href=&quot;https://storybook.js.org&quot;&gt;Storybook&lt;/a&gt;: it&#39;s incredibly powerful, flexible, and dynamic...but because of that flexibility, it can quickly devolve into a tangled mess of inconsistent patterns, duplicated code, and confusing file structures. We&#39;ve all been there—you start with good intentions, build a few components, and six months later you&#39;re drowning in technical debt wondering why nobody can find anything.&lt;/p&gt;
      &lt;p&gt;This guide is your roadmap to building a Storybook that actually scales. Whether you&#39;re starting fresh or untangling an existing setup, you&#39;ll learn the patterns and conventions that separate the maintainable Storybooks from the ones that make developers groan. We&#39;re talking about practical, battle-tested approaches that&#39;ll make your component library a joy to work with instead of a source of frustration.&lt;/p&gt; &lt;p&gt;If you&#39;re new to Storybook, here&#39;s the
      elevator pitch: it&#39;s a dedicated workshop for building UI components and pages in isolation, separate from your business logic, APIs, and application state. That means you can develop and test those hard-to-reach edge cases without having to orchestrate your entire app into the right condition. Even better, it brings together your UI, examples, and documentation in one place, creating a single source of truth that makes it easier to discover and reuse existing patterns. It&#39;s your front-end
      playground and living style guide rolled into one.&lt;/p&gt; &lt;hr /&gt; &lt;h2 id=&quot;philosophy&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;#philosophy&quot;&gt;#&lt;/a&gt; Philosophy&lt;/h2&gt; &lt;p&gt;Before thinking about how you want to structure your Storybook files, you need to determine who your audience is and what they need to know. Storybook has a lot of powerful plugins and tools that can either enhance or confuse, so my advice is to be judicious
      about what you use and why. Is your audience primarily developers, designers, or product managers? Do they need to know about the component&#39;s props, events, and methods? How about accessibility requirements or performance? How many code examples and usage patterns do you need to provide?&lt;/p&gt; &lt;p&gt;Remember that less is more. You don&#39;t necessarily need to create a new story for every variant, state, or use case. Sometimes, a single documentation page and one default story with robust
      props available is enough. Letting your users explore the component through the Storybook interface will often reveal additional use cases and patterns that you may not have considered.&lt;/p&gt; &lt;hr /&gt; &lt;h2 id=&quot;foundations&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;#foundations&quot;&gt;#&lt;/a&gt; Foundations&lt;/h2&gt; &lt;p&gt;Before we get into the UI patterns and conventions, let&#39;s talk about some tooling choices that will help you create a
      consistent and maintainable Storybook.&lt;/p&gt; &lt;h3&gt;Aliasing&lt;/h3&gt; &lt;p&gt;Here&#39;s a quick win: set up your Storybook with package aliases instead relying on relative imports to reference other components. Your future self will thank you when it&#39;s time to refactor, and your paths will be more readable:&lt;/p&gt; &lt;figure class=&quot;code-block&quot; data-lang=&quot;js&quot; data-edge=&quot;yellow&quot;&gt;&lt;div class=&quot;code-block-head&quot;&gt;&lt;figcaption&gt;&lt;span
      class=&quot;code-block-lang&quot;&gt;JavaScript&lt;/span&gt;&lt;/figcaption&gt;&lt;div class=&quot;code-block-tools&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;code-btn code-copy&quot; hidden&gt;Copy&lt;/button&gt;&lt;/div&gt;&lt;/div&gt;&lt;pre class=&quot;shiki&quot; tabindex=&quot;0&quot; role=&quot;region&quot; aria-label=&quot;Code sample: JavaScript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-comment&quot;&gt;//
      Good&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-keyword&quot;&gt;import&lt;/span&gt;&lt;span&gt; { Template } &lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt; &quot;@my-design-system/button/stories/template.js&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-comment&quot;&gt;//
      Avoid&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-keyword&quot;&gt;import&lt;/span&gt;&lt;span&gt; { Template } &lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt; &quot;../../../button/stories/template.js&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;h3&gt;Rendering libraries&lt;/h3&gt; &lt;p&gt;This suggestion is mostly for projects shipping
      vanilla HTML, CSS, and JavaScript without a framework. If you&#39;re building for &lt;a href=&quot;https://react.dev&quot;&gt;React&lt;/a&gt;, &lt;a href=&quot;https://vuejs.org&quot;&gt;Vue&lt;/a&gt;, or &lt;a href=&quot;https://angular.dev&quot;&gt;Angular&lt;/a&gt;, what rendering engine you use will be dictated by the framework.&lt;/p&gt; &lt;p&gt;For vanilla projects, I recommend leveraging &lt;a href=&quot;https://lit.dev&quot;&gt;lit&lt;/a&gt; to handle your templates. Lit is a library that
      provides a set of utilities for building web components that are easy to test, debug, and reuse. The lit-html library is lightweight and performant with utilities for dynamic rendering that you&#39;ll be grateful for later.&lt;/p&gt; &lt;blockquote&gt; &lt;h4&gt;Bonus tip&lt;/h4&gt; You can even ship the template file with your component for CMS systems like Drupal or WordPress. &lt;/blockquote&gt; &lt;p&gt;The lit package provides features such as:&lt;/p&gt; &lt;ul&gt;
      &lt;li&gt;&lt;strong&gt;Conditional classes&lt;/strong&gt;: apply classes based on the Storybook &lt;code&gt;args&lt;/code&gt; and &lt;code&gt;context&lt;/code&gt;&lt;/li&gt; &lt;li&gt;&lt;strong&gt;Style maps&lt;/strong&gt;: dynamically apply inline styles&lt;/li&gt; &lt;li&gt;&lt;strong&gt;Conditional rendering&lt;/strong&gt;: show or hide elements&lt;/li&gt; &lt;li&gt;&lt;strong&gt;Optional attributes&lt;/strong&gt;: only render attributes when they&#39;re defined&lt;/li&gt; &lt;/ul&gt;
      &lt;h3&gt;Compose, don&#39;t duplicate&lt;/h3&gt; &lt;p&gt;When you&#39;re building complex components or patterns that leverage other components, resist the urge to copy and paste markup. Instead, import and call their templates. This keeps everything consistent and cuts down on the maintenance burden.&lt;/p&gt; &lt;hr /&gt; &lt;h2 id=&quot;file-structure&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;#file-structure&quot;&gt;#&lt;/a&gt; File structure&lt;/h2&gt;
      &lt;p&gt;The best architecture is self-documenting. When your files are organized in a way that is easy to understand and navigate, it becomes easier to contribute to the project and easier to find things when you need to. Though some of this will be dictated by the structure of your component library and the tools you have chosen, there are general principles that will help you create a standardized format. First, it&#39;s important to keep component-focused Storybook assets as close to the component
      source files as possible; this ensures the documentation stays up-to-date with component changes. Inside my component folder, I typically keep a &lt;code&gt;stories&lt;/code&gt; folder with the following files:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;&lt;code&gt;component.stories.js&lt;/code&gt; - story definitions, controls (&lt;code&gt;argTypes&lt;/code&gt;), default values (&lt;code&gt;args&lt;/code&gt;), and configuration&lt;/li&gt; &lt;li&gt;&lt;code&gt;template.js&lt;/code&gt; - reusable render functions
      that accept arguments and return rendered HTML&lt;/li&gt; &lt;li&gt;[optional] &lt;code&gt;component.docs.mdx&lt;/code&gt; - documentation for the component, including usage patterns, code examples, and best practices; alternatively, you can leverage JSDoc comments in your story file&lt;/li&gt; &lt;li&gt;[optional] &lt;code&gt;component.test.js&lt;/code&gt; - visual regression testing grids (used to define the component&#39;s states and variants for tools like 💕 &lt;a
      href=&quot;https://www.chromatic.com&quot;&gt;Chromatic&lt;/a&gt;)&lt;/li&gt; &lt;/ul&gt; &lt;hr /&gt; &lt;h2 id=&quot;story-files&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;#story-files&quot;&gt;#&lt;/a&gt; Story files&lt;/h2&gt; &lt;p&gt;The story file (&lt;code&gt;.stories.js&lt;/code&gt;) is where all the magic happens—it&#39;s where you define your component&#39;s stories, controls, and configuration. I like to keep all my stories for a component in a single
      file. This makes it easier to find and update stories, and it makes it easier to see all the stories for a component at a glance. That said, if a component (or more likely, a pattern) has a lot of stories, it might make sense to split them into multiple files. Let&#39;s break down each essential part of a story file.&lt;/p&gt; &lt;h3&gt;Default export&lt;/h3&gt; &lt;p&gt;Your &lt;a href=&quot;https://storybook.js.org/docs/api/csf&quot;&gt;default export&lt;/a&gt; is the entry point for all your
      stories. It contains all the metadata and configuration for your component:&lt;/p&gt; &lt;figure class=&quot;code-block&quot; data-lang=&quot;js&quot; data-edge=&quot;yellow&quot;&gt;&lt;div class=&quot;code-block-head&quot;&gt;&lt;figcaption&gt;&lt;span class=&quot;code-block-lang&quot;&gt;JavaScript&lt;/span&gt;&lt;/figcaption&gt;&lt;div class=&quot;code-block-tools&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;code-btn code-copy&quot;
      hidden&gt;Copy&lt;/button&gt;&lt;/div&gt;&lt;/div&gt;&lt;pre class=&quot;shiki&quot; tabindex=&quot;0&quot; role=&quot;region&quot; aria-label=&quot;Code sample: JavaScript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-keyword&quot;&gt;export&lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt; default&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; title: &lt;/span&gt;&lt;span
      class=&quot;tk-string&quot;&gt;&quot;Components/Button&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; component: &lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;&quot;Button&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; argTypes: { &lt;/span&gt;&lt;span class=&quot;tk-comment&quot;&gt;/* control definitions */&lt;/span&gt;&lt;span&gt; },&lt;/span&gt;&lt;/span&gt;&lt;span
      class=&quot;line&quot;&gt;&lt;span&gt; args: { &lt;/span&gt;&lt;span class=&quot;tk-comment&quot;&gt;/* default values */&lt;/span&gt;&lt;span&gt; },&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; parameters: { &lt;/span&gt;&lt;span class=&quot;tk-comment&quot;&gt;/* additional configuration */&lt;/span&gt;&lt;span&gt; },&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; tags: [ &lt;/span&gt;&lt;span class=&quot;tk-comment&quot;&gt;/* organizational
      tags */&lt;/span&gt;&lt;span&gt; ]&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;};&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;p&gt;Some of the guidance for these properties will differ based on what framework or rendering library you&#39;re using. I&#39;m going to cover how to configure these properties for a vanilla project using lit, however, you can find more detailed information in the &lt;a
      href=&quot;https://storybook.js.org/docs/api/csf&quot;&gt;Storybook documentation&lt;/a&gt; for your specific framework.&lt;/p&gt; &lt;h4&gt;Title&lt;/h4&gt; &lt;p&gt;Pick a clear, human-readable title that reflects your component hierarchy. If your folder structure maps to how you want to group components in the sidebar, you can use a &lt;a href=&quot;https://storybook.js.org/docs/api/main-config/main-config-stories#with-an-array-of-globs&quot;&gt;configuration object&lt;/a&gt; in your configuration
      file (&lt;code&gt;.storybook/main.js&lt;/code&gt;):&lt;/p&gt; &lt;figure class=&quot;code-block&quot; data-lang=&quot;js&quot; data-edge=&quot;yellow&quot; data-line-numbers&gt;&lt;div class=&quot;code-block-head&quot;&gt;&lt;figcaption&gt;&lt;span class=&quot;code-block-lang&quot;&gt;JavaScript&lt;/span&gt;&lt;/figcaption&gt;&lt;div class=&quot;code-block-tools&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;code-btn code-copy&quot; hidden&gt;Copy&lt;/button&gt;&lt;/div&gt;&lt;/div&gt;&lt;pre
      class=&quot;shiki&quot; tabindex=&quot;0&quot; role=&quot;region&quot; aria-label=&quot;Code sample: JavaScript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-keyword&quot;&gt;export&lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt; default&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; stories: [&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;&lt;span
      class=&quot;line&quot;&gt;&lt;span class=&quot;tk-comment&quot;&gt; // 👇 Sets the directory containing your stories&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; directory: &lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;&#39;../packages/components&#39;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-comment&quot;&gt; // 👇 Storybook will load all files that match this glob&lt;/span&gt;&lt;/span&gt;&lt;span
      class=&quot;line&quot;&gt;&lt;span&gt; files: &lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;&#39;*.stories.*&#39;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-comment&quot;&gt; // 👇 Used when generating automatic titles for your stories&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; titlePrefix: &lt;/span&gt;&lt;span
      class=&quot;tk-string&quot;&gt;&#39;MyComponents&#39;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; },&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; ],&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;};&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;p&gt;If you want to create a set of custom categorizations for your components, you can add as many paths as you need to the
      &lt;code&gt;title&lt;/code&gt;. Every slash-separated segment will be used as a group in the sidebar, under which the component will appear.&lt;/p&gt; &lt;figure class=&quot;code-block&quot; data-lang=&quot;js&quot; data-edge=&quot;yellow&quot;&gt;&lt;div class=&quot;code-block-head&quot;&gt;&lt;figcaption&gt;&lt;span class=&quot;code-block-lang&quot;&gt;JavaScript&lt;/span&gt;&lt;/figcaption&gt;&lt;div class=&quot;code-block-tools&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;code-btn
      code-copy&quot; hidden&gt;Copy&lt;/button&gt;&lt;/div&gt;&lt;/div&gt;&lt;pre class=&quot;shiki&quot; tabindex=&quot;0&quot; role=&quot;region&quot; aria-label=&quot;Code sample: JavaScript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-keyword&quot;&gt;export&lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt; default&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; stories: [&lt;/span&gt;&lt;/span&gt;&lt;span
      class=&quot;line&quot;&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; title: &lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;&#39;Buttons/Close&#39;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; },&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; ],&lt;/span&gt;&lt;/span&gt;&lt;span
      class=&quot;line&quot;&gt;&lt;span&gt;};&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;p&gt;When determining how to group or nest your components, consider what distinctions would be most useful to your users. Does it benefit them to be able to dig into certain categories in one place instead of having to navigate through the entire library to find what they need? A few possible categories to consider are:&lt;/p&gt; &lt;ul&gt;
      &lt;li&gt;&lt;strong&gt;Buttons&lt;/strong&gt;&lt;/li&gt; &lt;li&gt;&lt;strong&gt;Forms&lt;/strong&gt;&lt;/li&gt; &lt;li&gt;&lt;strong&gt;Graphs&lt;/strong&gt;&lt;/li&gt; &lt;li&gt;&lt;strong&gt;Layouts&lt;/strong&gt;&lt;/li&gt; &lt;li&gt;&lt;strong&gt;Navigation&lt;/strong&gt;&lt;/li&gt; &lt;/ul&gt; &lt;h4&gt;Component&lt;/h4&gt; &lt;p&gt;Specify the root component name here. This helps Storybook&#39;s documentation features understand what you&#39;re showcasing. If you&#39;re using a framework, this
      will typically be the component name or the imported component object. See the &lt;a href=&quot;https://storybook.js.org/docs/api/csf&quot;&gt;Storybook documentation&lt;/a&gt; for more information.&lt;/p&gt; &lt;h4&gt;Controls&lt;/h4&gt; &lt;p&gt;This is where you define the controls that users can interact with in the Storybook UI. Here&#39;s a pro tip: organize them into logical categories so people can quickly find what they&#39;re looking for:&lt;/p&gt; &lt;ul&gt;
      &lt;li&gt;&lt;strong&gt;State&lt;/strong&gt;—interactive states like focused, open, or disabled&lt;/li&gt; &lt;li&gt;&lt;strong&gt;Variant&lt;/strong&gt;—design variations like visual appearance or size&lt;/li&gt; &lt;li&gt;&lt;strong&gt;Content&lt;/strong&gt;—text, images, or nested components&lt;/li&gt; &lt;li&gt;&lt;strong&gt;Advanced&lt;/strong&gt;—edge cases or specialized configurations&lt;/li&gt; &lt;/ul&gt; &lt;figure class=&quot;code-block&quot; data-lang=&quot;js&quot;
      data-edge=&quot;yellow&quot; data-line-numbers&gt;&lt;div class=&quot;code-block-head&quot;&gt;&lt;figcaption&gt;&lt;span class=&quot;code-block-lang&quot;&gt;JavaScript&lt;/span&gt;&lt;/figcaption&gt;&lt;div class=&quot;code-block-tools&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;code-btn code-copy&quot; hidden&gt;Copy&lt;/button&gt;&lt;/div&gt;&lt;/div&gt;&lt;pre class=&quot;shiki&quot; tabindex=&quot;0&quot; role=&quot;region&quot; aria-label=&quot;Code sample:
      JavaScript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;argTypes: {&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; size: {&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; control: &lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;&quot;select&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; options: [&lt;/span&gt;&lt;span
      class=&quot;tk-string&quot;&gt;&quot;small&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;&quot;medium&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;&quot;large&quot;&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; description: &lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;&quot;The size of the button&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;span
      class=&quot;line&quot;&gt;&lt;span&gt; table: { category: &lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;&quot;Variant&quot;&lt;/span&gt;&lt;span&gt; }&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; },&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; isDisabled: {&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; control: &lt;/span&gt;&lt;span
      class=&quot;tk-string&quot;&gt;&quot;boolean&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; description: &lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;&quot;Whether the button is disabled&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; table: { category: &lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;&quot;State&quot;&lt;/span&gt;&lt;span&gt;
      }&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; }&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;p&gt;Document and restrict these categories to ensure consistency across your component library. If you have a lot of controls, you can also consider creating a shared controls file to import and reuse across your components. You can store these in a &lt;code&gt;controls&lt;/code&gt;
      folder inside your Storybook folder (i.e., &lt;code&gt;.storybook/controls/states.js&lt;/code&gt;, &lt;code&gt;.storybook/controls/variants.js&lt;/code&gt;, &lt;code&gt;.storybook/controls/content.js&lt;/code&gt;, &lt;code&gt;.storybook/controls/advanced.js&lt;/code&gt;). This is a great way to keep your controls organized and easy to find.&lt;/p&gt; &lt;blockquote&gt; &lt;h4&gt;Bonus tip&lt;/h4&gt; If you identify your Storybook folder as a workspace in your package manager, you can import the
      controls into your story files using the workspace alias. For example, if you have a `controls` folder in your Storybook folder, you can import the controls like this: &lt;figure class=&quot;code-block&quot; data-lang=&quot;js&quot; data-edge=&quot;yellow&quot;&gt;&lt;div class=&quot;code-block-head&quot;&gt;&lt;figcaption&gt;&lt;span class=&quot;code-block-lang&quot;&gt;JavaScript&lt;/span&gt;&lt;/figcaption&gt;&lt;div class=&quot;code-block-tools&quot;&gt;&lt;button type=&quot;button&quot;
      class=&quot;code-btn code-copy&quot; hidden&gt;Copy&lt;/button&gt;&lt;/div&gt;&lt;/div&gt;&lt;pre class=&quot;shiki&quot; tabindex=&quot;0&quot; role=&quot;region&quot; aria-label=&quot;Code sample: JavaScript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-keyword&quot;&gt;import&lt;/span&gt;&lt;span&gt; { states, variants, content, advanced } &lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;
      &quot;local-storybook/controls&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;p&gt;Be sure to export all your controls via an &lt;code&gt;index.js&lt;/code&gt; file in your &lt;code&gt;controls&lt;/code&gt; folder in order to leverage this syntax most effectively.&lt;/p&gt; &lt;/blockquote&gt; &lt;h4&gt;Default values&lt;/h4&gt; &lt;p&gt;Always provide sensible defaults for all your controls. This way, your primary story renders in a useful state
      right out of the gate:&lt;/p&gt; &lt;figure class=&quot;code-block&quot; data-lang=&quot;js&quot; data-edge=&quot;yellow&quot;&gt;&lt;div class=&quot;code-block-head&quot;&gt;&lt;figcaption&gt;&lt;span class=&quot;code-block-lang&quot;&gt;JavaScript&lt;/span&gt;&lt;/figcaption&gt;&lt;div class=&quot;code-block-tools&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;code-btn code-copy&quot; hidden&gt;Copy&lt;/button&gt;&lt;/div&gt;&lt;/div&gt;&lt;pre class=&quot;shiki&quot; tabindex=&quot;0&quot;
      role=&quot;region&quot; aria-label=&quot;Code sample: JavaScript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;args: {&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; size: &lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;&quot;medium&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; isDisabled: &lt;/span&gt;&lt;span
      class=&quot;tk-number&quot;&gt;false&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; label: &lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;&quot;Click me&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;p&gt;Not all controls have to have a default state, though. When given the choice between adding a meaningless &lt;code&gt;normal&lt;/code&gt; or
      &lt;code&gt;default&lt;/code&gt; value into the options that doesn&#39;t actually trigger a state, I opt for setting the default to &lt;code&gt;undefined&lt;/code&gt;.&lt;/p&gt; &lt;figure class=&quot;code-block&quot; data-lang=&quot;js&quot; data-edge=&quot;yellow&quot;&gt;&lt;div class=&quot;code-block-head&quot;&gt;&lt;figcaption&gt;&lt;span class=&quot;code-block-lang&quot;&gt;JavaScript&lt;/span&gt;&lt;/figcaption&gt;&lt;div class=&quot;code-block-tools&quot;&gt;&lt;button type=&quot;button&quot;
      class=&quot;code-btn code-copy&quot; hidden&gt;Copy&lt;/button&gt;&lt;/div&gt;&lt;/div&gt;&lt;pre class=&quot;shiki&quot; tabindex=&quot;0&quot; role=&quot;region&quot; aria-label=&quot;Code sample: JavaScript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-comment&quot;&gt;// Good&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;[&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;&quot;outline&quot;&lt;/span&gt;&lt;span&gt;,
      &lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;&quot;subtle&quot;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-comment&quot;&gt;// Bad&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;[&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;&quot;default&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span
      class=&quot;tk-string&quot;&gt;&quot;outline&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;&quot;subtle&quot;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;h4&gt;Parameters&lt;/h4&gt; &lt;p&gt;Parameters let you configure behavior and attach metadata. Think of them as the extra settings that make your stories more useful:&lt;/p&gt; &lt;figure class=&quot;code-block&quot; data-lang=&quot;js&quot;
      data-edge=&quot;yellow&quot; data-line-numbers&gt;&lt;div class=&quot;code-block-head&quot;&gt;&lt;figcaption&gt;&lt;span class=&quot;code-block-lang&quot;&gt;JavaScript&lt;/span&gt;&lt;/figcaption&gt;&lt;div class=&quot;code-block-tools&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;code-btn code-copy&quot; hidden&gt;Copy&lt;/button&gt;&lt;/div&gt;&lt;/div&gt;&lt;pre class=&quot;shiki&quot; tabindex=&quot;0&quot; role=&quot;region&quot; aria-label=&quot;Code sample:
      JavaScript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;parameters: {&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; design: {&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; type: &lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;&quot;figma&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; url: &lt;/span&gt;&lt;span
      class=&quot;tk-string&quot;&gt;&quot;https://www.figma.com/...&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; },&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; actions: {&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; handles: [&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;&quot;click&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;&quot;focus&quot;&lt;/span&gt;&lt;span&gt;,
      &lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;&quot;blur&quot;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; },&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; status: {&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; type: &lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;&quot;stable&quot;&lt;/span&gt;&lt;span class=&quot;tk-comment&quot;&gt; // or &quot;experimental&quot;,
      &quot;deprecated&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; }&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;h3&gt;Creating individual stories&lt;/h3&gt; &lt;p&gt;Each story represents a specific state or variant of your component. Use &lt;code&gt;.bind({})&lt;/code&gt; to create stories that inherit from your default configuration, then override specific args to
      demonstrate different use cases:&lt;/p&gt; &lt;figure class=&quot;code-block&quot; data-lang=&quot;js&quot; data-edge=&quot;yellow&quot; data-line-numbers&gt;&lt;div class=&quot;code-block-head&quot;&gt;&lt;figcaption&gt;&lt;span class=&quot;code-block-lang&quot;&gt;JavaScript&lt;/span&gt;&lt;/figcaption&gt;&lt;div class=&quot;code-block-tools&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;code-btn code-copy&quot; hidden&gt;Copy&lt;/button&gt;&lt;/div&gt;&lt;/div&gt;&lt;pre
      class=&quot;shiki&quot; tabindex=&quot;0&quot; role=&quot;region&quot; aria-label=&quot;Code sample: JavaScript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-keyword&quot;&gt;export&lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt; const&lt;/span&gt;&lt;span&gt; Default &lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt;=&lt;/span&gt;&lt;span&gt; Template.&lt;/span&gt;&lt;span
      class=&quot;tk-function&quot;&gt;bind&lt;/span&gt;&lt;span&gt;({});&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Default.args &lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt;=&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-comment&quot;&gt; // Override specific args for this story&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;};&lt;/span&gt;&lt;/span&gt;&lt;span
      class=&quot;line&quot;&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-keyword&quot;&gt;export&lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt; const&lt;/span&gt;&lt;span&gt; Disabled &lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt;=&lt;/span&gt;&lt;span&gt; Template.&lt;/span&gt;&lt;span class=&quot;tk-function&quot;&gt;bind&lt;/span&gt;&lt;span&gt;({});&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Disabled.args &lt;/span&gt;&lt;span
      class=&quot;tk-keyword&quot;&gt;=&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; isDisabled: &lt;/span&gt;&lt;span class=&quot;tk-number&quot;&gt;true&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;};&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-keyword&quot;&gt;export&lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt;
      const&lt;/span&gt;&lt;span&gt; WithIcon &lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt;=&lt;/span&gt;&lt;span&gt; Template.&lt;/span&gt;&lt;span class=&quot;tk-function&quot;&gt;bind&lt;/span&gt;&lt;span&gt;({});&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;WithIcon.args &lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt;=&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; icon: &lt;/span&gt;&lt;span
      class=&quot;tk-string&quot;&gt;&quot;settings&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; label: &lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;&quot;Settings&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;};&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;h3&gt;Naming conventions&lt;/h3&gt; &lt;p&gt;Good story names are concise and descriptive. Here are some patterns that work
      well:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;&lt;strong&gt;Default&lt;/strong&gt;—your primary interactive example&lt;/li&gt; &lt;li&gt;&lt;strong&gt;With Icon&lt;/strong&gt;—demonstrating optional features&lt;/li&gt; &lt;li&gt;&lt;strong&gt;Loading State&lt;/strong&gt;—showing specific states&lt;/li&gt; &lt;li&gt;&lt;strong&gt;Error Variant&lt;/strong&gt;—highlighting edge cases&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;And here&#39;s what not to do: don&#39;t repeat the component name in every story title. It&#39;s
      redundant and clutters your navigation.&lt;/p&gt; &lt;h3&gt;Organizing with tags&lt;/h3&gt; &lt;p&gt;Tags are your friends when it comes to controlling where stories appear and how they behave:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;Use &lt;strong&gt;&lt;code&gt;!dev&lt;/code&gt;&lt;/strong&gt; to hide stories from the sidebar—super useful for documentation-only stories&lt;/li&gt; &lt;li&gt;Use &lt;strong&gt;&lt;code&gt;!autodocs&lt;/code&gt;&lt;/strong&gt; to exclude stories from auto-generated docs
      pages&lt;/li&gt; &lt;li&gt;Combine both tags for visual regression testing grids that shouldn&#39;t clutter your UI&lt;/li&gt; &lt;/ul&gt; &lt;hr /&gt; &lt;h2 id=&quot;template-files&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;#template-files&quot;&gt;#&lt;/a&gt; Template files&lt;/h2&gt; &lt;p&gt;Your template file (&lt;code&gt;template.js&lt;/code&gt;) is where the actual rendering happens. Think of it as the engine that powers your stories.&lt;/p&gt;
      &lt;h3&gt;Template structure&lt;/h3&gt; &lt;p&gt;Export a primary &lt;code&gt;Template(args, context)&lt;/code&gt; function that returns your rendered component. If you&#39;re using a library like Lit, this would be an &lt;code&gt;html&lt;/code&gt; TemplateResult.&lt;/p&gt; &lt;p&gt;Here&#39;s the key: import and render nested component templates instead of duplicating their markup. We talked about this earlier, but it bears repeating—composition over duplication.&lt;/p&gt; &lt;h3&gt;Standard
      arguments&lt;/h3&gt; &lt;p&gt;Your templates should accept some common arguments to stay flexible:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;&lt;code&gt;rootClass&lt;/code&gt;—the base CSS class for your component&lt;/li&gt; &lt;li&gt;&lt;code&gt;id&lt;/code&gt; and &lt;code&gt;testId&lt;/code&gt;—for identification and testing&lt;/li&gt; &lt;li&gt;&lt;code&gt;customClasses&lt;/code&gt;—an array of additional classes&lt;/li&gt; &lt;li&gt;&lt;code&gt;customStyles&lt;/code&gt;—an object of custom CSS
      properties&lt;/li&gt; &lt;/ul&gt; &lt;h3&gt;Using directives for dynamic behavior&lt;/h3&gt; &lt;p&gt;Most rendering libraries give you directives or utilities to handle dynamic rendering. Here&#39;s what you&#39;ll typically need:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;&lt;code&gt;classMap&lt;/code&gt;—for conditional classes based on your &lt;code&gt;rootClass&lt;/code&gt;&lt;/li&gt; &lt;li&gt;&lt;code&gt;styleMap&lt;/code&gt;—for inline style objects&lt;/li&gt;
      &lt;li&gt;&lt;code&gt;ifDefined&lt;/code&gt;—for optional attributes that should only render when they exist&lt;/li&gt; &lt;li&gt;&lt;code&gt;when&lt;/code&gt;—for conditional regions of your template&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;For interactive examples, use &lt;code&gt;context.updateArgs&lt;/code&gt; to toggle arguments (like &lt;code&gt;isOpen&lt;/code&gt; for a modal). And when you need to pass modifiable CSS custom properties, do it through &lt;code&gt;customStyles&lt;/code&gt;—it&#39;s
      cleaner than hardcoding values.&lt;/p&gt; &lt;h3&gt;Composition patterns&lt;/h3&gt; &lt;p&gt;When you&#39;re composing complex components, use parent wrappers to position the pieces and pass arguments down to child templates. Here are some real-world examples of how this might look:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;An action bar might compose a popover, close button, and action group&lt;/li&gt; &lt;li&gt;An action menu might compose a popover with an action button trigger and a menu for
      content&lt;/li&gt; &lt;li&gt;A coach mark might compose a popover, coach indicator, and internal layout&lt;/li&gt; &lt;/ul&gt; &lt;hr /&gt; &lt;h2 id=&quot;visual-testing&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;#visual-testing&quot;&gt;#&lt;/a&gt; Visual testing&lt;/h2&gt; &lt;p&gt;If you&#39;re using a visual regression tool like 💕 &lt;a href=&quot;https://www.chromatic.com&quot;&gt;Chromatic&lt;/a&gt;, you&#39;ll want to set up testing grids. These live in
      a separate &lt;code&gt;component.test.js&lt;/code&gt; file.&lt;/p&gt; &lt;h3&gt;Test grids&lt;/h3&gt; &lt;p&gt;Most visual regression setups use some kind of variants helper to generate grids of different states and variants. You&#39;ll typically provide your &lt;code&gt;Template&lt;/code&gt; along with arrays of test data and state data.&lt;/p&gt; &lt;p&gt;Here&#39;s the strategy: group many states and variants into a single snapshot grid to optimize your test runs. Nobody wants to wait forever for
      visual tests to complete.&lt;/p&gt; &lt;p&gt;In your main &lt;code&gt;*.stories.js&lt;/code&gt; file, export the grid and mark it with &lt;code&gt;!autodocs&lt;/code&gt; and usually &lt;code&gt;!dev&lt;/code&gt; so it doesn&#39;t clutter your documentation or sidebar.&lt;/p&gt; &lt;h3&gt;Control patterns&lt;/h3&gt; &lt;p&gt;Keep your controls organized and consistent across your component library. If you have common state controls (like &lt;code&gt;isOpen&lt;/code&gt;,
      &lt;code&gt;isSelected&lt;/code&gt;, &lt;code&gt;isHovered&lt;/code&gt;, or &lt;code&gt;isFocused&lt;/code&gt;), put them in a shared location and import them.&lt;/p&gt; &lt;p&gt;Use the same categories we talked about earlier: State, Variant, Content, and Advanced. And here&#39;s a neat trick—use &lt;code&gt;if&lt;/code&gt; conditions to tie related controls together. For example, only show &lt;code&gt;imageIsFixedHeight&lt;/code&gt; when &lt;code&gt;hasImage&lt;/code&gt; is true.&lt;/p&gt;
      &lt;p&gt;Don&#39;t be afraid to reuse controls from related components either. If another component already has the perfect icon dropdown setup, import it instead of rebuilding it from scratch.&lt;/p&gt; &lt;hr /&gt; &lt;h2 id=&quot;interactions-and-accessibility&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;#interactions-and-accessibility&quot;&gt;#&lt;/a&gt; Interactions and accessibility&lt;/h2&gt; &lt;h3&gt;Actions and events&lt;/h3&gt; &lt;p&gt;When you&#39;re
      working with nested components, reuse their action handlers by spreading them into your own &lt;code&gt;parameters.actions.handles&lt;/code&gt;. For interactive triggers like popover toggles, wire up &lt;code&gt;onclick&lt;/code&gt; to &lt;code&gt;context.updateArgs&lt;/code&gt; or leverage the behavior from child templates.&lt;/p&gt; &lt;h3&gt;Accessibility&lt;/h3&gt; &lt;p&gt;Always provide ARIA attributes through your arguments—things like &lt;code&gt;aria-haspopup&lt;/code&gt;,
      &lt;code&gt;aria-controls&lt;/code&gt;, &lt;code&gt;aria-expanded&lt;/code&gt;, and &lt;code&gt;aria-pressed&lt;/code&gt;. Use the &lt;code&gt;ifDefined&lt;/code&gt; directive so they only render when needed.&lt;/p&gt; &lt;p&gt;Keep focus, hover, and active states controllable in your test grids. This makes it way easier to verify your component looks right in all states.&lt;/p&gt; &lt;p&gt;When you&#39;re demonstrating popovers or menus, make sure trigger and popup IDs are consistent and exposed as
      arguments. Your QA team (and screen reader users) will appreciate it.&lt;/p&gt; &lt;hr /&gt; &lt;h2 id=&quot;documentation-and-metadata&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;#documentation-and-metadata&quot;&gt;#&lt;/a&gt; Documentation and metadata&lt;/h2&gt; &lt;h3&gt;Design links&lt;/h3&gt; &lt;p&gt;Link your stories to design files (like Figma) through &lt;code&gt;parameters.design&lt;/code&gt;. This creates a direct connection between your
      implementation and the design source of truth.&lt;/p&gt; &lt;h3&gt;Package information&lt;/h3&gt; &lt;p&gt;Include your &lt;code&gt;package.json&lt;/code&gt; and any metadata files in your parameters. This powers documentation blocks and helps users understand what version they&#39;re looking at.&lt;/p&gt; &lt;h3&gt;Component status&lt;/h3&gt; &lt;p&gt;Use &lt;code&gt;parameters.status.type&lt;/code&gt; to communicate where your component is in its lifecycle—is it stable, experimental, or deprecated?
      Add tags like &lt;code&gt;&amp;quot;migrated&amp;quot;&lt;/code&gt; to track major updates or migrations.&lt;/p&gt; &lt;hr /&gt; &lt;h2 id=&quot;dos-and-don&#39;ts&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;#dos-and-don&#39;ts&quot;&gt;#&lt;/a&gt; Dos and don&#39;ts&lt;/h2&gt; &lt;p&gt;Let&#39;s wrap up with some quick guidelines to keep you on the right track:&lt;/p&gt; &lt;h3&gt;Do&lt;/h3&gt; &lt;ul&gt; &lt;li&gt;&lt;strong&gt;Import child component
      templates&lt;/strong&gt; instead of copying their markup&lt;/li&gt; &lt;li&gt;&lt;strong&gt;Keep your &lt;code&gt;template.js&lt;/code&gt; focused&lt;/strong&gt; on the component itself—accept &lt;code&gt;customClasses&lt;/code&gt; and &lt;code&gt;customStyles&lt;/code&gt; for composition&lt;/li&gt; &lt;li&gt;&lt;strong&gt;Categorize your controls&lt;/strong&gt; and set sensible defaults in &lt;code&gt;args&lt;/code&gt;&lt;/li&gt; &lt;li&gt;&lt;strong&gt;Hide VRT-only stories&lt;/strong&gt; from your
      sidebar and docs using tags&lt;/li&gt; &lt;li&gt;&lt;strong&gt;Reuse shared controls&lt;/strong&gt; from a common location instead of duplicating them&lt;/li&gt; &lt;/ul&gt; &lt;h3&gt;Don&#39;t&lt;/h3&gt; &lt;ul&gt; &lt;li&gt;&lt;strong&gt;Don&#39;t hardcode IDs&lt;/strong&gt;—use a helper function to generate random IDs&lt;/li&gt; &lt;li&gt;&lt;strong&gt;Don&#39;t duplicate markup&lt;/strong&gt; from nested components—import and compose instead&lt;/li&gt; &lt;li&gt;&lt;strong&gt;Don&#39;t skip
      accessibility&lt;/strong&gt;—always include proper ARIA attributes&lt;/li&gt; &lt;/ul&gt; &lt;hr /&gt; &lt;h2 id=&quot;code-examples&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;#code-examples&quot;&gt;#&lt;/a&gt; Code examples&lt;/h2&gt; &lt;p&gt;Here are some practical code snippets to get you started.&lt;/p&gt; &lt;h3&gt;Story default export&lt;/h3&gt; &lt;p&gt;Here&#39;s what a typical story file default export looks like:&lt;/p&gt; &lt;figure
      class=&quot;code-block&quot; data-lang=&quot;js&quot; data-edge=&quot;yellow&quot; data-line-numbers&gt;&lt;div class=&quot;code-block-head&quot;&gt;&lt;figcaption&gt;&lt;span class=&quot;code-block-lang&quot;&gt;JavaScript&lt;/span&gt;&lt;/figcaption&gt;&lt;div class=&quot;code-block-tools&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;code-btn code-copy&quot; hidden&gt;Copy&lt;/button&gt;&lt;/div&gt;&lt;/div&gt;&lt;pre class=&quot;shiki&quot; tabindex=&quot;0&quot; role=&quot;region&quot;
      aria-label=&quot;Code sample: JavaScript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-keyword&quot;&gt;import&lt;/span&gt;&lt;span&gt; packageJson &lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt; &quot;../package.json&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span
      class=&quot;tk-keyword&quot;&gt;export&lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt; default&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; title: &lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;&quot;Components/Button&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; component: &lt;/span&gt;&lt;span
      class=&quot;tk-string&quot;&gt;&quot;Button&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; argTypes: {&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; size: {&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; control: &lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;&quot;select&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; options:
      [&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;&quot;small&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;&quot;medium&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;&quot;large&quot;&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; table: { category: &lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;&quot;Variant&quot;&lt;/span&gt;&lt;span&gt;
      }&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; },&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; isDisabled: {&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; control: &lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;&quot;boolean&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; table: { category: &lt;/span&gt;&lt;span
      class=&quot;tk-string&quot;&gt;&quot;State&quot;&lt;/span&gt;&lt;span&gt; }&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; }&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; },&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; args: {&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; size: &lt;/span&gt;&lt;span
      class=&quot;tk-string&quot;&gt;&quot;medium&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; isDisabled: &lt;/span&gt;&lt;span class=&quot;tk-number&quot;&gt;false&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; label: &lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;&quot;Click me&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;
      },&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; parameters: {&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; design: {&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; type: &lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;&quot;figma&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; url: &lt;/span&gt;&lt;span
      class=&quot;tk-string&quot;&gt;&quot;https://www.figma.com/...&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; },&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; packageJson,&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; status: { type: &lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;&quot;stable&quot;&lt;/span&gt;&lt;span&gt; }&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;
      },&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; tags: [&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;&quot;autodocs&quot;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;};&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;button type=&quot;button&quot; class=&quot;code-expand&quot; aria-expanded=&quot;false&quot; hidden&gt;Show all 31 lines&lt;/button&gt;&lt;/figure&gt;&lt;h3&gt;Template composition&lt;/h3&gt;
      &lt;p&gt;When you&#39;re composing complex components, here&#39;s how you might structure it:&lt;/p&gt; &lt;figure class=&quot;code-block&quot; data-lang=&quot;js&quot; data-edge=&quot;yellow&quot; data-line-numbers&gt;&lt;div class=&quot;code-block-head&quot;&gt;&lt;figcaption&gt;&lt;span class=&quot;code-block-lang&quot;&gt;JavaScript&lt;/span&gt;&lt;/figcaption&gt;&lt;div class=&quot;code-block-tools&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;code-btn code-copy&quot;
      hidden&gt;Copy&lt;/button&gt;&lt;/div&gt;&lt;/div&gt;&lt;pre class=&quot;shiki&quot; tabindex=&quot;0&quot; role=&quot;region&quot; aria-label=&quot;Code sample: JavaScript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-keyword&quot;&gt;import&lt;/span&gt;&lt;span&gt; { Template &lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt;as&lt;/span&gt;&lt;span&gt; Popover } &lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt;from&lt;/span&gt;&lt;span
      class=&quot;tk-string&quot;&gt; &quot;@my-design-system/popover/stories/template.js&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-keyword&quot;&gt;import&lt;/span&gt;&lt;span&gt; { Template &lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt;as&lt;/span&gt;&lt;span&gt; Button } &lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;
      &quot;@my-design-system/button/stories/template.js&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-keyword&quot;&gt;export&lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt; const&lt;/span&gt;&lt;span class=&quot;tk-function&quot;&gt; Template&lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt; =&lt;/span&gt;&lt;span&gt; (args, context) &lt;/span&gt;&lt;span
      class=&quot;tk-keyword&quot;&gt;=&gt;&lt;/span&gt;&lt;span class=&quot;tk-function&quot;&gt; Popover&lt;/span&gt;&lt;span&gt;({&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-keyword&quot;&gt; ...&lt;/span&gt;&lt;span&gt;args,&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; isOpen: &lt;/span&gt;&lt;span class=&quot;tk-number&quot;&gt;true&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span
      class=&quot;tk-function&quot;&gt; trigger&lt;/span&gt;&lt;span&gt;: (passthroughs, ctx) &lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt;=&gt;&lt;/span&gt;&lt;span class=&quot;tk-function&quot;&gt; Button&lt;/span&gt;&lt;span&gt;({&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; label: &lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;&quot;Open Menu&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span
      class=&quot;tk-keyword&quot;&gt; ...&lt;/span&gt;&lt;span&gt;passthroughs&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; }, ctx),&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; content: [&lt;/span&gt;&lt;span class=&quot;tk-comment&quot;&gt;/* child content templates */&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;},
      context);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;h3&gt;Test grid setup&lt;/h3&gt; &lt;p&gt;If you&#39;re setting up visual regression tests, your test file might look like this:&lt;/p&gt; &lt;figure class=&quot;code-block&quot; data-lang=&quot;js&quot; data-edge=&quot;yellow&quot; data-line-numbers&gt;&lt;div class=&quot;code-block-head&quot;&gt;&lt;figcaption&gt;&lt;span class=&quot;code-block-lang&quot;&gt;JavaScript&lt;/span&gt;&lt;/figcaption&gt;&lt;div
      class=&quot;code-block-tools&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;code-btn code-copy&quot; hidden&gt;Copy&lt;/button&gt;&lt;/div&gt;&lt;/div&gt;&lt;pre class=&quot;shiki&quot; tabindex=&quot;0&quot; role=&quot;region&quot; aria-label=&quot;Code sample: JavaScript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-keyword&quot;&gt;import&lt;/span&gt;&lt;span&gt; { Template } &lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt;from&lt;/span&gt;&lt;span
      class=&quot;tk-string&quot;&gt; &quot;./template.js&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-keyword&quot;&gt;export&lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt; const&lt;/span&gt;&lt;span&gt; TestGrid &lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt;=&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; render:
      Template,&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; parameters: {&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; chromatic: { disableSnapshot: &lt;/span&gt;&lt;span class=&quot;tk-number&quot;&gt;false&lt;/span&gt;&lt;span&gt; }&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; }&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;};&lt;/span&gt;&lt;/span&gt;&lt;span
      class=&quot;line&quot;&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;tk-keyword&quot;&gt;export&lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt; const&lt;/span&gt;&lt;span&gt; Default &lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt;=&lt;/span&gt;&lt;span&gt; TestGrid.&lt;/span&gt;&lt;span class=&quot;tk-function&quot;&gt;bind&lt;/span&gt;&lt;span&gt;({});&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Default.tags &lt;/span&gt;&lt;span
      class=&quot;tk-keyword&quot;&gt;=&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;&quot;!autodocs&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span class=&quot;tk-string&quot;&gt;&quot;!dev&quot;&lt;/span&gt;&lt;span&gt;];&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Default.args &lt;/span&gt;&lt;span class=&quot;tk-keyword&quot;&gt;=&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span
      class=&quot;tk-comment&quot;&gt; // Test-specific args&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;};&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;hr /&gt; &lt;h2 id=&quot;wrapping-up&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;#wrapping-up&quot;&gt;#&lt;/a&gt; Wrapping up&lt;/h2&gt; &lt;p&gt;Building a Storybook that scales isn&#39;t about following every best practice to the letter—it&#39;s about making
      intentional choices that serve your team and your users. Throughout this guide, we&#39;ve covered the foundational decisions that&#39;ll keep your component library maintainable: organizing your files so they&#39;re easy to navigate, composing components instead of duplicating markup, structuring your story files with clear defaults and well-organized controls, and never forgetting about accessibility.&lt;/p&gt; &lt;p&gt;The most important takeaway? &lt;strong&gt;Consistency wins.&lt;/strong&gt; Pick
      the patterns that make sense for your project—whether that&#39;s keeping all stories in one file or splitting them up, using Lit for vanilla projects or sticking with your framework&#39;s conventions, setting up visual regression tests or relying on manual QA. Whatever you choose, document it, share it with your team, and stick with it. A consistent Storybook with clear conventions will always be more valuable than a perfectly optimized one that nobody can navigate.&lt;/p&gt; &lt;p&gt;Six months from
      now, when you&#39;re diving back into a component you haven&#39;t touched in ages, you&#39;ll be grateful you put in the effort upfront. Your teammates will thank you. Your designers will thank you. And most importantly, you&#39;ll avoid that tangled mess of technical debt we talked about at the start. Let&#39;s build something great!&lt;/p&gt;
    </content>
    <summary>Storybook is a powerful and flexible tool but with great power comes great responsibility...to architect a scalable and intuitive approach. In this post, I dive deep into techniques for building a Storybook your developers and users will love.</summary>
    <category term="blog" />
    <category term="storybook" />
    <category term="tooling" />
  </entry>
  <entry>
    <title>AI as a reflection of our values</title>
    <link href="https://castastrophe.blog/blog/ai-as-a-reflection-of-our-values/" />
    <updated>2025-10-19T00:00:00.000Z</updated>
    <id>https://castastrophe.blog/blog/ai-as-a-reflection-of-our-values/</id>
    <content type="html"
      >&lt;p&gt;&lt;a href=&quot;https://openai.com/index/why-language-models-hallucinate/&quot;&gt;https://openai.com/index/why-language-models-hallucinate/&lt;/a&gt;&lt;/p&gt; &lt;p&gt;I first encountered this article and subsequent white paper a few days ago, and I&#39;ve been ruminating on it all weekend. The white paper employs the metaphor of students taking a multiple-choice test to illustrate the grading process for the efficacy of an AI model. I find this metaphor particularly evocative because
      multiple-choice testing is frequently criticized for being an inadequate way to determine a student&#39;s understanding of the subject.&lt;/p&gt; &lt;p&gt;The paper highlights that since making a guess increases the likelihood of getting the right answer compared to leaving it blank, AI logically opts to return an assumption or estimate instead of stating, &amp;quot;I don&#39;t know.&amp;quot; As a student of the United States public education system, I was well acquainted with this advice. After all,
      leaving an answer blank guarantees a zero, while a guess offers a one-in-four chance of earning a point.&lt;/p&gt; &lt;p&gt;&lt;strong&gt;Therefore, it&#39;s better to guess, isn&#39;t it?&lt;/strong&gt;&lt;/p&gt; &lt;h2 id=&quot;what&#39;s-the-harm-of-a-guess%3F&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;#what&#39;s-the-harm-of-a-guess%3F&quot;&gt;#&lt;/a&gt; What&#39;s the harm of a guess?&lt;/h2&gt; &lt;p&gt;The problem with a child guessing on the test is
      that the teacher loses an understanding of what the child really learned and where they need to reinforce or take a different approach for previous lessons. On top of this, some children are really good at taking standardized tests. Does that mean they know what it is we wanted them to learn from that lesson?&lt;/p&gt; &lt;p&gt;Map this back to the article on AI hallucinations, and we see that, yet again, AI is holding up a mirror to our flaws. We entered the work with assumptions and expectations,
      and the models returned answers that in fact reflect our own faulty definitions of reality and truth. It sought to bolster our egos because reinforcing pre-conceived notions is better received by the audience than telling someone their premise is invalid. What can we learn about training AI models from alternative educational models such as Constructivism, which aims to encourage problem-solving and critical thinking, connecting new data with previous knowledge?&lt;/p&gt; &lt;p&gt;How do we grade and
      assess the success of a model without falling back to assessment methods that we already know to be inconclusive for our children?&lt;/p&gt;
    </content>
    <summary>As we examine the flaws in our AI models, we begin to see parallels with the flaws already identified in our own society. What can we learn from these existing critiques and how does it apply to how we train models in the future?</summary>
    <category term="blog" />
    <category term="ai" />
  </entry>
  <entry>
    <title>From Figma to code: why design systems need continuous collaboration</title>
    <link href="https://castastrophe.blog/blog/from-figma-to-code/" />
    <updated>2025-10-03T00:00:00.000Z</updated>
    <id>https://castastrophe.blog/blog/from-figma-to-code/</id>
    <content type="html"
      >&lt;p&gt;The most high-performing design systems teams I&#39;ve been on are ones with designers and engineers working in parallel. This is not to say that it&#39;s easy to get into this workflow because so often design projects are iterating well beyond where engineering is able to implement. When we slow down though and talk through this work together, I think we find so much opportunity to ask each other meaningful questions and challenge our pre-held notions and expectations.&lt;/p&gt;
      &lt;p&gt;What does this look like in practice? Below I&#39;ll outline a few of the questions I generally have when engaging with component designers.&lt;/p&gt; &lt;h2 id=&quot;squish-ification&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;#squish-ification&quot;&gt;#&lt;/a&gt; Squish-ification&lt;/h2&gt; &lt;p&gt;The web is a notorious squishy platform. Screen sizes, color palettes, user preferences and needs all vary widely by browser and user. Some features
      aren&#39;t implemented in all browsers and others are implemented but behave in subtly different ways. Some users prefer large text and others want dark themes. There are a thousand ways that we engage with the web and a lot of angles to come at building widgets for those surfaces. So when I&#39;m approaching a new pattern or design, I start by asking myself a few questions:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;What are the min and max sizes or ratios where this thing looks good?&lt;/li&gt; &lt;li&gt;Should
      spacing or padding scale up or down with type? Or should the spacing be static and not influenced by increases or decreases in base font sizes?&lt;/li&gt; &lt;li&gt;What is the general ratio you&#39;d like this widget to take up on the screen?&lt;/li&gt; &lt;li&gt;How do I engage with this element if I don&#39;t use a mouse? This is not just an accessibility concern but it also impacts how elements are constructed in the DOM because visual order doesn&#39;t necessarily equal semantic order. Even if I
      reorder elements visually using layouts, my keyboard will continue to navigate the elements in the order they exist in the DOM (unless you update that order with tabindex as well).&lt;/li&gt; &lt;li&gt;Are there any contextual customizations to an element? Should this thing look or behave differently if it exists inside something else or if it only has a certain amount of space available?&lt;/li&gt; &lt;/ul&gt; &lt;h2 id=&quot;starting-the-conversation&quot; tabindex=&quot;-1&quot;&gt;&lt;a
      class=&quot;header-anchor&quot; href=&quot;#starting-the-conversation&quot;&gt;#&lt;/a&gt; Starting the conversation&lt;/h2&gt; &lt;p&gt;What these questions really get at is the heart of the design. When engineers start poking at a design, what they&#39;re wanting to do is really get inside your thought process and build something that fully maps to what you, as a designer, envisioned. It&#39;s so hard to communicate intent in static design documentation and as dynamic and squishy as the web has
      become, these back-and-forths are more valuable and important than ever.&lt;/p&gt;
    </content>
    <summary>High-performing design systems thrive on parallel collaboration between designers and engineers. While achieving this workflow can be challenging—especially when design iterations outpace implementation—slowing down for meaningful dialogue creates opportunities to challenge assumptions and ask critical questions.</summary>
    <category term="blog" />
    <category term="design" />
    <category term="responsive" />
  </entry>
</feed>
