The Speckled Cover Is the Costume: Reading the Composition Notebook Shirt

The first thing I notice is the speckle. A black-and-white field of dots, dense and irregular, filling a rectangle that stops short of the shirt’s edges. Down the left side runs a dark binding strip. Inside the rectangle, a white ruled label carries the word COMPOSITION in block capitals, and beneath that header sit an outlined ghost and an outlined pumpkin. The whole thing is a printed notebook cover, not a physical notebook. That distinction matters more than it sounds.

Most teacher Halloween shirts announce a role. They print a slogan, a pun, a list of reasons, a warning about caffeine. This one does something quieter and, I think, more interesting: it reproduces a surface. The speckled cover is the familiar marbled cardboard of a composition book, the kind stacked in supply closets and sold three for a dollar in August. The label is the ruled rectangle where a student writes their name and subject. Here the name field is occupied by a ghost and a pumpkin instead.

That substitution is the entire design argument. A composition notebook is a container for someone else’s writing. The cover is blank because the content belongs to the person who fills it. By placing Halloween figures in the label, the shirt turns the notebook into a costume label — the wearer is not the notebook’s owner but its contents. The ghost and pumpkin are what has been written on the cover this year. It is a small joke about identity and season, and it works because the visual grammar is so specific. Anyone who has held a composition book recognizes the proportions immediately: the dark tape binding, the white label with its ruled lines, the speckle that looks like static.

I keep thinking about the difference between a costume and a uniform. A group Halloween shirt for teachers often functions as a uniform — matching colors, matching text, a visible signal that this team belongs together. The composition notebook design can do that too, but it does it by handing each wearer the same blank surface. The speckle is identical. The label is identical. The ghost and pumpkin are identical. What varies is the person inside the shirt, which is exactly the point of a notebook cover. It is a frame, not a statement.

That is why the design reads differently from a generic school slogan. A slogan tells you what to think about the wearer. A reproduced object lets you read the wearer through the object. The composition book is a tool of record-keeping, of daily practice, of the unglamorous work of showing up and writing something down. Putting a ghost on the label does not mock that work. It acknowledges that the work has a season, and the season is silly.

If you are looking for the composition notebook shirt as a group costume piece, the design’s strength is that it does not require explanation. The binding strip, the speckle, the ruled label, the word COMPOSITION — these are visual facts, not claims. The shirt does not say “teacher” anywhere. It does not need to. The notebook cover is the teacher’s native object, and the ghost and pumpkin are the Halloween overlay. The joke is in the overlay, not in a caption.

What I appreciate most is the restraint. There is no additional text, no arrow, no “this is my costume” disclaimer. The design trusts the viewer to recognize the composition book and to understand that the label has been repurposed. That trust is rare in novelty shirts, which tend to over-explain. Here the speckle does the work. The eye reads the texture first, then the binding, then the label, then the ghost. The sequence is almost like reading a page: surface, margin, header, content. The shirt is a page you wear.

I would not claim this design improves anything or solves a problem. It is a printed garment. But as an object to read, it has a clear argument: a familiar surface, slightly altered, becomes a costume without becoming a slogan. The composition notebook is already a costume for the work of teaching — the cover you put on the thing you use every day. Adding a ghost and a pumpkin just makes the costume visible.

Editorial note: This publication and Quirky Cotton Collective share an owner.

React’s Activity Component vs. display: none: What Hiding a Tab Was Still Costing You in Effects and Layout

You switch tabs. The video you were watching keeps playing in the background. Or the draft in a <textarea> vanishes because you unmounted the panel. Both symptoms come from the same decision: how you hide a tab. And in React, that decision has a third option that changes the contract.

The usual choices are conditional mounting ({activeTab === 'video' && <Video />}) or keeping everything mounted and toggling display: none. The first destroys React state and DOM state. The second preserves both, but it also leaves every Effect and every DOM side effect running. React’s <Activity> boundary is designed to split that difference: it hides children with display: none, destroys their Effects, and keeps their React and DOM state for restoration.

This article is about choosing the hiding mechanism by the state you need to preserve and the side effects you need to stop. It is not a styling preference. It is a state-preservation contract, and getting it wrong shows up as background audio, leaked subscriptions, and drafts that disappear.

Stability note: React’s own references currently document <Activity> as experimental in some locations, exported as unstable_Activity and not available in a stable React version. The primary English react.dev reference does not state stable availability in the retrieved text. Before adopting Activity in production, confirm the export and stability status against the React version pinned in your package.json and the primary react.dev reference for that version. Do not assume the API surface is identical across 19.x minors without checking.

What each hiding strategy actually does

Before comparing, pin the behavior to the documentation. React’s <Activity> reference states that when an Activity boundary is hidden, React visually hides its children using the display: "none" CSS property and destroys their Effects, cleaning up active subscriptions. When the boundary becomes visible again, React reveals the children with their previous state restored and re-creates their Effects. While hidden, children still re-render in response to new props, at a lower priority than the rest of the content. (React docs: <Activity>)

That gives three distinct behaviors:

  • Conditional mount/unmount. React state is destroyed. DOM state is destroyed. Effects run cleanup and are removed. This is the default when you write {isVisible && <Panel />}.
  • display: none on a mounted subtree. React state survives. DOM state survives. Effects keep running because React never unmounted the component. DOM side effects persist because the DOM nodes still exist. MDN describes display: none as turning off display so the element has no effect on layout, with all descendants also having their display turned off. (MDN: display)
  • <Activity mode="hidden">. React state survives. DOM state survives. Effects are destroyed on hide and re-created on show. The DOM is hidden with display: none, but the Effect lifecycle behaves as if the subtree were unmounted.

The key sentence from the React docs is that Activity can be thought of as a mechanism for rendering “background activity”: rather than discarding content that is likely to become visible again, it maintains and restores the UI and internal state while ensuring hidden content has no unwanted side effects. That is the contract.

The video case: display: none keeps the Effect alive

Consider a tabbed interface with two video players. You want each tab to remember its playback position, so you render both players and toggle display: none on the inactive one. The React docs walk through exactly this failure: rendering both videos and hiding the inactive one with display: none causes both videos to play at the same time. (React Activity reference, troubleshooting section)

Why? The play() call lives in an Effect. Because the component is still mounted, the Effect is still active. display: none removes the element from layout, but it does not stop media playback. The browser keeps decoding audio for an element the user cannot see.

Activity solves this by not creating Effects when first rendered as hidden and destroying all Effects when switching from visible to hidden. The docs describe the intended behavior: the cleanup pauses the video, and switching back re-creates the Effect and resumes playback. The same reference notes that because a hidden component’s DOM is not destroyed, any side effects from that DOM will persist even after the component is hidden. That is the precise problem Activity is built to avoid.

If you have ever shipped a tabbed dashboard where a hidden chart kept polling, a hidden map kept firing tile requests, or a hidden video kept playing, this is the mechanism. The component was never unmounted, so its Effects never cleaned up.

The textarea case: unmounting loses DOM state

The opposite failure is conditional mounting. A contact tab contains a <textarea>. The user types a draft, switches to Home, and switches back. The draft is gone. The React docs use this exact example: because the Contact component is fully unmounted, the <textarea> element’s internal DOM state is lost. Wrapping the tabs in Activity boundaries preserves the DOM node and its internal state across tab switches. (React docs: Restoring the DOM of hidden components)

This is the case where display: none was doing real work for you. It kept the DOM alive. The problem is that it also kept every Effect alive. Activity is the targeted fix: it preserves the DOM state you want while destroying the Effects you almost certainly do not want running in the background.

What Activity does not do

Activity is not a drop-in replacement for every hidden tab, and the docs are explicit about the boundaries.

Hidden Activity content is not in the SSR response. The reference states that parts of the UI wrapped in <Activity mode="hidden"> are not included in the SSR response. If a tab’s content must be present in the initial server-rendered HTML, hidden Activity will not provide it. The docs suggest useDeferredValue as an alternative when you need to defer rendering while keeping content in the response. (React Activity reference, SSR caveat)

Pre-rendering only warms data that suspends. A hidden Activity rendered during the initial render still renders its children at lower priority and without mounting their Effects, which lets code or data load ahead of time. But the docs are precise: only data read from a source that activates a Suspense boundary, such as a Promise read with use, is fetched during pre-rendering. Activity does not detect data fetched inside an Effect. If your tab fetches in useEffect, the pre-render benefit does not apply. (React docs: Pre-rendering content)

A hidden Activity that renders only text produces no DOM output. The reference notes that a hidden Activity that just renders text will not render anything rather than rendering hidden text, because there is no corresponding DOM element to apply visibility changes to. If you rely on the hidden subtree existing in the DOM for measurement or third-party scripts, wrap it in an element.

Effects that assume mount-once will run again. Because Activity destroys Effects on hide and re-creates them on show, an Effect that was written as “run once on mount” will run again when the tab becomes visible. The docs recommend <StrictMode> to catch unexpected side effects from this behavior. This is not a bug; it is the contract. If your Effect is not resilient to setup/cleanup cycles, Activity will expose it.

What about layout and paint?

It is worth being precise here, because the topic invites a wrong conclusion. display: none already removes the element from layout. MDN states that display: none turns off display so the element has no effect on layout, and all descendants also have their display turned off. So the layout cost of a hidden tab is not the main problem with display: none in a React tab UI. The main problem is the Effect lifecycle and the DOM side effects that keep running.

Activity also hides with display: none, so it does not change the layout story. What it changes is that the hidden subtree’s Effects are destroyed, and updates to hidden children are deferred to lower priority. The React docs state that while hidden, children still re-render in response to new props, albeit at a lower priority than the rest of the content. That is a scheduling difference, not a layout difference.

If your actual problem is off-screen rendering cost for content that is not a tab, the CSS content-visibility property is the relevant tool. The web.dev article on content-visibility describes it as enabling the user agent to skip an element’s rendering work, including layout and painting, until it is needed, and notes that content-visibility: hidden hides an element while preserving its rendering state, unlike display: none which destroys rendering state. (web.dev: content-visibility) That is a browser rendering optimization, not a React Effect lifecycle mechanism. It does not stop your useEffect subscriptions. Do not treat it as a substitute for Activity in a React tab UI.

A decision table for tab UIs

The right question is not “which is faster?” It is “which state must survive, and which side effects must stop?”

Strategy React state DOM state Effects / subscriptions Use when
Conditional mount/unmount Destroyed Destroyed Cleaned up You want a fresh start every time, or the tab is expensive and rarely revisited.
display: none on mounted subtree Preserved Preserved Still running Only when you explicitly accept that Effects and DOM side effects continue while hidden.
<Activity mode="hidden"> Preserved Preserved Destroyed on hide, re-created on show You want drafts, scroll positions, and form state to survive, but you want polling, media, and subscriptions to stop.

Two caveats sit on top of that table. First, if the tab’s content must be in the initial SSR HTML, hidden Activity will not give it to you. Second, if the tab’s data is fetched in an Effect rather than through Suspense, Activity’s pre-rendering benefit does not apply.

How to audit an existing tab UI

You do not need a benchmark to find the candidates. You need to read the Effects inside every subtree that is currently hidden with display: none.

  1. List every display: none toggle in the tab container. For each hidden subtree, list its Effects.
  2. Flag Effects that assume visibility. Media playback, polling intervals, WebSocket subscriptions, IntersectionObserver and ResizeObserver registrations, analytics timers, and animation loops. These are the ones that keep running while the user cannot see the tab.
  3. Flag DOM side effects that persist. The React docs call out that because a hidden component’s DOM is not destroyed, side effects from that DOM persist. A playing <video> is the canonical example.
  4. Check whether the Effect is resilient to setup/cleanup cycles. Activity will destroy and re-create it. If the cleanup does not fully mirror the setup, the tab will misbehave on the second visit. The useEffect reference describes the contract: setup runs on commit, cleanup runs before the next setup when dependencies change, and cleanup runs after the component is removed from the DOM. (React docs: useEffect)
  5. Check SSR requirements before migrating. If the hidden tab’s markup must be in the server response, Activity is the wrong tool for that tab.

This audit is the migration plan. The tabs with visibility-assuming Effects are the ones where Activity changes behavior in a way users will notice, usually for the better.

How to verify the change

Two separate traces answer two separate questions.

React DevTools Profiler answers: did the Effect lifecycle change as intended? Record a session that switches to a tab and back. Confirm that the hidden tab’s Effects were cleaned up on hide and re-created on show. If a subscription is still active while the tab is hidden, the migration did not take effect, or the Effect is living outside the Activity boundary.

Chrome DevTools Performance panel answers: did the main thread work change? The Performance panel records runtime performance and represents main thread activity in a flame chart, with the x-axis as time and the y-axis as the call stack. (Chrome DevTools Performance reference) Record a trace that switches tabs and compare the main-thread activity before and after. If the hidden tab was polling or animating, the trace will show it. If Activity stopped it, the trace will show the gap.

Do not expect Activity to reduce layout cost for the hidden subtree. Both display: none and Activity remove the subtree from layout. The measurable difference is in Effect activity and in the priority of updates to hidden children, not in layout time for the hidden content itself.

Version pinning and status

React 19 was released as stable on December 5, 2024, per the React 19 release post. (React v19 release) The <Activity> reference on react.dev documents the component’s behavior as described above. React’s own references, including es.react.dev, currently document Activity as experimental in some locations, exported as unstable_Activity and unavailable in stable React. The primary English react.dev reference does not state stable availability in the retrieved text. Before adopting Activity in production, confirm the export and stability status against the React version pinned in your package.json and the primary react.dev reference for that version. Do not assume the API surface is identical across 19.x minors without checking.

This article makes no claim about Next.js App Router integration specifics for Activity, because the retrieved primary sources do not cover it. If you are on the App Router, verify server-rendering behavior against your framework version before migrating tabs whose content must appear in the initial HTML.

FAQ

Does display: none stop my useEffect from running?

No. display: none is a CSS change. The component stays mounted, so its Effects stay active. If you need Effects to stop while a tab is hidden, you need to unmount the subtree or wrap it in an Activity boundary.

Does Activity reduce layout work for the hidden tab?

Activity hides children with display: none, which already removes them from layout. The difference Activity makes is in the Effect lifecycle and in deferring updates to hidden children to lower priority. It is not a layout optimization for hidden content.

Can I use Activity for a tab whose content must be server-rendered?

Not if the content is inside <Activity mode="hidden"> during SSR. The React reference states that hidden Activity content is not included in the SSR response. The docs suggest useDeferredValue when you need to defer rendering while keeping content in the response.

Will Activity pre-fetch my tab’s data?

Only if the data is read through a Suspense boundary, such as a Promise read with use. The React docs state that Activity does not detect data fetched inside an Effect. If your tab fetches in useEffect, pre-rendering will not warm it.

Why does my video keep playing when I hide its tab?

Because the component is still mounted and its play() Effect is still active. display: none removes the element from layout but does not stop media playback. The React Activity reference documents this exact failure and shows Activity as the fix, because Activity destroys Effects on hide.

Is content-visibility: hidden a replacement for Activity?

No. content-visibility: hidden preserves browser rendering state for hidden content, but it does not change React’s Effect lifecycle. Your subscriptions and media Effects keep running. Use it for browser rendering optimization, not for React side-effect control.

The Period After ‘meh’ Is the Whole Design

Editorial note: This publication and Quirky Cotton Collective share an owner.

The native garment preview for the Meh Funny Sarcastic Saying Introvert Dry Humor shirt shows large bold white lowercase meh. lettering on a dark shirt. No animal illustration is present. That is the whole graphic: four letters and a period, set at a size that would normally be reserved for a band name or a warning label. The visual observation is narrow, and it is enough to argue about, because the design makes a specific typographic decision that most slogan shirts avoid.

The period is the load-bearing element. Without it, meh is a noise — a shrug rendered in text, the sound a person makes when asked to choose a restaurant. With the period, it becomes a sentence. A sentence has a speaker, and a speaker who bothers to punctuate a dismissal is making a small claim about finality. The word says I don’t care; the period says and I have finished caring in a grammatically complete way. That tension between content and form is the joke, and it is a visual joke rather than a verbal one. You can read it from across a room before you have parsed the letters.

Scale does the rest of the work. Large bold lowercase lettering on a dark ground is a loud treatment. Loud typography is the visual grammar of urgency: sale signs, safety placards, tour posters, the DO NOT in a warning. Applying that grammar to a word that means no strong opinion is a category mismatch, and category mismatches are the engine of dry humor. The shirt is not funny because of what it says. It is funny because of how seriously it says it. A small italic meh in the corner of the chest would be a different object entirely — more of a private aside, less of a public statement. The bold lowercase version is a public statement of disinterest, which is inherently a little absurd.

Lowercase matters too, and it is worth separating observation from interpretation. The preview shows lowercase lettering; that is the observation. My reading is that lowercase pulls against the loudness of the size. Capital letters would make MEH. read as a command or a brand. Lowercase keeps it conversational, almost muttered, even at billboard scale. The period then closes the sentence with a formality the word has not earned. So the design stacks three registers — shout, mutter, full stop — and lets them contradict each other. That is a more deliberate piece of graphic design than the retail title’s stack of adjectives (funny, sarcastic, introvert, dry humor) would suggest. The title is search copy. The shirt is typography.

This is where the object becomes useful to think with, especially for people whose working lives are spent in systems that reward visible enthusiasm. Engineering culture has its own vocabulary of eagerness: the enthusiastic yes in a planning thread, the emoji reaction to a scope change, the cheerful adoption of a tool nobody has benchmarked. A shirt that says meh. at poster scale is a small counterweight. It is not a manifesto and it does not cure anything; it is a piece of clothing that states a boundary in the most efficient typographic form available. Four letters and a period is about as terse as a message can get while remaining a complete sentence. The design’s argument is that terseness can be a form of clarity rather than rudeness — that meh. is a full answer, not a failure to produce a better one.

There is a limit to how far that reading should be pushed. The shirt does not demonstrate anything about introversion, sarcasm, or dry humor as personality traits, and the retail title’s use of those words is marketing rather than evidence. What the artwork actually shows is a typographic contrast: a dismissive word rendered with the confidence of a headline. Everything else — whether the wearer is an introvert, whether the joke lands at a particular office, whether the dark ground suits a given wardrobe — is context the preview cannot supply. I am reading the graphic, not the person who might wear it.

That distinction is the reason the design holds up better than most slogan shirts. Slogan shirts usually fail because the words have to carry the entire joke, and words are easy to overexplain. This one distributes the joke across size, case, color contrast, and punctuation. Remove any one element and it weakens: capitalize it and it becomes a brand; shrink it and it becomes a whisper; drop the period and it becomes a sound effect. The period is doing the work of a punchline without adding a single new letter. For anyone who spends their days tuning interfaces where a one-pixel change alters the meaning of a layout, that is a recognizable craft: the smallest mark carries the most weight.

The practical takeaway is not to buy a shirt. It is to notice how much meaning a period and a type size can hold, and to be suspicious of copy that needs four adjectives to explain a joke the artwork already tells in five characters. If the graphic has to be described as funny, sarcastic, introvert, dry humor before it reads as any of those things, the description is doing work the design should have done. Here the design does the work. The title just helps people find it.

content-visibility: auto vs. Windowing: What Actually Cuts the Cost of a 5,000-Row React List

The ticket says the list is slow. Five thousand rows, server-rendered through the Next.js App Router, and the first scroll after hydration drops frames hard enough that QA files it as a freeze. Someone on the team has read that content-visibility: auto lets the browser skip off-screen rendering work, so the proposal is: add one CSS rule, delete the virtualization dependency, ship it.

That proposal rests on a category error, and it is worth naming before anyone touches the stylesheet. content-visibility: auto and windowing attack different costs. One defers browser layout and paint for subtrees the user cannot see. The other keeps the number of mounted DOM nodes bounded. A 5,000-row list can be slow for either reason, or both, and the fix for one does nothing for the other. The honest version of this article is not “CSS beats react-window.” It is a diagnosis: figure out which cost dominates, then pick the smallest lever that addresses it.

What content-visibility: auto actually does

Per MDN, content-visibility: auto turns on layout containment, style containment, and paint containment. If the element is not relevant to the user, the browser also skips its contents. Crucially, skipped content is not removed: it stays available to find-in-page, tab-order navigation, focus, and selection, unlike content-visibility: hidden, which behaves more like display: none for those purposes. MDN’s summary of the payoff is that the user agent can skip an element’s rendering work, including layout and painting, until it is needed, which makes initial page load faster.

Two properties of that behavior matter for a React list:

  • It is a rendering deferral, not a DOM reduction. Off-screen content under content-visibility: auto remains in the document object model and the accessibility tree. All 5,000 rows are still there. React still reconciles them. The browser just doesn’t lay out or paint the ones far from the viewport until they approach it.
  • It is Baseline Newly available, with a date attached. web.dev’s Baseline announcement pins the status to September 15, 2025, when the feature became available across all three major browser engines. Older browsers simply ignore the property, which is the graceful-degradation story: they render everything, as before.

So the first question for any 5,000-row list is not “should I use content-visibility?” It is “what is expensive: the browser’s style/layout/paint pass over off-screen rows, or the sheer number of mounted nodes and the React work that comes with them?”

The wrong approach, and why it fails at scale

The tempting move is to wrap each row in a component, slap content-visibility: auto on the row, and call it done. Two things go wrong.

First, the scrollbar lies. web.dev is explicit: with size containment applied, an element with no specified height in regular block layout lays out as if empty, at 0 height. The scrollbar’s size then depends on rows having non-zero height, and it shifts as rows render just in time. On a 5,000-row list that is a visible, continuous jitter, and it is exactly the kind of layout instability that shows up in CLS traces.

Second, the DOM is still 5,000 rows. Chrome’s own DOM-size performance insight states the problem plainly: a large DOM can increase the duration of style calculations and layouts, impacting responsiveness, and it also increases memory usage. content-visibility does not shrink that DOM. It does not reduce the number of React fibers, the reconciliation pass over the list, or the memory held by 5,000 mounted row components. If your profile shows a long Recalculate Style or Layout event, content-visibility is aimed at the right target. If it shows React spending tens of milliseconds rendering rows that are nowhere near the viewport, it is aimed at the wrong one.

There is a third failure mode worth flagging because it silently defeats the optimization: web.dev warns that the browser can only skip rendering work if your code does not call DOM APIs that force rendering on a skipped subtree. A measurement helper, an intersection observer callback that reads offsetHeight, a scroll-position calculation that walks every row, a focus-management routine that queries the list, a third-party analytics script measuring elements: any of these can force layout on subtrees the browser was trying to leave alone. The CSS is present, the win is not, and nobody knows why.

What windowing does differently

Windowing renders only a small subset of rows at any given time. React’s own performance guidance describes the technique as dramatically reducing re-render time and the number of DOM nodes created, and points to react-window and react-virtualized as the established libraries. Chrome’s DOM-size insight gives the same stack-specific advice for React: use a windowing library like react-window to minimize the number of DOM nodes created when rendering many repeated elements.

That is a different lever. Windowing changes what React mounts and reconciles. It bounds DOM node count, memory, and the per-update cost of the list. It also introduces its own costs: rows are absolutely positioned with inline styles, so you must not set row height and width from an external stylesheet; variable-height rows need a size function; and fast scrolling can flash blank space unless you overscan, which web.dev cautions against doing too aggressively because it erodes the whole point.

Neither tool is free. The question is which cost you are paying.

A measurement protocol before you choose

Do not pick a tool from a blog post, including this one. Pick it from a trace. The Chrome DevTools Performance panel is the documented instrument here: it records runtime performance for interactions and load performance for page loads, and its capture settings let you throttle CPU and network while recording so the trace approximates your users’ devices rather than your workstation.

  1. Build for production and profile the production build. React’s own guidance on memoization and measurement is blunt that development-mode timings mislead; Strict Mode double-renders and dev warnings distort the picture.
  2. Record a load trace with CPU throttled to a mid-tier mobile preset. Look at the main thread for Recalculate Style and Layout events and their durations. Chrome’s DOM-size insight treats a layout exceeding 40ms with over 100 layout objects, or a style recalculation affecting more than 300 elements, as the kind of event worth chasing.
  3. Record a runtime trace while scrolling the list and while typing into any filter input. The Interaction to Next Paint story lives here: web.dev’s rendering-performance guide notes that visual updates are the result of style, layout, paint, and composite work, and that changing a layout property forces the browser to reflow. If your scroll trace is dominated by layout and paint, content-visibility is a candidate. If it is dominated by React render work, it is not.
  4. Open React DevTools Profiler and record the same interactions. This separates React’s render time from the browser’s rendering time. A change that improves one may leave the other untouched, and you need to know which number moved.
  5. Count the DOM. The Performance panel’s DOM-size insight reports total elements, depth, and the element with the most children. If the list is the bulk of your element count, windowing is addressing a real cost that content-visibility cannot.

Only after that trace should you decide. A hypothetical, clearly labeled as such: if the load trace shows a 180ms Layout event on first paint and the React profiler shows the list component rendering in 12ms, the browser is the bottleneck and deferring off-screen layout is the right lever. If the profiler shows 90ms of React render on every filter keystroke and the layout events are small, windowing is the right lever. Those numbers are illustrative of how to read a trace, not measurements of any real application.

The content-visibility implementation, done correctly

If the trace points at browser layout and paint, the implementation is small but has one non-negotiable detail: contain-intrinsic-size. Without it, skipped rows collapse to zero height and the scrollbar shifts. With the auto keyword, web.dev documents that the browser remembers the last-rendered size and uses it instead of the placeholder, so sizing improves as the user explores the list.

/* app/globals.css — Next.js App Router, React 19.x */
.row {
  content-visibility: auto;
  /* Placeholder height until the row has rendered once.
     Pick a value close to your real row height; the auto
     keyword replaces it with the measured size afterward. */
  contain-intrinsic-size: auto 48px;
}

And the row component stays boring:

// components/Row.tsx
import type { RowData } from '@/lib/types';

export function Row({ row }: { row: RowData }) {
  return (
    <li className="row">
      <span>{row.id}</span>
      <span>{row.label}</span>
      <span>{row.status}</span>
    </li>
  );
}

Three rules for this pattern:

  • Apply it to a bounded chunk, not necessarily every row. If rows are cheap, wrapping groups of rows (say, 50 per section) reduces the number of containment boundaries the browser has to manage. If individual rows are expensive to lay out, per-row is fine. Measure both.
  • Audit for forced layout. Search the codebase for reads of offsetHeight, getBoundingClientRect, scrollHeight, and getComputedStyle that run against list rows during scroll or measurement. Each one can defeat the skip. web.dev notes Chromium can log console messages when such APIs are called on a content-visibility: hidden subtree; turn on verbose logging while you audit.
  • Handle the accessibility tree deliberately. Off-screen content remains in the accessibility tree under auto. That is usually what you want for a data list. But if you have landmark elements or decorative wrappers that you intend to hide from assistive technology, MDN and web.dev both note that display: none and visibility: hidden styles inside skipped subtrees are not rendered, so those elements can still appear in the accessibility tree. Add aria-hidden="true" where that is not desired.

When to reach for windowing instead

Windowing is the right call when the list itself is the problem: thousands of mounted nodes, memory pressure on low-end devices, reconciliation cost on every state update, or a filter input that re-renders the whole list. Chrome’s DOM-size insight and React’s performance guidance both point to react-window for exactly this shape of problem. The trade-off is real: you take on a dependency, you give up natural document flow, you must supply row sizes, and you must tune overscan to avoid blank flashes without over-rendering.

For a 5,000-row list in a server-rendered React 19.x app, the honest recommendation is often neither tool alone. A common architecture is windowing to keep mounted rows bounded, plus content-visibility: auto inside expensive row subtrees (a row with a chart, an embedded map, a rich text preview) so that even the mounted-but-off-screen rows defer their layout and paint. That is a labeled architectural suggestion, not a measured result; validate it against your own trace.

Decision matrix

Symptom in the trace Dominant cost Lever
Long Layout / Recalculate Style on load; React render time small Browser layout/paint of off-screen rows content-visibility: auto + contain-intrinsic-size: auto
High DOM element count; memory growth; slow reconciliation on updates Mounted node count and React work Windowing (react-window)
Both: large DOM and expensive row internals Mixed Windowing for the list, content-visibility inside heavy rows
CSS applied but no improvement Forced layout from DOM reads Audit offsetHeight/getBoundingClientRect calls; remove or batch them

FAQ

Does content-visibility: auto reduce the number of DOM nodes React mounts?

No. MDN states that off-screen content under content-visibility: auto remains in the document object model and the accessibility tree. It defers layout and paint; it does not unmount anything. If DOM node count is your bottleneck, windowing is the tool that changes it.

Why does my scrollbar jump when I add content-visibility: auto?

Because size containment makes an unsized element lay out as if empty, at 0 height, per web.dev. Fix it with contain-intrinsic-size, and use the auto keyword so the browser remembers the last-rendered size instead of reverting to the placeholder.

Is content-visibility safe to ship in production?

It is Baseline Newly available as of September 15, 2025, per web.dev. Browsers that do not support it ignore the property and render everything, so it degrades gracefully. The risk is not breakage; it is shipping it without measuring whether it addresses your actual bottleneck.

Can I use both windowing and content-visibility together?

Yes, and for lists with expensive row internals it is often the right combination: windowing bounds the mounted node count, and content-visibility defers layout and paint inside rows that are mounted but off-screen. Measure the combined result; do not assume the gains add.

How do I know which one to use?

Record a production trace in the Chrome DevTools Performance panel with CPU throttling, and record the same interaction in React DevTools Profiler. If layout and style recalculation dominate, use content-visibility. If React render time and DOM node count dominate, use windowing. If both, use both.

Checklist

  • Profile the production build, not dev, with CPU throttling on.
  • Separate React render time (React DevTools Profiler) from browser style/layout/paint time (Chrome Performance panel).
  • Check DOM element count with the Performance panel’s DOM-size insight.
  • If layout/paint dominates: add content-visibility: auto with contain-intrinsic-size: auto <height>, then audit for forced-layout DOM reads.
  • If node count and reconciliation dominate: adopt windowing, tune overscan, and supply row sizes.
  • If both: window the list and apply content-visibility inside heavy rows.
  • Re-record after the change and confirm the specific event you targeted actually got shorter.

The one-line version: content-visibility: auto is a layout and paint deferral tool, not a DOM-size reducer, and treating it as a drop-in replacement for windowing is how teams ship a CSS rule that changes nothing and then blame the browser.

The Doorbell Notification as a Design Object: Reading ‘There Is Motion at Your Front Door’

The shirt shows a white rounded notification banner with a bell. Inside the banner, in plain sans-serif, the words read: There is motion at your front door. Below the banner sits a circular, green-tinted view — the familiar fish-eye of a doorbell camera — and in it, very close to the lens, a wide-eyed orange tabby. Porch siding and a light fixture are visible behind the cat. That is the whole design. No punchline caption, no second panel, no explanatory text. The joke is the framing itself.

What makes the artwork work is that it reproduces a notification rather than describing one. The banner is not a speech bubble; it is a system message. The bell icon is not decorative; it is the affordance that tells you a device has fired. The circular view is not a portrait; it is a sensor feed. The cat is not posing; it is detected. Every element belongs to the grammar of a doorbell camera alert, and the humor comes from the collision between that flat, procedural language and the specific animal staring into the lens at close range.

I keep thinking about this design as a small lesson in interface honesty. The notification says exactly what happened: motion at the front door. It does not say someone is here, package delivered, or suspicious activity. It reports the sensor reading and leaves the interpretation to you. That is a useful discipline, and it is also why the shirt is funny. The system did its job. The system reported motion. The motion happens to be a cat with its nose against the glass.

The green tint is doing more work than it first appears. Doorbell-camera feeds are often rendered in green or gray because the sensor is optimized for motion detection rather than color fidelity, and the tint signals this is a machine view. On the shirt, that tint separates the circular image from the white banner above it. It tells you the cat is not a portrait but a capture. The porch siding and lights behind the cat establish depth without distracting from the face. The composition is tight: banner, circle, cat. Nothing else competes.

The wording matters too. There is motion at your front door is a complete sentence, but it is not a human sentence. It is the kind of sentence a device writes. Compare it to the way a person would describe the same event: your cat is on the porch again. The shirt chooses the device’s phrasing, and that choice is the joke. The cat is not named, not identified, not even acknowledged as a cat. It is motion. The notification does not know what a cat is. It knows only that something moved.

That gap between what a sensor reports and what a human sees is the argument of the design. The banner is the system’s claim; the circular view is the evidence; the cat is the interpretation. You read the banner first, then look down, then laugh because the motion is a wide-eyed tabby very close to the lens. The shirt is a tiny diagram of how notifications work — and of how often they undersell what is actually happening.

If you want the object itself, the shirt is sold as There Is Motion At Your Front Door Funny Cat Doorbell Meme by Quirky Cotton Collective. The design is the same banner-and-circle composition described above. I am not going to pretend the shirt demonstrates anything about software, security, or engineering results. It is a visual joke about a notification, and it works because the notification is rendered accurately.

What I like most is that the design does not add a caption to explain the joke. The banner is the caption. The cat is the punchline. The green circle is the frame. If you have ever glanced at a doorbell alert and seen something unexpected — a cat, a raccoon, a neighbor’s dog — the shirt reproduces that moment without exaggerating it. The cat’s expression is wide-eyed but not cartoonish. The porch is ordinary. The notification is plain. The humor is in the accuracy.

There is a practical reading here for anyone who designs interfaces: the notification is not the event. It is a compressed report of the event, and the compression is where meaning gets lost. The shirt makes that gap visible by showing you both the report and the thing reported. The banner says motion. The circle shows cat. The distance between those two words is the entire design.

Editorial note: This publication and Quirky Cotton Collective share an owner.

The Period After ‘meh’ Is the Whole Design

The native garment preview shows very small, centered lowercase meh. lettering on a dark shirt. No animal illustration is present. That is the entire observed design: four letters, a period, and a lot of dark fabric around them. The scale is the first thing I notice — not the word, which is familiar, but how little of the shirt it occupies. The period is the second. A period after a single word is a full stop, and a full stop is a decision.

Most slogan shirts treat the chest as a billboard. The text is large enough to read across a room, the joke is delivered before the wearer has to explain it, and the garment does the talking. This one does the opposite. The lettering is small enough that a reader has to be close, or looking directly, to catch it. That changes the social contract of the shirt. It is not a broadcast; it is a caption. The wearer is not performing indifference at volume. They are wearing a quiet notation that happens to be visible.

The period is what makes the notation read as final rather than decorative. Without it, meh is a sound — a shrug, a trailing vowel, something that could be followed by I guess or whatever. With the period, it is a sentence. A one-word sentence with terminal punctuation is grammatically complete and socially closed. It does not invite a follow-up question. That is a different object from a sarcastic slogan, which usually wants a laugh and therefore wants engagement. The period declines the engagement while still making the statement. I read that as the design’s actual argument: understatement is not the same as absence, and a boundary can be four letters long.

The retail title calls this a Meh Funny Sarcastic Introvert Minimalist Saying, and the keyword people type is meh shirt. Those words describe intent, not print. The print is lowercase, centered, and small. The title’s adjectives are marketing prose; the visual observation is the evidence. I am not going to claim the shirt is funny in practice, or that introverts will recognize themselves in it, or that it reads a certain way in an office. What I can say is that the design’s restraint is deliberate and legible as restraint. The smallness is not a printing error or a thumbnail artifact. It is the composition.

There is a useful distinction here between a slogan and a usable promise. A slogan is written to be repeated by someone else. A usable promise is written to be kept by the person wearing it. meh. at this scale reads closer to the second category. It is not trying to recruit anyone. It is a terse message about the wearer’s available enthusiasm, delivered at a volume that matches the message. If the word were large, the shirt would be shouting meh, which is a contradiction. The small type resolves the contradiction. The medium and the message agree.

I have written before about the period after meh as the load-bearing element, and I still think that is right, but the scale deserves its own attention. A period at large type is a punchline. A period at small type is a filing. The shirt reads like a status field that has been set and closed, not a joke that has been told. That is a more interesting object for a technical reader than another sarcastic one-liner, because it is about the difference between expressing a state and performing one. The design chooses expression. It does not ask you to laugh. It asks you to notice, and then it stops.

What the shirt does not do is demonstrate anything. It is not a diagram, it does not encode a process, and it makes no claim about software, security, or engineering. The temptation with a minimal design is to over-read it as a systems metaphor — the period as a terminator, the lowercase as a convention, the small type as a payload budget. Those are my associations, not the shirt’s claims. The observed object is four letters and a period on a dark ground. The restraint is real; the metaphor is mine. I am flagging that distinction because a minimal design is easy to inflate, and the honest reading is smaller than the clever one.

The practical takeaway is about how to read a terse object. When a design removes almost everything, the remaining choices carry more weight, and the first question is not what does it mean but what was kept. Here, what was kept is the word, the case, the period, the centering, and the scale. What was removed is the animal illustration, the extra clause, the exclamation point, and the volume. That removal is the design. If you are evaluating a minimal shirt — or writing one — the test is whether the kept elements agree with each other. In this case they do: a small word, a quiet case, a final period, and a lot of dark space that lets the notation sit rather than shout. The period is not decoration. It is the whole design.

Editorial note: This publication and Quirky Cotton Collective share an owner; this is not an independent review.

The Crossed-Out Hand Is the Argument: Reading the ‘Not Friendly Do Not Touch’ Warning Sign

The shirt does not say I am shy. It says NOT FRIENDLY, then DO NOT TOUCH, and it hangs a yellow caution triangle with a crossed-out hand between the two lines. That is the whole design. No mascot, no punchline, no decorative border. It borrows the grammar of a hazard placard and points it at a person.

That borrowing is worth taking seriously, because a warning sign is one of the few graphic forms most of us read without being taught. We learn the triangle before we learn to read. We learn the diagonal slash before we learn the word prohibited. The shirt is counting on that fluency, and the interesting question is what happens when a public-safety format is used to describe a private mood.

What the sign actually says

Look at the order of the elements. The caution triangle sits between the two lines of text, not above them. A real hazard placard usually puts the symbol first and the words underneath, so the eye gets the category before the sentence. Here the triangle is wedged into the middle, which means the reader hits NOT FRIENDLY, then the symbol, then DO NOT TOUCH. The graphic interrupts the sentence at its most literal point: the moment the warning turns from a description of temperament into an instruction about contact.

The crossed-out hand is doing more work than the words. A hand with a diagonal bar through it is the standard visual shorthand for no entry, do not pass, keep out. On a shirt, that symbol is aimed at the person reading it, not at a machine or a doorway. The wearer has put a barrier graphic on the part of the body a stranger is most likely to reach for. That is the joke, and it is also the design’s one real idea.

The yellow is the only color in the composition, as far as the supplied observation goes. Everything else is lettering and line. That single accent is what makes the triangle read as a caution sign rather than a logo, and it is why the shirt works at a glance from across a room. The color is not decoration; it is the cue that tells you which genre you are looking at.

A slogan is not a boundary

There is a difference between a shirt that states a preference and a shirt that enforces one, and this design is interesting because it pretends to do the second while only doing the first. Not friendly is a claim about character. Do not touch is an instruction. The caution triangle and the crossed-out hand are the visual equivalent of a locked door. But a shirt cannot lock anything. It can only announce that a lock exists somewhere in the wearer’s head.

That gap is where the humor lives, and it is also where the design is more honest than it first appears. Most social-boundary merchandise is aspirational: it describes the person you would like to be on a good day. This one describes a limit and then admits, through the absurdity of printing it on cotton, that the limit is being communicated rather than imposed. The sign is a request dressed as a prohibition. Anyone who has ever written please do not @ me in a channel topic knows the form.

For readers who spend their working lives writing terse messages to large groups of people, that distinction is familiar. A code comment that says do not modify is not a technical control; it is a social one. A README that says internal use only is a sentence, not a firewall. The shirt belongs to the same family of objects: a message that works only because the reader agrees to treat it as a rule. The caution triangle is doing the same job as the bold text in a warning block. It raises the cost of ignoring the sentence without making it impossible.

Why the warning-sign format is the right container

Warning signs are designed to be read by people who are not paying attention. That is their entire engineering brief. High contrast, short words, a symbol that survives being seen for a quarter of a second. The shirt borrows all of that. NOT FRIENDLY is four syllables and two words. DO NOT TOUCH is three words, all monosyllabic except the last. The triangle is a shape a child can identify. Nothing here requires a second read.

That efficiency is what separates this design from a longer, chattier introvert joke. A shirt that says sorry, I’m awkward, please don’t talk to me is asking for sympathy. A shirt that says DO NOT TOUCH under a hazard triangle is not asking for anything. It is issuing a notice. The format does the emotional work that the words would otherwise have to do, and it does it without a single hedge or apology. The bluntness is the point.

I would not claim the design is subtle. It is not trying to be. But it is composed, and composition is what makes a blunt message readable rather than aggressive. The triangle is centered, the two text lines are balanced around it, and the crossed-out hand is small enough to sit inside the symbol without crowding it. The restraint is what keeps the shirt on the friendly side of unfriendly.

The introvert label is doing something specific

The listing title includes the word introvert, and that is a useful clue to how the design expects to be read. Introversion is not misanthropy, and the shirt is careful not to say it is. Not friendly is a statement about available social energy, not about dislike. Do not touch is a statement about physical space. Together they describe a person who is present but not open for casual contact, which is a recognizable posture and not a diagnosis.

The caution triangle is what keeps that posture from reading as hostile. A hazard sign is impersonal by design. It does not resent you; it simply informs you. By wrapping the message in that format, the shirt turns a personal limit into a piece of public infrastructure. The wearer is not rejecting anyone in particular. They are posting a notice. That framing is the difference between a shirt that starts conversations and a shirt that ends them, and it is the reason the design can be funny without being mean.

What the shirt is not

It is worth being clear about the limits of the object. The design does not demonstrate anything about introversion, social psychology, or workplace behavior. It is a graphic that borrows a familiar format and applies it to a mood. Reading it closely tells you something about how warning signs work as visual rhetoric. It does not tell you anything about the person wearing it, and it certainly does not establish that the wearer is unfriendly in any measurable sense. The joke depends on the gap between the sign and the person, and that gap is the whole thing.

Nor is the shirt a solution to anything. It will not stop a stranger from tapping your shoulder in a crowded bar, and it will not make a meeting shorter. It is a sentence printed on fabric, and its only mechanism is the reader’s willingness to read it. That is true of most clothing with words on it, and it is worth saying plainly rather than dressing the object up as a social tool it cannot be.

The one technique: read the sign as a sentence

If there is a takeaway here, it is a reading habit rather than a fix. When a design borrows a public-sign format, check what the format promises and what the design actually delivers. The caution triangle promises a hazard. The crossed-out hand promises an enforced boundary. The shirt delivers a sentence. The distance between the promise and the delivery is where the meaning lives, and it is also where the design either works or collapses into noise.

That habit is useful well beyond t-shirts. It is the same question you ask of a status page, a deprecation notice, or a comment that says do not edit. The format is doing rhetorical work, and the work is only honest if the reader can see what is actually being enforced. This shirt passes that test because it never pretends the enforcement is real. The crossed-out hand is a picture of a boundary, not a boundary. The joke is that we all agree to act as if it were one.

The shirt is available as a standard t-shirt from Quirky Cotton Collective: Not Friendly Do Not Touch Funny Warning Sign Introvert.

Editorial note: This publication and Quirky Cotton Collective share an owner.

The Roots Are the Diagram: Reading ‘Let’s Root for Each Other’ as a Promise With a Boundary

Four root vegetables sit in a row of soil, each with a face: a carrot, a radish, a beet, an onion. Their roots hang below the soil line, drawn as clearly as the leaves above it. Around them, in capital letters, runs the line LET’S ROOT FOR EACH OTHER AND WATCH EACH OTHER GROW. The design is a gardening pun, and the pun is doing more work than it first appears.

Most slogan shirts ask the wearer to agree with a sentence. This one asks the reader to notice a diagram. The roots are the argument. Above the soil, four different plants look like four different plants. Below it, they are all doing the same thing: reaching down, holding ground, drawing up what they need. The artwork does not show them competing for the same patch. It shows them rooted side by side, each with its own visible structure. That is the part worth reading closely, especially for people whose working lives are spent in teams that talk constantly about growth.

Two Meanings of ‘Root,’ Held at Once

The word root carries the gardening sense and the encouragement sense at the same time. To root for someone is to support them from the sidelines. To put down roots is to become established, to stay. The shirt uses both without explaining either. The vegetables are literally rooted; the sentence is figuratively cheering. The design works because the two meanings are not competing. They are stacked, the way the drawing stacks leaves above soil and roots below.

That stacking is what makes the line more interesting than a generic encouragement slogan. Let’s root for each other is mutual. It is not one person cheering for another from a safe distance. It is a group agreeing to do the cheering as a group. And watch each other grow adds a second clause that changes the first. Support is not the end state. Growth is. The shirt is not content with being nice; it wants a result.

This is where the design becomes useful as a work-culture object rather than a pleasant garden joke. A slogan that stops at root for each other is a mood. A slogan that adds and watch each other grow is a small contract. It names an observer and a subject, and it implies that the growth will be visible to someone other than the person doing it.

The Smiling Vegetables Are Not Decoration

The faces matter. Each vegetable is drawn with a simple smile, and the smiles are what keep the line from sounding like a performance review. Without them, watch each other grow could read as surveillance. With them, it reads as a shared garden. The drawing sets the emotional register before the words have a chance to be over-interpreted.

There is also a quiet specificity in the choice of vegetables. Carrot, radish, beet, and onion are all root crops, and all four are drawn with their roots visible in the soil. The design is not a random assortment of cute produce. It is a set of examples that make the pun legible. A tomato would have broken the joke. The illustrator understood that the drawing had to carry the same word the sentence was using.

I read the four different root shapes as a mild argument about teams. A carrot root and a beet root do not look alike, and the shirt does not try to make them. The common structure is underground; the visible parts are allowed to differ. That is a more honest picture of mutual growth than a row of identical sprouts would have been. It is also a more demanding one, because it implies that watching someone grow means watching them become something you did not prescribe.

The Boundary Inside the Promise

Here is the tension I keep returning to. Watch each other grow is generous when it is offered and uncomfortable when it is expected. In a team, the difference between the two is usually a matter of consent and scope. Who decided that growth was the goal? Whose definition of growth is being watched? Is the watching occasional and warm, or continuous and evaluative?

The shirt does not answer those questions, and it should not be asked to. But the artwork gives a hint about how to hold them. The roots are below the soil, mostly out of sight. The smiling faces are above. The design keeps the messy, structural part of growth partly hidden and puts the friendly part forward. That is a reasonable way to draw a garden. It is a less reasonable way to run a performance system, where the hidden part is often the part that determines who gets to keep growing.

So the line reads best as an aspiration with a boundary built in. Each other is the boundary. The promise is reciprocal, not top-down. It is something peers offer one another, not something a manager announces on a slide. The shirt is a better fit for a team that already has the habit than for a team trying to manufacture it with merchandise.

What the Object Is, Plainly

The shirt is sold by Quirky Cotton Collective under the title Let’s Root For Each Other And Watch Each Other Grow Garden. The design places the full sentence around the four smiling root vegetables, with their roots drawn in soil. The phrase lets root for each other shirt is the kind of query a person types when they already know the sentiment and want to see how it has been drawn. The drawing is the answer: a small, legible garden diagram rather than a wall of type.

You can see the listing here: Let’s Root For Each Other And Watch Each Other Grow Garden.

Editorial note: this publication and Quirky Cotton Collective share an owner. This column is not an independent review.

Why This Works Better Than a Generic Encouragement Shirt

Most mutual-support slogans fail because they are all sentiment and no image. They ask the reader to feel something without giving the eye anything to do. This design gives the eye a small puzzle: four vegetables, four root systems, one sentence wrapped around them. The puzzle resolves in a second or two, and the resolution is the joke. That is a durable structure for a printed object, because it rewards a second look without requiring one.

The other thing the design gets right is scale. The sentence is long, and a long sentence on a shirt can turn into a paragraph. Here the line is broken around the illustration rather than stacked in a block, so the vegetables stay the center and the words frame them. The reading order is not strictly linear, which is fine for a design that is meant to be glanced at rather than recited.

For technical readers, there is a familiar shape to the joke. It is a diagram with a caption, and the caption is funnier because the diagram is literal. The roots are not a metaphor for the roots; they are roots. The metaphor arrives in the word root and then gets grounded by the drawing. That is the same move as a good comment in code: it does not explain what the line already shows, it adds the thing the line cannot show by itself. Here the line cannot show mutual support, so the four smiling vegetables do.

The One Technique: Read the Promise Before You Wear It

If there is a practical takeaway from this object, it is not about gardening and not about shirts. It is about the difference between a slogan and a usable promise. A usable promise names who is making it, who it is for, and what would count as keeping it. Let’s root for each other and watch each other grow names the group and the action, but it leaves the definition of growth open. That openness is what makes it a good shirt and a risky team motto.

Before you put a line like this on a wall, a slide, or a chest, ask the question the roots are quietly asking: who decides what growing looks like, and who is doing the watching? If the answer is we all are, and we agreed on it, the sentence is a promise. If the answer is whoever is watching, it is a slogan wearing a smile. The shirt is charming either way. The difference is in the soil.