<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>COMPUTERCOURSESONLINE</title>
	<atom:link href="https://computercoursesonline.com/index.php/feed/" rel="self" type="application/rss+xml" />
	<link>http://computercoursesonline.com</link>
	<description></description>
	<lastBuildDate>Thu, 28 May 2026 21:06:22 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=5.9.2</generator>
	<item>
		<title>Algorithmic Theming Engines: Building Self-Correcting Color Systems With `contrast-color()`</title>
		<link>http://computercoursesonline.com/index.php/2026/05/28/algorithmic-theming-engines-building-self-correcting-color-systems-with-contrast-color/</link>
					<comments>http://computercoursesonline.com/index.php/2026/05/28/algorithmic-theming-engines-building-self-correcting-color-systems-with-contrast-color/#respond</comments>
		
		<dc:creator><![CDATA[.]]></dc:creator>
		<pubDate>Thu, 28 May 2026 13:00:00 +0000</pubDate>
				<category><![CDATA[Css]]></category>
		<guid isPermaLink="false">http://computercoursesonline.com/?p=1227</guid>

					<description><![CDATA[Algorithmic Theming Engines: Building Self-Correcting Color Systems With `contrast-color()` Algorithmic Theming Engines: Building Self-Correcting Color Systems With `contrast-color()` Durgesh Pawar 2026-05-28T13:00:00+00:00 2026-05-28T20:50:06+00:00 The HTTP Archive Web Almanac has been tracking color contrast failures for years. The numbers have barely moved. After half a decade of design system tooling, accessibility linters, and entire JavaScript libraries dedicated...]]></description>
										<content:encoded><![CDATA[<p>              <title>Algorithmic Theming Engines: Building Self-Correcting Color Systems With `contrast-color()`</title></p>
<article>
<header>
<h1>Algorithmic Theming Engines: Building Self-Correcting Color Systems With `contrast-color()`</h1>
<address>Durgesh Pawar</address>
<p>                  2026-05-28T13:00:00+00:00<br />
                  2026-05-28T20:50:06+00:00<br />
                </header>
<p>The <a href="https://almanac.httparchive.org/">HTTP Archive Web Almanac</a> has been tracking color contrast failures for years. The numbers have barely moved. After half a decade of design system tooling, accessibility linters, and entire JavaScript libraries dedicated to computing readable text colors, <a href="https://almanac.httparchive.org/en/2025/accessibility#color-contrast">70% of websites still fail basic WCAG contrast checks in 2025</a>. The <a href="https://webaim.org/projects/million/">WebAIM Million</a> paints an even grimmer picture &mdash; 83.9% of homepages flagged for low contrast text in 2026, up from 79.1% in 2025. The rate improves by maybe a few percentage points per year on one benchmark and actually gets <em>worse</em> on another. That’s not progress &mdash; that’s proof that relying on runtime JavaScript for something this fundamental doesn’t scale across the open web. We didn’t need better libraries. We’ve needed better CSS.</p>
<p>The <code>contrast-color()</code> function is that better CSS. One declaration. The browser runs the contrast math during style computation, before the page paints, and hands you the right text color. No library, no build step, no hydration flash.</p>
<p><strong>Note</strong>: If you’ve seen it called <code>color-contrast()</code> in older articles and spec drafts &mdash; <a href="https://github.com/w3c/csswg-drafts/issues/7557">that name was changed</a>, and the old syntax no longer works in any browser.</p>
<h2 id="what-it-does-and-what-it-doesn-t">What It Does (And What It Doesn’t)</h2>
<p>The Level 5 version is simple. You give it a color. It gives you back <code>black</code> or <code>white</code>, whichever has more contrast against your input.</p>
<pre><code class="language-css">.button {
  background-color: var(--brand-color);
  color: contrast-color(var(--brand-color));
}
</code></pre>
<p>Change <code>--brand-color</code> to neon green, text goes black. Change it to midnight navy, text goes white. Swap themes at runtime via JavaScript and the text adapts instantly &mdash; no event listeners, no recalculation.</p>
<p>A few things to know about the current version:</p>
<ul>
<li>It returns a <code>&lt;color&gt;</code>, not a number. You get an actual color value (<code>black</code> or <code>white</code>), you can use anywhere CSS accepts a color.</li>
<li><strong>Black or white only</strong>, for now. Candidate color lists and target ratios are planned for Level 6.</li>
<li><strong>No keywords.</strong> If you’ve seen <code>max</code> in older blog posts, that was stripped from the spec. Using it will silently break your declaration.</li>
<li>As mentioned above, this function used to be called <code>color-contrast()</code> in early drafts. That name is dead &mdash; <a href="https://github.com/w3c/csswg-drafts/issues/7557">the CSSWG renamed it</a> to follow the convention that CSS functions are named for what they return. <code>color-mix()</code> returns a color. <code>contrast-color()</code> returns a color. The old <code>color-contrast()</code> name sounded like it returned a contrast <em>ratio</em> (a number like 4.5), which was misleading. Any tutorial from 2021&ndash;2023 showing <code>color-contrast()</code> syntax won’t work in current browsers.
<p></li>
</ul>
<div data-audience="non-subscriber" data-remove="true" class="feature-panel-container">
<aside class="feature-panel">
<div class="feature-panel-left-col">
<div class="feature-panel-description">
<p>Meet <strong><a data-instant href="https://www.smashingconf.com/online-workshops/">Smashing Workshops</a></strong> on <strong>front-end, design &amp; UX</strong>, with practical takeaways, live sessions, <strong>video recordings</strong> and a friendly Q&amp;A. With Brad Frost, Stéph Walter and <a href="https://smashingconf.com/online-workshops/workshops">so many others</a>.</p>
<p><a data-instant href="smashing-workshops" class="btn btn--green btn--large">Jump to the workshops&nbsp;↬</a></div>
</div>
<div class="feature-panel-right-col"><a data-instant href="smashing-workshops" class="feature-panel-image-link"></p>
<div class="feature-panel-image">
<img loading="lazy" class="feature-panel-image-img lazyload" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" alt="Feature Panel" width="257" height="355" data-src="/images/smashing-cat/cat-scubadiving-panel.svg"></p>
</div>
<p></a>
</div>
</aside>
</div>
<h2 id="the-spec-split-level-5-versus-level-6">The Spec Split: Level 5 Versus Level 6</h2>
<p>This function lives across two specifications. That’s unusual and worth understanding.</p>
<p><a href="https://www.w3.org/TR/css-color-5/#contrast-color"><strong>CSS Color Level 5</strong></a> defines what browsers ship today. One color in, black or white out. The algorithm is deliberately marked “UA-defined”, meaning the browser decides what math to use internally. Right now, every engine uses WCAG 2.x relative luminance. But that “UA-defined” label isn’t accidental &mdash; it’s a planned escape hatch.</p>
<p>You’ll see APCA (<strong>Accessible Perceptual Contrast Algorithm</strong>) mentioned a lot in this context. APCA models how human eyes actually perceive contrast, factoring in font weight, spatial frequency, and ambient light &mdash; a genuine improvement over the WCAG 2.x formula. By not locking <em>“use WCAG 2.x”</em> into the Level 5 spec, browser vendors <em>could</em> swap to APCA later without breaking any existing code. If the spec had shipped with a <code>wcag2()</code> keyword as the default, every site using it would’ve been stuck on the old math permanently.</p>
<p>But APCA’s future is far less certain than the hype suggests. Adrian Roselli’s “<a href="https://adrianroselli.com/2026/04/wcag3-contrast-as-of-april-2026.html">WCAG3 Contrast as of April 2026</a>” lays out the current situation clearly: APCA was pulled from the WCAG 3 working draft <a href="https://www.w3.org/TR/2023/WD-wcag-3.0-20230724/#color-and-contrast">in mid-2023</a> after failing to gain enough Working Group support. The WCAG 3 spec currently says the contrast algorithm is <em>“yet to be determined,”</em> and the standard itself <a href="https://www.w3.org/WAI/standards-guidelines/wcag/wcag3-intro/#timeline">may not be finalized until 2030 or later</a>. Roselli also <a href="https://issues.chromium.org/issues/341439947">filed a Chromium issue in May 2024</a> asking for the “Advanced Perceptual Contrast Algorithm” experiment flag to be removed from DevTools entirely, arguing that the implementation is outdated and risks misleading developers into thinking APCA is further along &mdash; or more official &mdash; than it actually is. That issue is still open.</p>
<p>None of this means APCA is dead. The research behind it is peer-reviewed and substantive, and its creator <a href="https://adrianroselli.com/2026/04/wcag3-contrast-as-of-april-2026.html#comment-408186">has noted</a> that colors passing APCA guidelines greatly exceed WCAG 2 minimums in the vast majority of cases. But right now, there is no guarantee APCA will be the algorithm that replaces WCAG 2.x &mdash; and that uncertainty matters for <code>contrast-color()</code>. If a different algorithm wins out, or if WCAG 3 adopts something entirely new, the “UA-defined” label means browsers can adapt without breaking your code. It also means the Level 6 features &mdash; candidate color lists, target ratios, the <code>tbd-fg</code>/<code>tbd-bg</code> keywords &mdash; are all designed around an algorithm that may or may not materialize in its current form.</p>
<p><strong>CSS Color Level 6</strong> adds the extended syntax &mdash; candidate color lists and target contrast ratios:</p>
<div class="break-out">
<pre><code class="language-css">/&#042; Level 6 future syntax — not shipping yet &#042;/
color: contrast-color(var(--bg) tbd-bg wcag2(aa), #1a1a2e, #e2e8f0, #fbbf24);
</code></pre>
</div>
<p>The browser would evaluate each candidate left to right and pick the first that meets the 4.5:1 AA threshold. The <code>tbd-fg</code> and <code>tbd-bg</code> keywords indicate whether the base color is foreground or background, which matters for directional contrast models like APCA. This is all Working Draft territory &mdash; doubly so given APCA’s uncertain status. Use the Level 5 version for now.</p>
<h2 id="browser-support">Browser Support</h2>
<p>This one’s in better shape than most new CSS features. All three major engines have shipped it in stable releases: <a href="https://chromestatus.com/feature/40142548">Chrome 147</a> (April 2026), <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/color_value/contrast-color">Firefox 146</a>, and <a href="https://developer.apple.com/documentation/safari-release-notes/safari-26-release-notes">Safari 26.0</a>. It reached <a href="https://web.dev/baseline/">Baseline Newly Available</a> status in April 2026. Check <a href="https://caniuse.com/mdn-css_types_color_contrast-color">caniuse</a> for the full version matrix. All three engines pass the Web Platform Tests for <code>contrast-color()</code>, which means the edge cases (e.g., tie-breaking logic, color space conversion, syntax parsing) behave the same across browsers.</p>
<p>The raw global support percentage on caniuse looks low, but that mostly reflects enterprise browsers and people who never update. If you’re reading this, your browser almost certainly supports it already.</p>
<p>Progressive enhancement is straightforward using <code>@supports</code>:</p>
<pre><code class="language-css">.card {
  background: var(--bg);
  color: &#035;fff;
  text-shadow: 0 0 4px rgb(0 0 0 / 0.8);
}

@supports (color: contrast-color(red)) {
  .card {
    color: contrast-color(var(--bg));
    text-shadow: none;
  }
}
</code></pre>
<p>Older browsers get white text with a dark shadow for legibility. Supporting browsers get the native calculation. Nobody sees broken text.</p>
<p>One thing to watch for: automated accessibility scanners (Lighthouse, Axe, etc.) can’t evaluate <code>text-shadow</code>. They only look at the computed <code>color</code> against <code>background-color</code>. So the fallback will still get flagged as a contrast failure in CI/CD pipelines, even if the shadow makes the text perfectly legible to human eyes. If your team runs automated a11y checks, you may need to allowlist that specific rule or add a comment explaining why the flag is a false positive.</p>
<blockquote><p><strong>A note on PostCSS</strong>:</p>
<p>There’s a plugin (<a href="https://github.com/csstools/postcss-plugins/tree/main/plugins/postcss-contrast-color-function"><code>@csstools/postcss-contrast-color-function</code></a>) that evaluates <code>contrast-color()</code> at build time. It works for static colors like <code>contrast-color(#ff0000)</code>. But the moment you use a custom property &mdash; <code>contrast-color(var(--bg))</code> &mdash; the plugin can’t help because it has no access to runtime values. If your theming is dynamic (which is the whole point of doing this), skip the polyfill and rely on <code>@supports</code>.</p></blockquote>
<div class="partners__lead-place"></div>
<h2 id="the-gotchas">The Gotchas</h2>
<h3 id="it-doesn-t-guarantee-perceptual-or-aaa-compliance">It Doesn’t Guarantee Perceptual or AAA Compliance</h3>
<p>This can trip people up: <em>“I used the contrast function, so my site passes accessibility checks now, right?”</em></p>
<p>Mathematically? Usually yes. There is a persistent myth that for certain “mid-tone” backgrounds, both black and white fail the standard WCAG 4.5:1 AA ratio. That’s mathematically false. Under the WCAG 2.x relative luminance formula, there is absolutely no background color where both pure black and pure white fail AA. One (or both) will always pass.</p>
<p>Take <code>#2277d3</code> (a medium blue). It sits right on a mathematical knife-edge where both black and white actually pass AA (both hit roughly 4.58:1). <code>contrast-color()</code> will hand you whichever has the slight mathematical edge.</p>
<p>But here is the actual gotcha: the WCAG 2.x math has known perceptual blind spots. That same <code>#2277d3</code> with black text mathematically passes AA, but to human eyes, it can be incredibly difficult to read. <code>contrast-color()</code> gives you <em>mathematical</em> compliance, which is great for automated audits, but that doesn’t always equal <em>perceptual</em> accessibility. (This is exactly why APCA exists and why the spec was designed to let browsers swap algorithms later.)</p>
<p>Furthermore, if you’re aiming for the stricter WCAG AAA standard (7.0:1), a true dead zone <em>does</em> exist. For backgrounds with a luminance between roughly 10% and 30%, neither black nor white will hit 7:1. In those cases, <code>contrast-color()</code> can’t save you &mdash; it just hands you the “least bad” failing option.</p>
<h3 id="transitions-snap-not-fade">Transitions Snap, Not Fade</h3>
<p>If you’re animating a background from <code>white</code> to <code>black</code> on hover:</p>
<pre><code class="language-css">.btn {
  background-color: &#035;fff;
  color: contrast-color(&#035;fff); /&#042; black &#042;/
  transition: background-color 1s, color 1s;
}
.btn:hover {
  background-color: &#035;000;
  color: contrast-color(&#035;000); /&#042; white &#042;/
}
</code></pre>
<p>The background fades smoothly over one second. But because the Level 5 output is a <a href="https://www.w3.org/TR/css-transitions-1/#discrete">discrete value</a> (black or white), the text color can’t be interpolated. It snaps.</p>
<p>And here is the visual gotcha: the snap doesn’t happen halfway through. If you’ve been building themes for a while, you probably have muscle memory from the old Sass days, where we checked if <code>lightness($bg) &gt; 50%</code>. That relied on HSL lightness, where 50% is the geometric midpoint.</p>
<p>But WCAG 2.x relative luminance is a non-linear scale. Under the WCAG formula, the mathematical tipping point &mdash; where black and white have identical contrast against the background &mdash; actually occurs at approximately 18% relative luminance (specifically ~17.9%).</p>
<p>Because of that, the visual behavior during a white-to-black fade is heavily skewed. The text doesn’t snap in the middle. It stays black for the vast majority of the animation, only snapping to white at the very tail-end of the transition when the background gets extremely dark. It’s a jarring, late hard cut.</p>
<p>You might assume <a href="https://css-tricks.com/almanac/properties/t/transition/transition-behavior/"><code>transition-behavior: allow-discrete</code></a> fixes this. It doesn’t. <code>allow-discrete</code> does not fix the jarring visual experience because it cannot interpolate a binary output; it only shifts the timing of the hard snap to the 50% mark of the animation duration. If you need smooth text color transitions, you’ll have to layer <code>color-mix()</code> or manage the crossfade yourself.</p>
<h3 id="tie-goes-to-white">Tie Goes To White</h3>
<p>If the background is a perfect middle gray where both black and white produce identical contrast ratios, <a href="https://www.w3.org/TR/css-color-5/#contrast-color">the spec has a hardcoded tiebreaker</a>: white wins. Not a big deal in practice, but worth knowing if you’re debugging gray palettes and the text isn’t doing what you expect.</p>
<h3 id="gradients-and-images-are-out">Gradients And Images Are Out</h3>
<p>The function takes a flat <code>&lt;color&gt;</code> value. You can’t pass it a gradient or a <code>url()</code>. <code>contrast-color(linear-gradient(...))</code> is a parse error. If your background is a photo or a complex gradient, you still need JavaScript or manually color-pick for overlay text.</p>
<h3 id="transparent-colors-are-composited-first">Transparent Colors Are Composited First</h3>
<p>Pass a semi-transparent color, and the browser blends it against an assumed opaque canvas (usually white) before running the contrast math. It’s not ignoring your alpha channel &mdash; it’s compositing it. But the result might surprise you if you expected the function to “see through” to whatever’s actually behind the element.</p>
<h3 id="windows-high-contrast-mode">Windows High Contrast Mode</h3>
<p>If a user enables Windows High Contrast, the <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/@media/forced-colors"><code>forced-colors: active</code></a> media query kicks in and the browser aggressively overwrites author-defined colors. <code>contrast-color()</code> bows out &mdash; <a href="https://www.w3.org/TR/css-color-adjust-1/#forced-colors-mode">forced system colors</a> like <code>CanvasText</code> take over completely. You don’t need to write manual media queries to undo your contrast logic; the browser handles the hierarchy.</p>
<h2 id="combining-it-with-other-color-functions">Combining It With Other Color Functions</h2>
<p>Black or white sounds limiting, but once you feed that output into other CSS color functions, you can build an entire component palette off a single custom property.</p>
<h3 id="brand-tinted-contrast-with-relative-color-syntax">Brand-Tinted Contrast With Relative Color Syntax</h3>
<p>Pure black text on a vibrant card looks fine. Pure white on a coral card can feel flat. What if the contrast text was a very dark or very light <em>tint</em> of the background color instead?</p>
<p>Kevin Hamer explored related territory in his CSS-Tricks piece “<a href="https://css-tricks.com/approximating-contrast-color-with-other-css-features/">Approximating contrast-color() With Other CSS Features</a>”, where he used OKLCH lightness and <code>round()</code> to approximate the black/white switch <em>without</em> <code>contrast-color()</code> &mdash; essentially <code>oklch(from &lt;color&gt; round(1.21 - l) 0 0)</code>. That’s a polyfill strategy: get the binary light/dark decision working in browsers that don’t support the native function yet. What we’re doing here is different &mdash; we <em>start</em> with <code>contrast-color()</code>’s native output and then enrich it by injecting the background’s own hue:</p>
<pre><code class="language-css">.card {
  --bg-hue: 260; /&#042; Indigo &#042;/
  --bg: oklch(0.6 0.1 var(--bg-hue));
  background: var(--bg);

  /&#042; Pull L from the black/white contrast color,
     but inject subtle chroma and the background's hue &#042;/
  color: oklch(from contrast-color(var(--bg)) l 0.05 var(--bg-hue));
}
</code></pre>
<p>When <code>contrast-color()</code> returns white, <code>l</code> is 1 (full lightness). When it returns black, <code>l</code> is 0. By pulling the background’s hue back in and adding a touch of chroma, you get text that reads as a deep dark indigo or a pale icy indigo instead of generic black/white. Hamer’s approach gives you the black/white decision without browser support; this one takes the decision the browser already made and gives it personality.</p>
<p><strong>Fair warning</strong>: By tweaking the lightness and chroma of the black/white output, you can push a borderline contrast ratio into failing territory. Always run your tinted output through an accessibility linter before shipping.</p>
<p><strong>Also worth noting</strong>: This example chains two very modern features &mdash; <code>contrast-color()</code> and <code>oklch(from ...)</code>. If either one isn’t supported, the entire declaration fails silently. Your <code>@supports</code> block needs to test for both:</p>
<div class="break-out">
<pre><code class="language-css">@supports (color: contrast-color(red)) and (color: oklch(from red l c h)) {
  /&#042; Safe to use both &#042;/
}
</code></pre>
</div>
<h3 id="softened-contrast-with-color-mix">Softened Contrast With <code>color-mix()</code></h3>
<p>Similar idea, simpler API. Mix the sharp black/white output back into the background to soften it:</p>
<div class="break-out">
<pre><code class="language-css">.alert {
  --bg: var(--alert-color);
  background: var(--bg);

  /&#042; 80% contrast, 20% background = softer but readable &#042;/
  color: color-mix(in oklch, contrast-color(var(--bg)) 80%, var(--bg));

  /&#042; 40% contrast for a subtle border &#042;/
  border: 1px solid
    color-mix(in oklch, contrast-color(var(--bg)) 40%, var(--bg));
}
</code></pre>
</div>
<p>One custom property driving text, border, and potentially <code>box-shadow</code> or <code>outline</code>. Change <code>--alert-color</code> and the entire component recalculates.</p>
<p>This pattern also works well for <code>::placeholder</code> text, which is a common pain point in dynamic theming. Placeholder text should be readable but visually softer than the input’s main text &mdash; <code>color-mix()</code> with <code>contrast-color()</code> gets you there:</p>
<div class="break-out">
<pre><code class="language-css">input {
  --bg: var(--input-bg);
  background: var(--bg);
  color: contrast-color(var(--bg));
}

input::placeholder {
  color: color-mix(in oklch, contrast-color(var(--bg)) 50%, var(--bg));
}
</code></pre>
</div>
<p><code>50%</code> mix gives you a muted but legible placeholder that adapts automatically to whatever background the input sits on.</p>
<h3 id="theme-aware-contrast-with-light-dark">Theme-Aware Contrast With <code>light-dark()</code></h3>
<p>For apps that support system light/dark mode:</p>
<pre><code class="language-css">:root {
  color-scheme: light dark;
  --surface: light-dark(&#035;fff, #121212);
}

.component {
  background: var(--surface);
  color: contrast-color(var(--surface));
}
</code></pre>
<p>When the operating system switches to dark mode, <code>--surface</code> resolves to <code>#121212</code>, and <code>contrast-color()</code> returns white. No media queries, no JavaScript theme detection. The whole chain resolves natively.</p>
<div class="partners__lead-place"></div>
<h2 id="what-you-can-remove-from-your-bundle">What You Can Remove From Your Bundle</h2>
<p>The practical payoff: every one of these libraries existed because CSS couldn’t do contrast math. If you’re only using them for readable-text-color selection, you can pull them out of your runtime entirely:</p>
<table class="tablesaw break-out">
<thead>
<tr>
<th>Library</th>
<th>Size</th>
<th>What it did</th>
</tr>
</thead>
<tbody>
<tr>
<td>chroma-js</td>
<td>~14 kB</td>
<td>Color parsing, luminance calc, readable color selection</td>
</tr>
<tr>
<td>polished</td>
<td>~11 kB</td>
<td><code>readableColor()</code> for styled-components</td>
</tr>
<tr>
<td>tinycolor2</td>
<td>~5 kB</td>
<td>Hex parsing, WCAG contrast ratio math</td>
</tr>
</tbody>
</table>
<p>You might still need these for generating complex color scales, but the contrast-for-readability use case is now covered natively.</p>
<p>Beyond bundle size, there’s a performance angle that’s easy to overlook. Those JavaScript libraries don’t just cost you network bytes &mdash; they run on the main thread. Every time a theme changes or a component mounts with a dynamic background, your JS has to parse the color, compute luminance, decide black or white, and write the result back to the DOM. That’s main-thread work competing with layout, event handlers, and everything else your app is doing. <code>contrast-color()</code> moves all of that into the browser’s native style computation phase &mdash; heavily optimized C++ that runs before paint. For apps with lots of themed components, that’s a real difference in responsiveness.</p>
<p>There’s also a subtle bug that goes away: <strong>hydration flash</strong>. In React or Vue SSR apps, the server renders HTML without JavaScript. The client then hydrates, running JS to calculate contrast and inject the correct text color. For a brief window between initial paint and hydration, the text is either invisible or the wrong color. Moving contrast into CSS eliminates that entirely &mdash; the browser resolves the correct color during the initial paint, before JavaScript loads.</p>
<h2 id="what-we-used-to-do">What We Used To Do</h2>
<p>For context on what this replaces:</p>
<p><strong>Sass era.</strong> You’d write a function that checked <code>lightness($bg) &gt; 50%</code> and returned black or white at compile time. Worked for static themes. Completely useless for user-picked colors, CMS palettes, or dark mode, because the output was baked into the CSS file and could never change at runtime.</p>
<p><strong>The variable toggle hack.</strong> When CSS custom properties shipped, people got creative. GitHub used a version of this for their issue label picker &mdash; splitting colors into <code>--r</code>, <code>--g</code>, <code>--b</code> channels, calculating Rec.709 luminance inside <code>calc()</code>, multiplying by negative infinity, and clamping to <code>0</code> or <code>1</code>. It worked. It was also unreadable, unmaintainable, and would break silently if you got one parenthesis wrong. (Kevin Hamer’s <a href="https://css-tricks.com/approximating-contrast-color-with-other-css-features/">OKLCH-based approximation</a> is the most elegant version of this lineage &mdash; cleaner math, better perceptual alignment &mdash; but it’s still a workaround for a function that now ships natively.)</p>
<p><code>contrast-color()</code> replaces all of these approaches with a single function call. And because the spec lets browsers upgrade the underlying algorithm, your code won’t need to change if and when a successor to WCAG 2.x contrast math lands &mdash; whether that’s APCA or something else entirely.</p>
<hr />
<p></p>
<p>That <a href="https://almanac.httparchive.org/en/2025/accessibility#color-contrast">70% failure rate</a> was never about developers refusing to care about contrast. It was about the distance between caring and shipping &mdash; the library, the build step, the runtime calculation, the hydration flash, the one component someone forgot to wire up. Every gap in that chain was a spot where accessibility quietly dropped out.</p>
<p><code>contrast-color()</code> doesn’t make developers care more. It makes caring cost nothing.</p>
<div class="signature">
  <img src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" alt="Smashing Editorial" width="35" height="46" loading="lazy" class="lazyload" data-src="https://www.smashingmagazine.com/images/logo/logo--red.png"><br />
  <span>(gg, yk)</span>
</div>
</article>
]]></content:encoded>
					
					<wfw:commentRss>http://computercoursesonline.com/index.php/2026/05/28/algorithmic-theming-engines-building-self-correcting-color-systems-with-contrast-color/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Four Levels Of Customer Understanding</title>
		<link>http://computercoursesonline.com/index.php/2026/05/22/four-levels-of-customer-understanding/</link>
					<comments>http://computercoursesonline.com/index.php/2026/05/22/four-levels-of-customer-understanding/#respond</comments>
		
		<dc:creator><![CDATA[.]]></dc:creator>
		<pubDate>Fri, 22 May 2026 13:00:00 +0000</pubDate>
				<category><![CDATA[Ux]]></category>
		<guid isPermaLink="false">http://computercoursesonline.com/?p=1229</guid>

					<description><![CDATA[Four Levels Of Customer Understanding Four Levels Of Customer Understanding Vitaly Friedman 2026-05-22T13:00:00+00:00 2026-05-28T20:50:06+00:00 Many companies think they know fairly well what their users want and need, and how they make their decisions. Yet most of the time these are merely big assumptions and big hunches &#8212; with little real evidence to support them. In...]]></description>
										<content:encoded><![CDATA[<p>              <title>Four Levels Of Customer Understanding</title></p>
<article>
<header>
<h1>Four Levels Of Customer Understanding</h1>
<address>Vitaly Friedman</address>
<p>                  2026-05-22T13:00:00+00:00<br />
                  2026-05-28T20:50:06+00:00<br />
                </header>
<p>Many companies think they know fairly well what their users want and need, and how they make their decisions. Yet most of the time these are merely big assumptions and big hunches &mdash; with little real evidence to support them. In practice, <strong>obvious reasons</strong> might be true, but they rarely paint the full picture.</p>
<p>To understand our customers, we must triangulate across <a href="https://www.linkedin.com/posts/hannahshamji_8-months-ago-i-posted-an-original-framework-share-7307469501229420544-t19q/">four levels of customer understanding</a> by Hannah Shamji. It’s a useful way to think about the <strong>underlying reasons</strong> for user behavior, hidden motivations, and the complex layers of messy and noisy reality that are often overlooked. Let’s see how it works.</p>
<figure class="
  
  
  "></p>
<p>    <a href="https://www.linkedin.com/posts/hannahshamji_8-months-ago-i-posted-an-original-framework-share-7307469501229420544-t19q/"></p>
<p>    <img loading="lazy" width="800" height="706" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" alt="A diagram titled Understanding Customers: Four levels of customer understanding, showing four nested pink circles, each representing a level of understanding, with descriptions beside. From outermost to innermost: Level 1 (What they say), Level 2 (What they think or feel), Level 3 (What they do), and Level 4 (Why they do it)." class="lazyload" data-src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/four-levels-customer-understanding/1-four-levels-customer-understanding.jpg"></p>
<p>    </a><figcaption class="op-vertical-bottom">
      Four levels of customer understanding: what people say, think or feel, do, and why they do it. By <a href="https://www.linkedin.com/posts/hannahshamji_8-months-ago-i-posted-an-original-framework-share-7307469501229420544-t19q/">Hannah Shamji</a>. (<a href="https://files.smashing.media/articles/four-levels-customer-understanding/1-four-levels-customer-understanding.jpg">Large preview</a>)<br />
    </figcaption></figure>
<h2 id="don-t-ask-users-your-burning-questions">Don’t Ask Users Your Burning Questions</h2>
<p>To learn about customers, it might seem reasonable to <strong>ask people</strong> what they think and draw conclusions from it. But it’s <a href="https://www.linkedin.com/pulse/research-questions-interview-erika-hall/">rarely an effective way</a> to get actionable answers. In fact, as it turns out, what people <em>think</em>, <em>feel</em>, <em>say</em>, and <em>do</em> are often <a href="https://uxdesign.cc/dont-ask-users-your-burning-questions-34abedd92a0?sk=v2%2F8aba2177-2a6a-49f9-b9f7-6b173f2b92f8">very different things</a>.</p>
<figure class="
  
  
  "></p>
<p>    <a href="https://www.linkedin.com/feed/update/urn:li:activity:7140680577552117760/"></p>
<p>    <img loading="lazy" width="800" height="1001" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" alt="A list detailing six reasons people cancel subscriptions, categorized into voluntary and involuntary churn, with explanations for each." class="lazyload" data-src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/four-levels-customer-understanding/2-reasons-people-cancel-subscriptions.jpeg"></p>
<p>    </a><figcaption class="op-vertical-bottom">
      People don’t always cancel because they want to. Reasons for voluntary and involuntary customer churn. By <a href="https://www.linkedin.com/feed/update/urn:li:activity:7140680577552117760/">Emily Anderson</a>. (<a href="https://files.smashing.media/articles/four-levels-customer-understanding/2-reasons-people-cancel-subscriptions.jpeg">Large preview</a>)<br />
    </figcaption></figure>
<p>As Erika Hall <a href="https://medium.com/mule-design/on-surveys-5a73dda5e9a0">wrote</a>, asking a question directly is the <strong>worst way</strong> to get a true and useful answer to that question. We don’t always understand or are aware of our <strong>true motivations</strong>. We often apply our own context and interpretations to questions.</p>
<p>We also exaggerate (<em>a lot!</em>). We focus on <strong>edge cases</strong> and unrealistic scenarios, and we favor <strong>short-term goals</strong> over long-term goals. So if users say that they absolutely need to <em>compare products in a table</em>, it doesn’t mean that they couldn’t get to their underlying goal <a href="https://www.productchart.com/monitors/">without it</a>.</p>
<h2 id="possible-vs-probable">“Possible” vs. “Probable”</h2>
<p>Just to indicate how tricky listening to <em>words</em> alone is: even little <strong>nuances in words chosen</strong> matter. In practice, users are rarely precise in expressing their thoughts, and a good example is the distinction between <em>possible</em>, <em>plausible</em>, and <em>probable</em>, as <a href="https://www.linkedin.com/feed/update/urn:li:activity:7447580258918100992/">discovered by Thomas D&rsquo;hooge</a>.</p>
<figure class="
  
  
  "></p>
<p>    <a href="https://www.linkedin.com/feed/update/urn:li:activity:7447580258918100992/"></p>
<p>    <img loading="lazy" width="800" height="1000" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" alt="A chart showing overlaid density plots (ridge plots) that illustrate the numerical interpretation of various probability phrases" class="lazyload" data-src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/four-levels-customer-understanding/3-numerical-interpretation-various-probability-phrases.jpeg"></p>
<p>    </a><figcaption class="op-vertical-bottom">
      Numerical interpretation of probability phrases and their ranges. <a href="https://www.linkedin.com/feed/update/urn:li:activity:7447580258918100992/">Source</a>. (<a href="https://files.smashing.media/articles/four-levels-customer-understanding/3-numerical-interpretation-various-probability-phrases.jpeg">Large preview</a>)<br />
    </figcaption></figure>
<p>A <a href="https://jcom.sissa.it/article/pubid/JCOM_1902_2020_A03/">study on Dutch verbal probability terms</a> shows how unreliable the choice of words is. While extreme words have some agreement, terms like “possible,” “maybe,” “uncertain,” or “likely” lead to a wide spread of <strong>interpretations</strong>. So we shouldn’t rely on what people say, but rather try to go deeper.</p>
<h2 id="the-levels-of-understanding">The Levels Of Understanding</h2>
<p>To get a <strong>more realistic and less biased</strong> view of customers’ needs, we need to understand a broader picture across 4 levels:</p>
<figure class="
  
  
  "></p>
<p>    <a href="https://files.smashing.media/articles/four-levels-customer-understanding/4-four-levels-customer-understanding.jpeg"></p>
<p>    <img loading="lazy" width="800" height="800" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" alt="A diagram titled Understanding Customers: Four levels of customer understanding, showing four nested pink circles, each representing a level of understanding, with descriptions beside. From outermost to innermost: Level 1 (What they say), Level 2 (What they think or feel), Level 3 (What they do), and Level 4 (Why they do it)." class="lazyload" data-src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/four-levels-customer-understanding/4-four-levels-customer-understanding.jpeg"></p>
<p>    </a><figcaption class="op-vertical-bottom">
      Four levels of customer understanding: what people say, think or feel, do, and why they do it. By Hannah Shamji, visualized by Helio. (<a href="https://files.smashing.media/articles/four-levels-customer-understanding/4-four-levels-customer-understanding.jpeg">Large preview</a>)<br />
    </figcaption></figure>
<ul>
<li><strong>Level 1: “What they say”</strong><br />
Easier to collect, but mostly opinions, and most unreliable. People often explain their behavior through the lens of how they <em>perceive</em> it, or how they <em>want it to be perceived</em>, which isn’t always accurate. We shouldn’t rely too much on CRM data, surveys, or polls.</li>
<li><strong>Level 2: “What they think and feel”</strong><br />
Gives more context, but is still heavily shaped by memory and personal preferences. Good user research and interviews help us understand expectations and experiences.</li>
<li><strong>Level 3: “What they do”</strong><br />
We study actual behavior, actions taken or skipped, usage data, and analytics. We run <a href="https://www.linkedin.com/pulse/breaking-down-complexity-task-analysis-ux-vitaly-friedman-sjt4f/">task analysis and workflow analysis</a> to understand how people use the product.</li>
<li><strong>Level 4: “Why they do it”</strong><br />
We study <strong>underlying motivations</strong> and root causes, through observations of real workflows and in-depth interviews. Typically, it requires a trustworthy relationship with the user, repeat interviews, and task walkthroughs.</li>
</ul>
<p>Personally, I wouldn’t recommend <a href="https://www.linkedin.com/posts/vitalyfriedman_ux-design-metrics-share-7072585934147371008-XWXu">NPS</a> (<a href="https://www.linkedin.com/posts/vitalyfriedman_measure-ux-in-b2b-an-alternative-to-nps-share-7396872383355256833-eZQQ">alternative</a>). It’s worth noting that different levels might reveal conflicting or contradictory data. To get a better understanding, we need to <a href="https://www.linkedin.com/posts/vitalyfriedman_how-to-resolve-conflicting-data-and-ux-share-7340729861457534979-enqH">triangulate and reconcile data</a> with mixed-method research.</p>
<h2 id="capturing-emotions-and-nuance">Capturing Emotions And Nuance</h2>
<p>Emotions are always difficult to capture, but they are easier to spot once you observe people doing what they need to do <strong>without external influence</strong> or interruptions. The ability to positively impact users grows by moving <a href="https://www.nngroup.com/articles/sympathy-vs-empathy-ux/">from sympathy to empathy</a> or even compassion, as articulated by Sarah Gibbons.</p>
<figure class="
  
  
  "></p>
<p>    <a href="https://www.nngroup.com/articles/sympathy-vs-empathy-ux/"></p>
<p>    <img loading="lazy" width="800" height="643" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" alt="A diagram titled ‘Spectrum of Empathy’ with a vertical ‘Effort’ axis and a horizontal ‘Understanding &amp; Engagement’ axis. Four overlapping circles, increasing in size from left to right, represent Pity, Sympathy, Empathy, and Compassion, each with a corresponding phrase." class="lazyload" data-src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/four-levels-customer-understanding/5-spectrum-empathy.png"></p>
<p>    </a><figcaption class="op-vertical-bottom">
      Spectrum of Empathy: from pity to sympathy to empathy to compassion. By <a href="https://www.nngroup.com/articles/sympathy-vs-empathy-ux/">Sarah Gibbons</a>. (<a href="https://files.smashing.media/articles/four-levels-customer-understanding/5-spectrum-empathy.png">Large preview</a>)<br />
    </figcaption></figure>
<p>In the past, I was using <em>“speak-aloud” protocol</em> and asked users to walk me through their thought process as they were completing tasks. But it actually turns out to be quite disruptive. Because people are focused on speaking at the same time while solving a task, many <strong>emotions remain hidden</strong> or obscured by their language.</p>
<p>So, when conducting usability testing, <strong>I don’t ask users to speak</strong> through their experience. Instead, I observe where they tap or hover with the mouse, where their mouse circles without an action, where they scroll, and how long. Eventually, when a user confirms that they are done or that they are stuck, I ask questions.</p>
<figure class="
  
  
  "></p>
<p>    <a href="https://uca.edu/bewell/files/2020/11/Feelings-Wheel-Learn-How-to-Label-Your-Feelings.pdf"></p>
<p>    <img loading="lazy" width="800" height="800" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" alt="A colorful circular chart of emotions, starting with Happy, Sad, Angry, Fearful, Bad, Surprised, and Disgusted at the inner ring, branching out to more specific feelings in the outer rings." class="lazyload" data-src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/four-levels-customer-understanding/6-circular-chart-emotions.jpeg"></p>
<p>    </a><figcaption class="op-vertical-bottom">
      The Wheel of Emotions helps articulate emotions more precisely. (<a href="https://files.smashing.media/articles/four-levels-customer-understanding/6-circular-chart-emotions.jpeg">Large preview</a>)<br />
    </figcaption></figure>
<p>The <a href="https://uca.edu/bewell/files/2020/11/Feelings-Wheel-Learn-How-to-Label-Your-Feelings.pdf">Emotion Wheel</a> (<a href="https://feelingswheel.com">website</a>) by Geoffrey Roberts is a helpful little tool for better describing a range of emotions during user interviews or design sessions. It certainly needs refinement for <strong>product design needs</strong>, but it helps us get more precise about <strong>the sentiment</strong> customers or colleagues might be experiencing, moving beyond just “good” or “bad”.</p>
<p>One helpful trick is to use <strong>mirroring</strong> &mdash; repeating what a user has said, or ask the same question twice, just paraphrasing it. Or navigating the emotions wheel (see above) to better capture and understand the emotion.</p>
<p>These strategies help uncover some of the issues that perhaps didn’t come up in the first answer. That’s also when a user tends to add more useful context and details as they explain their confusion.</p>
<h2 id="emotions-aren-t-everything">Emotions Aren’t Everything</h2>
<p>Some people <a href="https://alinbuda.com/my-case-against-empathy/">strongly disagree</a>:</p>
<blockquote><p>“Our work is about others &mdash; their problems, their pain, their mess. Our job is to make sense of it and then do something about it. Not to emote or perform but to act on and solve it. There is a <strong>flawed belief</strong> that to build great things, you first need to emotionally fully absorb someone else’s experience.”</p>
<p>&mdash; Alin Buda</p></blockquote>
<figure class="
  
  
  "></p>
<p>    <a href="https://www.linkedin.com/posts/indiyoung_uxresearch-designresearch-inclusivedesign-share-7047371089408081920-qEsG/"></p>
<p>    <img loading="lazy" width="800" height="549" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" alt="A diagram categorizing potential harms of solutions into mild, serious, lasting, and systemic, with corresponding examples." class="lazyload" data-src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/four-levels-customer-understanding/7-diagram-categorizing-potential-harms-solutions.jpeg"></p>
<p>    </a><figcaption class="op-vertical-bottom">
      Different solutions can cause different levels of harm, which can be way more severe and impactful than emotional response. By <a href="https://www.linkedin.com/posts/indiyoung_uxresearch-designresearch-inclusivedesign-share-7047371089408081920-qEsG/">Indi Young</a>. (<a href="https://files.smashing.media/articles/four-levels-customer-understanding/7-diagram-categorizing-potential-harms-solutions.jpeg">Large preview</a>)<br />
    </figcaption></figure>
<p>I think that Alin brings up a very strong argument, and personally, I find it difficult to disagree with. However, I do see user’s emotional response as a <strong>signal</strong> of how well the product is working for them. How engaged or detached they are in their journey, how they react to <a href="https://www.chrbutler.com/the-art-secret-behind-all-great-design">aesthetics</a>, how confused or confident they are.</p>
<p>Ultimately, these are signals. To make a difference, we must go <strong>beyond emotions</strong> and explore what people actually do. Usually, this means relentlessly observing, diagnosing, and focusing on underlying user needs.</p>
<h2 id="observe-and-diagnose-don-t-validate">Observe And Diagnose, Don’t Validate</h2>
<p>Instead of asking, we need to <strong>observe</strong>. Usually, I focus on small things that make or break an experience. I see where users <strong>lose time</strong>, repeat actions, hover without clicking, or click and then go back. Pay attention to subtle cues like scratching their neck, raising eyebrows, or expressions of worry, joy, or confusion.</p>
<p>Many companies talk about “validation” through user testing, but often that means simply confirming existing assumptions. But we should instead <strong>diagnose existing behavior</strong> without preconceived notions or affiliations. We don’t validate &mdash; we actually research instead.</p>
<figure class="
  
  
  "></p>
<p>    <a href="https://www.linkedin.com/posts/nikkianderson-ux_the-moment-someone-says-lets-validate-this-share-7371206500830199809-nvB0/"></p>
<p>    <img loading="lazy" width="800" height="1002" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" alt="A diagram categorizing potential harms of solutions into mild, serious, lasting, and systemic, with corresponding examples." class="lazyload" data-src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/four-levels-customer-understanding/8-words-instead-validate.jpg"></p>
<p>    </a><figcaption class="op-vertical-bottom">
      Words to use instead of “validate”: research, understand, investigate, assess, evaluate, examine, learn. By <a href="https://www.linkedin.com/posts/nikkianderson-ux_the-moment-someone-says-lets-validate-this-share-7371206500830199809-nvB0/">Nikki Anderson</a>. (<a href="https://files.smashing.media/articles/four-levels-customer-understanding/8-words-instead-validate.jpg">Large preview</a>)<br />
    </figcaption></figure>
<p>That research means not just understanding <strong>customers’ real motivations</strong>, but also risks, doubts, concerns, worries, and perhaps even <a href="https://www.linkedin.com/posts/indiyoung_uxresearch-designresearch-inclusivedesign-share-7047371089408081920-qEsG/">harms</a>.</p>
<p>The only way to get there is by building a sincere, honest, and <strong>trustworthy relationship</strong> &mdash; one that feels right and resonates deeply. When customers truly care and want to help, getting to a real understanding becomes much, much easier.</p>
<h2 id="practical-ways-to-uncover-user-needs">Practical Ways To Uncover User Needs</h2>
<p>We don’t need expensive tools to uncover user needs. David Travis provides a <a href="https://medium.com/@userfocus/60-ways-to-understand-user-needs-that-arent-focus-groups-or-surveys-8510e13b3408">fantastic overview</a> of helpful strategies to do just that. Here are some initiatives to spread the word about real user’s struggles or gain a <strong>deeper understanding</strong> of user needs:</p>
<ul>
<li><strong>Exposure hours</strong>, when every employee must be <a href="https://archive.uie.com/brainsparks/2011/12/19/exposure-hours-drive-ux-innovation/">exposed to their customers</a> for at least 2 hours every 6&ndash;12 weeks.</li>
<li><strong>Live UX testing</strong>, where we invite everyone in the company to join and observe.</li>
<li><strong>Co-design with users</strong>, where we show new features and ask users to rank them.</li>
<li><strong>Helpdesk insights</strong>, where we ask for frequent complaints and questions from the support every 3&ndash;6 months.</li>
<li><strong>Listening in</strong>, where we tune in on a customer service call, web chat, or eavesdrop where users hang out.</li>
</ul>
<p>The core idea here is that you don’t need extensive and expensive tools to uncover user needs. You need to create spaces where <strong>customers’ struggles can be exposed</strong> and make these struggles visible across the entire company.</p>
<p>It can be <strong>short video clips</strong> of user sessions or a monthly newsletter with what we learned this month. Making these pain points visible can rally everyone from marketing to engineering to keep users’ struggles at the back of their minds.</p>
<h2 id="wrapping-up">Wrapping Up</h2>
<p>To make an impact, we must go way <strong>beyond user feedback</strong>. It’s never enough to listen to surveys &mdash; we must <strong>observe customers’ actual behaviors</strong> and build relationships to truly understand their goals and their motivations.</p>
<p>And most importantly, we need to understand <strong>what questions</strong> we actually want to have answered. Not what “validation” we need to move on with the project, but what we don’t know and what we need to <strong>research</strong>.</p>
<p>Without it, everything else is merely hunches and assumptions &mdash; and often wrong and expensive ones.</p>
<h2 id="meet-measure-ux-amp-design-impact">Meet “Measure UX &amp; Design Impact”</h2>
<p>Meet <a href="https://measure-ux.com"><strong>Measure UX &amp; Design Impact</strong></a>, Vitaly’s practical guide <strong>for designers and UX leads</strong> on how to track and visualize the incredible <strong>impact</strong> of your UX work on business &mdash; with a <a href="https://smashingconf.com/online-workshops/workshops/vitaly-friedman-impact-design/">live UX training</a> later this year. <a href="https://measure-ux.com/">Jump to details</a>.</p>
<figure class="break-out" style="margin-bottom:0;padding-bottom:0">
    <a href="https://measure-ux.com/" title="How To Measure UX and Design Impact, with Vitaly Friedman"><br />
    <img loading="lazy" width="900" height="466" style="border-radius: 11px" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" alt="How to Measure UX and Design Impact, with Vitaly Friedman." class="lazyload" data-src="https://computercoursesonline.com/wp-content/uploads/2025/04/measure-ux-and-design-impact-course.png"><br />
    </a><figcaption class="op-vertical-bottom">Meet <a href="https://measure-ux.com/">Measure UX and Design Impact</a>, a practical video course for designers and UX leads.</figcaption></figure>
<div class="book-cta__inverted">
<div class="book-cta" data-handler="ContentTabs" data-mq="(max-width: 480px)">
<nav class="content-tabs content-tabs--books">
<ul>
<li class="content-tab"><a href="#"><button class="btn btn--small btn--white btn--white--bordered"><br />
Video + UX Training</button></a></li>
<li class="content-tab"><a href="#"><button class="btn btn--small btn--white btn--white--bordered">Video only</button></a></li>
</ul>
</nav>
<div class="book-cta__col book-cta__hardcover content-tab--content">
<h3 class="book-cta__title"><span>Video + UX Training</span></h3>
<p><span class="book-cta__price"><span><span class=""><span class="currency-sign">$</span>&nbsp;<span>495<sup class="sup">.00</sup></span></span> <span class="book-cta__price--old"><span class="currency-sign">$</span>&nbsp;<span>799<sup class="sup">.00</sup></span></span></span></span><br />
<a href="https://smart-interface-design-patterns.thinkific.com/enroll/3081832?price_id=3951439" class="btn btn--full btn--medium btn--text-shadow"><br />
Get Video + UX Training</p>
<div></div>
<p></a></p>
<p class="book-cta__desc">25 video lessons (8h) + <a href="https://smashingconf.com/online-workshops/workshops/vitaly-friedman-impact-design/">Live UX Training</a>.<br />100 days money-back-guarantee.</p>
</div>
<div class="book-cta__col book-cta__ebook content-tab--content">
<h3 class="book-cta__title"><span>Video only</span></h3>
<div data-audience="anonymous free supporter" data-remove="true"><span class="book-cta__price" data-handler="PriceTag"><span><span class=""><span class="currency-sign">$</span>&nbsp;<span>250<sup class="sup">.00</sup></span></span><span class="book-cta__price--old"><span class="currency-sign">$</span>&nbsp;<span>350<sup class="sup">.00</sup></span></span></span></span></div>
<p><a href="https://smart-interface-design-patterns.thinkific.com/enroll/3081832?price_id=3950630" class="btn btn--full btn--medium btn--text-shadow"><br />
Get the video course</p>
<div></div>
<p></a></p>
<p class="book-cta__desc" data-audience="anonymous free supporter" data-remove="true">25 video lessons (8h). Updated yearly.<br />Also available as a <a href="https://smart-interface-design-patterns.thinkific.com/enroll/3570306?price_id=4503439">UX Bundle with 3 video courses.</a></p>
</div>
<p><span></span></div>
</div>
<h2 id="useful-resources">Useful Resources</h2>
<ul>
<li><a href="https://www.linkedin.com/posts/hannahshamji_8-months-ago-i-posted-an-original-framework-share-7307469501229420544-t19q/">Four Levels of Customer Understanding</a>, by Hannah Shamji</li>
<li><a href="https://medium.com/@userfocus/60-ways-to-understand-user-needs-that-arent-focus-groups-or-surveys-8510e13b3408">60 Ways To Understand User Needs</a>, by David Travis</li>
<li><a href="https://uca.edu/bewell/files/2020/11/Feelings-Wheel-Learn-How-to-Label-Your-Feelings.pdf">Emotion Wheel Toolkit (PNG)</a>, by Geoffrey Roberts</li>
<li><a href="https://uca.edu/bewell/files/2020/11/Feelings-Wheel-Learn-How-to-Label-Your-Feelings.pdf">Feelings Wheel PDF</a></li>
<li><a href="https://feelingswheel.com">Feelings Wheel Online</a></li>
<li><a href="https://alinbuda.com/my-case-against-empathy/">My Case Against Empathy</a>, by Alin Buda</li>
<li><a href="https://www.linkedin.com/feed/update/urn:li:activity:7447580258918100992/">Possible vs. Probable</a>, by Thomas D’hooge</li>
<li><a href="https://jcom.sissa.it/article/pubid/JCOM_1902_2020_A03/">Communicating probability: a multinational study of the interpretation of verbal probability terms</a>, by Maarten C. de Vries, Marjolijn L. de Boer, and Martine Bouman.</li>
</ul>
<h3 id="useful-books">Useful Books</h3>
<ul>
<li><em>Deploy Empathy: A practical guide to interviewing customers</em>, by Michele Hansen</li>
<li><em>Humankind</em>, by Rutger Bregman</li>
</ul>
<div class="signature">
  <img src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" alt="Smashing Editorial" width="35" height="46" loading="lazy" class="lazyload" data-src="https://www.smashingmagazine.com/images/logo/logo--red.png"><br />
  <span>(yk)</span>
</div>
</article>
]]></content:encoded>
					
					<wfw:commentRss>http://computercoursesonline.com/index.php/2026/05/22/four-levels-of-customer-understanding/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Advanced Tree Counting: Mathematical Layouts With `sibling-index()` And `sibling-count()`</title>
		<link>http://computercoursesonline.com/index.php/2026/05/21/advanced-tree-counting-mathematical-layouts-with-sibling-index-and-sibling-count/</link>
					<comments>http://computercoursesonline.com/index.php/2026/05/21/advanced-tree-counting-mathematical-layouts-with-sibling-index-and-sibling-count/#respond</comments>
		
		<dc:creator><![CDATA[.]]></dc:creator>
		<pubDate>Thu, 21 May 2026 08:00:00 +0000</pubDate>
				<category><![CDATA[Css]]></category>
		<guid isPermaLink="false">http://computercoursesonline.com/?p=1223</guid>

					<description><![CDATA[Advanced Tree Counting: Mathematical Layouts With `sibling-index()` And `sibling-count()` Advanced Tree Counting: Mathematical Layouts With `sibling-index()` And `sibling-count()` Durgesh Pawar 2026-05-21T08:00:00+00:00 2026-05-21T20:44:30+00:00 You know that thing where you have a grid of cards, and you want them to fade in one after another? That staggered cascade effect. Looks great. Should be simple. And yet every...]]></description>
										<content:encoded><![CDATA[<p>              <title>Advanced Tree Counting: Mathematical Layouts With `sibling-index()` And `sibling-count()`</title></p>
<article>
<header>
<h1>Advanced Tree Counting: Mathematical Layouts With `sibling-index()` And `sibling-count()`</h1>
<address>Durgesh Pawar</address>
<p>                  2026-05-21T08:00:00+00:00<br />
                  2026-05-21T20:44:30+00:00<br />
                </header>
<p>You know that thing where you have a grid of cards, and you want them to fade in one after another? That staggered cascade effect. Looks great. Should be simple. And yet every time I’ve built it, the implementation has made me feel like I’m doing something fundamentally stupid.</p>
<figure class="break-out">
<p data-height="480" data-theme-id="light" data-slug-hash="zxowBog" data-user="smashingmag" data-default-tab="result" class="codepen">See the Pen [Dynamic Staggered Animations with CSS sibling-index() [forked]](https://codepen.io/smashingmag/pen/zxowBog) by <a href="https://codepen.io/durgeshpawar">Durgesh</a>.</p><figcaption>See the Pen <a href="https://codepen.io/smashingmag/pen/zxowBog">Dynamic Staggered Animations with CSS sibling-index() [forked]</a> by <a href="https://codepen.io/durgeshpawar">Durgesh</a>.</figcaption></figure>
<p>Because the options were always the same. Say you want staggered animation delays on a list of 10 items. You either wrote a Sass loop that spat out a dozen <code>:nth-child()</code> rules, each one hardcoding a <code>--index</code> variable for that specific position:</p>
<pre><code class="language-css">/&#042; One rule per item. Hope the list never grows. &#042;/
li:nth-child(1) { --idx: 1; }
li:nth-child(2) { --idx: 2; }
li:nth-child(3) { --idx: 3; }
/&#042; ... eight more of these ... &#042;/
li:nth-child(10) { --idx: 10; }

li {
  animation-delay: calc(var(--idx) &#042; 100ms);
}
</code></pre>
<p>Ten items. Ten rules. If the list grows to 50? You cap it and hope for the best, or set up a Sass loop that generates hundreds of selectors at build time. Engineers like Roman Komarov have come up with <a href="https://kizu.dev/tree-counting-and-random/">O(âˆšN) strategies</a> &mdash; legitimately clever stuff &mdash; but you still end up with 63 rules to cover 1,023 elements.</p>
<p>Or you looped through elements in JavaScript and set inline styles. <code>style=&quot;--index: 3&quot;</code>. Right there in the DOM. Works fine. Also spreads layout concerns across your scripts and quietly breaks six months later when someone refactors the component without realizing the CSS depends on a JavaScript-injected variable.</p>
<p>Both approaches have always bugged me for the same reason: <strong>you’re telling the browser something it already knows</strong>. The browser <em>built</em> the DOM tree. It knows which element is the third child. It has the data. <strong>CSS just couldn’t access it.</strong></p>
<p>Well, now it can:</p>
<pre><code class="language-css">li {
  animation-delay: calc(sibling-index() * 100ms);
}
</code></pre>
<p>One line. Works for 5 items or 5,000. No event listeners. No mutation observers. No re-renders.</p>
<p><code>sibling-index()</code> and <code>sibling-count()</code> are part of the <a href="https://drafts.csswg.org/css-values-5/#tree-counting">CSS Values and Units Module Level 5</a> spec (Section 9, if you’re the type who reads W3C drafts for fun). The proposal was <a href="https://github.com/w3c/csswg-drafts/issues/4559">approved via CSSWG issue #4559</a> after substantial discussion. The functions themselves take no arguments &mdash; you just use them.</p>
<ul>
<li><strong><code>sibling-index()</code></strong> gives you the 1-based position of an element among its parent’s children. First child returns <code>1</code>. Fifth child returns <code>5</code>. It only counts element nodes &mdash; text nodes, comments, and whitespace are all invisible to it.</li>
<li><strong><code>sibling-count()</code></strong> gives you the total number of element children the parent has. Basically, the CSS equivalent of <code>element.parentElement.children.length</code> in JavaScript, but available in your stylesheet.</li>
</ul>
<p>Both functions resolve to <code>&lt;integer&gt;</code> &mdash; not <code>&lt;string&gt;</code>, an actual number. That means you can throw them into <code>calc()</code>, <code>min()</code>, <code>max()</code>, <code>round()</code>, <code>mod()</code>, trigonometric stuff like <a href="https://web.dev/articles/css-trig-functions"><code>sin()</code> and <code>cos()</code></a>. When you write <code>calc(sibling-index() * 100ms)</code>, CSS handles the type coercion and spits out a valid <code>&lt;time&gt;</code> value. No tricks needed. Compare that with <code>counter()</code>, which returns a string and can only live inside <code>content</code> on pseudo-elements &mdash; it’s a different thing entirely.</p>
<p>One clarification that trips people up: <code>:nth-child()</code> is a <em>selector</em>. It picks elements. It doesn’t produce a value. You can’t write <code>calc(:nth-child() * 10px)</code> &mdash; that’s not valid CSS. <code>sibling-index()</code> does the opposite. It sits inside your declarations and gives you a number you can calculate with. They solve different problems, and until now we’ve been duct-taping <code>:nth-child()</code> into a role it was never designed for.</p>
<h2 id="patterns-worth-stealing">Patterns Worth Stealing</h2>
<p>Once it clicks that these are just integers, ideas come fast.</p>
<div data-audience="non-subscriber" data-remove="true" class="feature-panel-container">
<aside class="feature-panel">
<div class="feature-panel-left-col">
<div class="feature-panel-description">
<p>Meet <strong><a data-instant href="https://www.smashingconf.com/online-workshops/">Smashing Workshops</a></strong> on <strong>front-end, design &amp; UX</strong>, with practical takeaways, live sessions, <strong>video recordings</strong> and a friendly Q&amp;A. With Brad Frost, Stéph Walter and <a href="https://smashingconf.com/online-workshops/workshops">so many others</a>.</p>
<p><a data-instant href="smashing-workshops" class="btn btn--green btn--large">Jump to the workshops&nbsp;↬</a></div>
</div>
<div class="feature-panel-right-col"><a data-instant href="smashing-workshops" class="feature-panel-image-link"></p>
<div class="feature-panel-image">
<img loading="lazy" class="feature-panel-image-img lazyload" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" alt="Feature Panel" width="257" height="355" data-src="/images/smashing-cat/cat-scubadiving-panel.svg"></p>
</div>
<p></a>
</div>
</aside>
</div>
<h3 id="reverse-stagger">Reverse Stagger</h3>
<p>Want the last item to animate first? Subtract:</p>
<pre><code class="language-css">.card {
  animation: fade-in 0.4s ease both;
  animation-delay: calc((sibling-count() - sibling-index()) &#042; 80ms);
}
</code></pre>
<p>Last child gets <code>(N - N) * 80ms = 0ms</code> &mdash; it fires instantly. First child gets <code>(N - 1) * 80ms</code>. The animation kicks off the moment the page loads instead of pausing for an awkward beat.</p>
<h3 id="automatic-equal-widths">Automatic Equal Widths</h3>
<p>Stop counting children manually to set percentages:</p>
<pre><code class="language-css">.tab {
  width: calc(100% / sibling-count());
}
</code></pre>
<p>Five tabs? 20% each. Add a sixth? 16.66%. Remove two? 25%. No media queries, no resize observers, no JavaScript at all.</p>
<p>That said, you can imagine a scenario where too many items make for really narrow tabs, at which point you might want to go with something else, perhaps a Flexbox wrapping solution.</p>
<h3 id="hue-distribution">Hue Distribution</h3>
<p>Spread colors evenly across the color wheel:</p>
<pre><code class="language-css">.swatch {
  background-color: hsl(
    calc((360deg / sibling-count()) &#042; sibling-index()) 70% 50%
  );
}
</code></pre>
<p>Three items get hues 120Â° apart. Twelve items get 30Â° increments. The palette adapts to whatever’s in the DOM, which is the kind of thing you’d normally reach for a JavaScript color library to do.</p>
<h3 id="circular-menus">Circular Menus</h3>
<p>Distributing items in a circle used to mean calculating sine and cosine in JavaScript. CSS now has <a href="https://web.dev/articles/css-trig-functions"><code>sin()</code> and <code>cos()</code> natively</a> (Juan Diego RodrÃ­guez has a great <a href="https://css-tricks.com/the-most-hated-css-feature-cos-and-sin/">practical walkthrough</a> of these on CSS-Tricks), and combined with tree-counting, the whole thing collapses into pure CSS:</p>
<pre><code class="language-css">.radial-item {
  --angle: calc((360deg / sibling-count()) &#042; sibling-index());
  --radius: 120px;

  position: absolute;
  left: calc(50% + var(--radius) &#042; cos(var(--angle)));
  top: calc(50% + var(--radius) &#042; sin(var(--angle)));
  transform: rotate(calc(var(--angle) &#042; -1));
}
</code></pre>
<p>Six items? Hexagon. Eight? Octagon. Add or remove items, and the layout recalculates. No JavaScript computing coordinates.</p>
<h3 id="z-index-stacking">Z-Index Stacking</h3>
<p>Building a card fan? One line:</p>
<pre><code class="language-css">.card {
  z-index: calc(sibling-count() - sibling-index());
}
</code></pre>
<p>First card stacks highest, last card gets 0. Flip the math if you want the reverse.</p>
<div class="partners__lead-place"></div>
<h2 id="the-gotchas">The Gotchas</h2>
<p>These are worth going through individually because they’re not obvious from the spec.</p>
<h3 id="shadow-dom-scoping">Shadow DOM Scoping</h3>
<p><code>sibling-index()</code> and <code>sibling-count()</code> operate on the DOM tree, not the flattened visual tree. This distinction will absolutely bite you with <a href="https://www.smashingmagazine.com/2025/07/web-components-working-with-shadow-dom/">Web Components</a>.</p>
<p>Say you have a custom element with this shadow DOM:</p>
<pre><code class="language-html">&lt;section&gt;
  &lt;slot&gt;&lt;/slot&gt;
  &lt;div class="internal"&gt;&lt;/div&gt;
&lt;/section&gt;
</code></pre>
<p>If you style <code>.internal</code> with <code>sibling-index()</code>, it returns <code>2</code>. Always. Even if the <code>&lt;slot&gt;</code> projects 300 elements. The function sees two children of <code>&lt;section&gt;</code> in the shadow tree &mdash; the <code>&lt;slot&gt;</code> and the <code>.internal</code> div. Projected light DOM content doesn’t exist as far as the count is concerned.</p>
<p>There’s also a security thing going on here. If a light DOM stylesheet tries to reach into a component via <code>::part()</code> and use <code>sibling-index()</code>, the browser returns <code>0</code>. Flat zero. It’s a deliberate wall to prevent external CSS from probing the internal structure of third-party components. Honestly, I think that’s the right call.</p>
<h3 id="pseudo-elements-don-t-count">Pseudo-Elements Don’t Count</h3>
<p><code>::before</code> and <code>::after</code> aren’t siblings. They don’t show up in <code>sibling-count()</code> and they don’t have their own <code>sibling-index()</code>. But &mdash; and this is the part that’ll save you a debugging session &mdash; you <em>can</em> use these functions inside pseudo-element declarations. When you write <code>#target::before { width: calc(sibling-index() * 10px); }</code>, it evaluates <code>sibling-index()</code> against <code>#target</code>, not against the pseudo-element. The pseudo-element isn’t a real node, so the function traces back to its originating element. Same story with <code>::slotted(*)::before</code> &mdash; it checks the slotted element’s index in the light DOM.</p>
<h3 id="display-none-still-counts"><code>display: none</code> Still Counts</h3>
<p>This one burned me. Elements with <code>display: none</code> vanish from the layout tree. They take up no space. Screen readers don’t see them. But they’re still in the DOM.</p>
<p>Since <code>sibling-index()</code> reads the DOM tree, not the layout tree, hidden elements get counted:</p>
<pre><code class="language-html">&lt;ul&gt;
  &lt;!-- sibling-index() = 1 --&gt;
  &lt;li&gt;Apple&lt;/li&gt;
  &lt;!-- sibling-index() = 2, invisible --&gt;
  &lt;li style="display:none"&gt;Banana&lt;/li&gt;
  &lt;!-- sibling-index() = 3, NOT 2 --&gt;
  &lt;li&gt;Cherry&lt;/li&gt;       
&lt;/ul&gt;
</code></pre>
<p>Cherry is <code>3</code>, not <code>2</code>. The hidden banana still holds its spot.</p>
<p>This doesn’t matter for most layouts. But if you’re building something like a search filter that hides non-matching items with <code>display: none</code>, your staggered animations and circular layouts will develop gaps. The visible items keep their original, non-sequential indexes. For anything that depends on continuous counting &mdash; radial menus, proportional widths &mdash; you’ll need to actually remove filtered nodes from the DOM instead of just hiding them. Or fall back to JavaScript-managed indexes.</p>
<p><strong>Note:</strong> <code>visibility: hidden</code> and <code>opacity: 0</code> count too, but that feels more intuitive since those elements still take up space. <code>display: none</code> is the sneaky one because the element disappears visually but still occupies a DOM slot.</p>
<h4 id="custom-properties-evaluate-immediately">Custom Properties Evaluate Immediately</h4>
<p>This is subtle. If you try to centralize the index on a parent:</p>
<pre><code class="language-css">.parent {
  --idx: sibling-index();
}
</code></pre>
<p>&hellip;that <code>--idx</code> resolves right there on <code>.parent</code>. It grabs the parent’s own sibling index, locks it to that number, and every child inherits that single fixed value. Every child gets the same number. Almost certainly not what you want or expect.</p>
<p>The fix is simple &mdash; put the function on the elements that need it:</p>
<pre><code class="language-css">.child {
  --idx: sibling-index();
  animation-delay: calc(var(--idx) &#042; 100ms);
}
</code></pre>
<p>The CSSWG has discussed an <code>inherits: declaration</code> addition to <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/@property"><code>@property</code></a> that could theoretically fix this. If you haven’t used <code>@property</code>, it lets you define a custom property’s type, initial value, and inheritance behavior &mdash; way more control than a raw <code>--variable</code>. But the <code>inherits: declaration</code> idea is still in early CSSWG discussion, not baked into any spec draft. It could be years before it lands &mdash; or it might not land at all. Even with <code>@property</code> today, there’s no mechanism to say <em>“don’t evaluate yet, wait for the child.”</em> So for now, just apply directly.</p>
<h3 id="performance-at-scale">Performance at Scale</h3>
<p>Changing the DOM &mdash; i.e., adding, removing, reordering children &mdash; triggers style recalculation for affected siblings. The browser handles this during the cascade phase (before layout and paint), so it’s faster than the old approach of looping in JavaScript and stamping inline styles.</p>
<p>But there’s a real cost if you push it. Inserting an element at the beginning of a container with 10,000 children forces the engine to recalculate the sibling index for all 10,000 elements after it. For normal stuff &mdash; navigation, card grids, tab bars &mdash; you’ll never notice. For a live stock ticker or an infinite-scroll feed with thousands of nodes constantly churning, keep using JavaScript-managed indexes inside your virtualization window. These functions are fast. They’re not zero-cost.</p>
<h2 id="browser-support">Browser Support</h2>
<p>As of writing, <a href="https://developer.chrome.com/blog/new-in-chrome-138">Chrome/Edge 138</a> shipped these functions in stable releases (June 2025), and <a href="https://webkit.org/blog/17640/webkit-features-for-safari-26-2/">Safari 26.2</a> followed. Firefox hasn’t shipped them in stable yet, but Mozilla’s <a href="https://github.com/mozilla/standards-positions/issues/1194">spec position is positive</a> and implementation work is actively underway &mdash; tracked under <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=1953973">Bugzilla issue #1953973</a>. Check <a href="https://caniuse.com/wf-sibling-count">caniuse</a> for the latest before you ship.</p>
<p>Chrome and Safari together cover roughly 75&ndash;80% of global traffic. That’s a strong majority, but Firefox’s absence means you still need a fallback.</p>
<p>For shipping today, <code>@supports</code> is your friend:</p>
<pre><code class="language-css">/&#042; Baseline that works everywhere &#042;/
.item {
  width: 25%;
  animation-delay: 0ms;
}

/&#042; Progressively enhance where supported &#042;/
@supports (z-index: sibling-index()) {
  .item {
    width: calc(100% / sibling-count());
    animation-delay: calc(sibling-index() &#042; 80ms);
  }
}
</code></pre>
<p>Static fallback for Firefox. Mathematical layout for everyone else. Nobody gets a broken page.</p>
<blockquote><p><strong>On polyfills:</strong></p>
<p>A JavaScript polyfill that loops through siblings and sets inline styles is the exact thing these functions exist to replace. But that doesn’t mean you’re stuck with hardcoded fallback values either. Juan Diego RodrÃ­guez wrote a solid piece on “<a href="https://css-tricks.com/how-to-wait-for-the-sibling-count-and-sibling-index-functions/">How to Wait for the <code>sibling-count()</code> and <code>sibling-index()</code> Functions</a>” that lays out the right model for progressive enhancement until native support hits Baseline. His approach uses existing CSS techniques (like Roman Komarov’s <a href="https://kizu.dev/tree-counting-and-random/">counting hacks</a>) as a bridge rather than a full JavaScript polyfill. Worth reading if you need to ship something production-ready today while Firefox catches up.</p></blockquote>
<div class="partners__lead-place"></div>
<h2 id="accessibility-notes">Accessibility Notes</h2>
<p>This needs saying because it’s easy to get excited and forget: <strong>these functions are purely visual</strong>. They change how things look. They don’t change what things <em>mean</em>.</p>
<p>If you use <code>sibling-index()</code> math to visually reorder a list &mdash; via <code>order</code> or grid placement &mdash; a screen reader still reads the DOM in source order. Keyboard tab order follows the DOM, too. Visual layout and semantic structure will contradict each other, and that’s an accessibility failure.</p>
<p>For interactive components like data grids, radial menus, or custom listboxes that lean on tree-counting for layout, you still need JavaScript to sync ARIA attributes. <code>aria-posinset</code> and <code>aria-setsize</code> have no idea what CSS is calculating. If your CSS says <em>“this is visually item 3 of 7”</em> but ARIA says something different (or nothing), assistive technology users get a broken experience.</p>
<p>On the debugging side, recent versions of Chrome DevTools let you inspect computed <code>sibling-index()</code> and <code>sibling-count()</code> values directly in the Elements panel, which helps when the math isn’t doing what you expect.</p>
<figure class="
  
    break-out article__image
  
  
  "></p>
<p>    <a href="https://files.smashing.media/articles/mathematical-layouts-sibling-index-sibling-count/sibling-index-devtools.jpeg"></p>
<p>    <img loading="lazy" width="800" height="400" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" alt="sibling-index devtools" class="lazyload" data-src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/mathematical-layouts-sibling-index-sibling-count/sibling-index-devtools.jpeg"></p>
<p>    </a><figcaption class="op-vertical-bottom">
      (<a href="https://files.smashing.media/articles/mathematical-layouts-sibling-index-sibling-count/sibling-index-devtools.jpeg">Large preview</a>)<br />
    </figcaption></figure>
<h2 id="what-s-coming">What’s Coming</h2>
<p>The current spec only counts <em>all</em> element siblings. But the CSSWG has documented a planned extension in <a href="https://github.com/w3c/csswg-drafts/issues/9572">issue #9572</a>: an <code>of &lt;selector&gt;</code> argument, matching what <code>:nth-child()</code> already supports.</p>
<p>Something like <code>sibling-index(of .active)</code> would let you count only siblings matching a specific selector. An element that’s the eighth child overall but the third <code>.active</code> child would return <code>3</code>. For dynamic UIs where you’re filtering or toggling visibility, that would keep the index sequential without requiring DOM manipulation.</p>
<p>There’s also been CSSWG discussion around <a href="https://github.com/w3c/csswg-drafts/issues/11068"><code>children-count()</code></a> and <a href="https://github.com/w3c/csswg-drafts/issues/11069"><code>descendant-count()</code></a> functions &mdash; the first would tell you how many children an element has (useful for parent-driven layouts), the second would count all descendants recursively. Both are still at the proposal stage, but they’d round out the tree-counting story: <code>sibling-index()</code> and <code>sibling-count()</code> give you the horizontal view (where am I among my peers?), while <code>children-count()</code> and <code>descendant-count()</code> would give you the vertical view (what’s below me?).</p>
<p><em>That feeling I mentioned at the top &mdash; writing ten <code>:nth-child()</code> rules for a staggered animation and wondering if you’re missing something obvious? You weren’t. The obvious thing just didn’t exist yet.</em></p>
<div class="signature">
  <img src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" alt="Smashing Editorial" width="35" height="46" loading="lazy" class="lazyload" data-src="https://www.smashingmagazine.com/images/logo/logo--red.png"><br />
  <span>(gg, yk)</span>
</div>
</article>
]]></content:encoded>
					
					<wfw:commentRss>http://computercoursesonline.com/index.php/2026/05/21/advanced-tree-counting-mathematical-layouts-with-sibling-index-and-sibling-count/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Ten Data-Backed Truths Of User Experience ROI</title>
		<link>http://computercoursesonline.com/index.php/2026/05/15/ten-data-backed-truths-of-user-experience-roi/</link>
					<comments>http://computercoursesonline.com/index.php/2026/05/15/ten-data-backed-truths-of-user-experience-roi/#respond</comments>
		
		<dc:creator><![CDATA[.]]></dc:creator>
		<pubDate>Fri, 15 May 2026 10:00:00 +0000</pubDate>
				<category><![CDATA[Ux]]></category>
		<guid isPermaLink="false">http://computercoursesonline.com/?p=1225</guid>

					<description><![CDATA[Ten Data-Backed Truths Of User Experience ROI Ten Data-Backed Truths Of User Experience ROI Carrie Webster 2026-05-15T10:00:00+00:00 2026-05-21T20:44:30+00:00 In the high-stakes economy of today, the cost of a friction-heavy interface is no longer just “lost clicks”, but potentially millions in wasted engineering spend and lost business value. As a veteran UX designer who has helped...]]></description>
										<content:encoded><![CDATA[<p>              <title>Ten Data-Backed Truths Of User Experience ROI</title></p>
<article>
<header>
<h1>Ten Data-Backed Truths Of User Experience ROI</h1>
<address>Carrie Webster</address>
<p>                  2026-05-15T10:00:00+00:00<br />
                  2026-05-21T20:44:30+00:00<br />
                </header>
<p>In the high-stakes economy of today, the cost of a friction-heavy interface is no longer just “lost clicks”, but potentially millions in wasted engineering spend and lost business value. As a veteran UX designer who has helped build digital products since the early mobile-first era, I’ve watched business leaders shift from viewing design as a “cosmetic preference” to recognising that user experience is actually the primary engine of business survival.</p>
<p>A UX design role is as much about <strong>research</strong> and <strong>analytics</strong> as it is about pixels, and I believe that hard data is the only tool powerful enough to bridge the gap between design and the boardroom. Facts don’t just advocate for the user; they prove that UX is a <strong>non-negotiable requirement</strong> for a healthy bottom line. Even in the rooms where decisions are made, UX is frequently undervalued as a ‘visual’ role. I’ve learned that the most effective way to dismantle this myth is through <strong>data</strong>.</p>
<p>The following ten facts represent the current reality of the digital world. These are not just “design tips”; they are the clinical, data-backed pillars for financial growth in a saturated market. Some of these facts are also commonly used by designers as best practices.</p>
<p>For example, I once led a B2C mobile design project, where I was able to strip 1.2 seconds off the mobile load time by reducing and removing some of the visual assets. The result was an immediate 12% lift in completed transactions, proving that in UX, every tenth of a second is a direct lever for revenue.</p>
<h2 id="1-fixing-issues-in-the-design-phase-is-100-times-cheaper">1. Fixing Issues In The Design Phase Is 100 Times Cheaper</h2>
<p>One of the most compelling financial arguments for UX is the <strong>1:100 rule</strong>. Modern studies, such as from the <a href="https://www.researchgate.net/figure/BM-System-Science-Institute-Relative-Cost-of-Fixing-Defects_fig1_255965523">IBM Systems Institute</a> and <a href="https://www.seguetech.com/rising-costs-defects/">Sugue Technologies</a>, show that fixing an error after a product has been developed and launched can be <strong>up to 100 times more expensive</strong> than fixing it during the initial design and prototyping phase.</p>
<p>Think of UX as “engineering insurance.” By the time a developer touches the code, every interaction should have been validated. If you discover a fundamental navigation flaw after launch, <a href="https://www.linkedin.com/pulse/2024-cost-fixing-defects-importance-acceptance-a-j-karikari-a10ef/">you aren’t just paying for the fix</a>; you’re paying for technical debt, lost developer time, and the revenue lost while users struggle with a broken flow.</p>
<figure class="
  
    break-out article__image
  
  
  "></p>
<p>    <a href="https://files.smashing.media/articles/data-backed-truths-user-experience-roi/1-cost-bug-fixing.png"></p>
<p>    <img loading="lazy" width="800" height="420" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" alt="Graph showing the cost of bug fixing during different phases." class="lazyload" data-src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/data-backed-truths-user-experience-roi/1-cost-bug-fixing.png"></p>
<p>    </a><figcaption class="op-vertical-bottom">
      Graph showing the cost of bug fixing during different phases. (Image source: <a href="https://qatestlab.medium.com/the-true-cost-of-a-software-bug-f8ee6a08b10b">QATestLab</a>) (<a href="https://files.smashing.media/articles/data-backed-truths-user-experience-roi/1-cost-bug-fixing.png">Large preview</a>)<br />
    </figcaption></figure>
<div data-audience="non-subscriber" data-remove="true" class="feature-panel-container">
<aside class="feature-panel">
<div class="feature-panel-left-col">
<div class="feature-panel-description">
<p>Meet <strong><a data-instant href="https://www.smashingconf.com/online-workshops/">Smashing Workshops</a></strong> on <strong>front-end, design &amp; UX</strong>, with practical takeaways, live sessions, <strong>video recordings</strong> and a friendly Q&amp;A. With Brad Frost, Stéph Walter and <a href="https://smashingconf.com/online-workshops/workshops">so many others</a>.</p>
<p><a data-instant href="smashing-workshops" class="btn btn--green btn--large">Jump to the workshops&nbsp;↬</a></div>
</div>
<div class="feature-panel-right-col"><a data-instant href="smashing-workshops" class="feature-panel-image-link"></p>
<div class="feature-panel-image">
<img loading="lazy" class="feature-panel-image-img lazyload" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" alt="Feature Panel" width="257" height="355" data-src="/images/smashing-cat/cat-scubadiving-panel.svg"></p>
</div>
<p></a>
</div>
</aside>
</div>
<h2 id="2-performance-impacts-user-experience">2. Performance Impacts User Experience</h2>
<p>In the current landscape, performance is the essential foundation of user experience. A beautiful interface is worthless if the user bounces before it renders. <a href="https://www.wearetenet.com/blog/website-speed-page-load-time-statistics">The data is uncompromising</a>: <strong>47% of users expect a page to load in two seconds or less</strong>, and missing this window is a financial catastrophe. A mere one-second delay can reduce conversions by <strong>20%</strong> and satisfaction by <strong>16%</strong>, while retail businesses lose an estimated <strong>$2.6 billion annually</strong> to slow load times. When mobile load time moves from one to three seconds, the bounce rate spikes by <strong>32%</strong>, and by the third second, conversion rates typically plummet from <strong>40% to 29%</strong>.</p>
<p>However, this volatility offers a massive lever for growth. Even a microscopic <strong>0.1-second improvement</strong> can lift retail conversions by <strong>8.4%</strong>, and travel site conversions by <strong>10.1%</strong>. Improving your Largest Contentful Paint (LCP) by 31% &mdash; a benchmark 67% of websites achieved as of June 2025 &mdash; can drive a direct <strong>8% increase in sales</strong>. As a long-time designer, I treat speed as a primary design element.</p>
<blockquote><p>If the site isn’t instantaneous, the design hasn’t just failed &mdash; it effectively doesn’t exist.</p></blockquote>
<figure class="
  
    break-out article__image
  
  
  "></p>
<p>    <a href="https://files.smashing.media/articles/data-backed-truths-user-experience-roi/2-conversion-rate-page-load-time.png"></p>
<p>    <img loading="lazy" width="800" height="467" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" alt="Graph showing conversion rate by page load time" class="lazyload" data-src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/data-backed-truths-user-experience-roi/2-conversion-rate-page-load-time.png"></p>
<p>    </a><figcaption class="op-vertical-bottom">
      When pages load in one second, conversion rates are about 40%. (Image source: <a href="https://www.wearetenet.com/blog/website-speed-page-load-time-statistics">Tenet</a>) (<a href="https://files.smashing.media/articles/data-backed-truths-user-experience-roi/2-conversion-rate-page-load-time.png">Large preview</a>)<br />
    </figcaption></figure>
<h2 id="3-your-site-has-50-milliseconds-to-impress-your-customers">3. Your Site Has 50 Milliseconds to Impress Your Customers</h2>
<p>First impressions are both <a href="https://www.nngroup.com/articles/first-impressions-human-automaticity/">visceral and aesthetic</a>. <a href="https://cxl.com/blog/first-impressions-matter-the-importance-of-great-visual-design/">Research</a> indicates that users form an opinion about a website’s visual appeal in approximately <strong>50 milliseconds</strong> (0.05 seconds). That’s not a lot of time! This split-second “gut-feeling” is a survival mechanism that dictates whether a user stays to explore your value proposition or bounces immediately.</p>
<p>In the current market, <strong>94% of first impressions</strong> are <a href="https://cxl.com/blog/first-impressions-matter-the-importance-of-great-visual-design/">strictly design related</a>. If your interface feels “off” or dated, users subconsciously project that lack of quality onto your entire product or service. Your content effectively doesn’t exist if your design hasn’t earned the five seconds of attention required to read it.</p>
<h2 id="4-hick-s-law-the-cost-of-overwhelm">4. Hick’s Law: The Cost of Overwhelm</h2>
<p>Stakeholders often think “more options” equals “more value.” Psychology proves the opposite. <a href="https://ixdf.org/literature/article/hick-s-law-making-the-choice-easier-for-users">Hick’s Law</a> states that the time it takes to make a decision increases with the number of options available.</p>
<p>Every extra menu item or form field is a “tax” on the user’s brain. As noted by <a href="https://www.landbase.com/blog/conversion-rate-statistics">Landbase</a>, top-performing sites now achieve conversion rates exceeding 11%, while average performers struggle below 3%. Those performing well have applied personalization and optimization strategies to simplify the experience.</p>
<blockquote><p>If you want to increase your revenue by tomorrow, find one field to delete from your checkout flow today.</p></blockquote>
<figure class="
  
  
  "></p>
<p>    <a href="https://files.smashing.media/articles/data-backed-truths-user-experience-roi/3-complex-choices-vs-simple.png"></p>
<p>    <img loading="lazy" width="800" height="608" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" alt="Demonstrating complex choices vs simple." class="lazyload" data-src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/data-backed-truths-user-experience-roi/3-complex-choices-vs-simple.png"></p>
<p>    </a><figcaption class="op-vertical-bottom">
      Demonstrating complex choices vs simple. (Image source: <a href="https://ixdf.org/literature/article/hick-s-law-making-the-choice-easier-for-users">Mads Soegaard</a>) (<a href="https://files.smashing.media/articles/data-backed-truths-user-experience-roi/3-complex-choices-vs-simple.png">Large preview</a>)<br />
    </figcaption></figure>
<h2 id="5-white-space-improves-comprehension">5. White Space Improves Comprehension</h2>
<p>“White space” is often viewed as wasted real estate by non-designers. In reality, it is a tool for focus. Strategic use of white space can <a href="https://www.loop11.com/the-power-of-white-space-in-ux-design/">increase a user’s content comprehension by up to <strong>20%</strong></a>.</p>
<p>White space prevents “cognitive load” from peaking. By giving the user’s eyes a place to rest, you guide them toward the most important elements, usually your “Buy” or “Sign Up” button. In 2026, as attention spans have dropped to roughly <strong><a href="https://time.com/3858309/attention-spans-goldfish/">8 seconds</a></strong>, simplicity is the ultimate luxury and a major driver of engagement.</p>
<p>For example, in a fintech dashboard I worked on, analyst users were feeling overwhelmed by a ‘data dump’ layout in some of the dashboard components. I applied more white space around the data to lower their cognitive load. Simply giving the data room to breathe led to a 25% decrease in time-on-task and a significant boost in trial-to-paid conversions.</p>
<h2 id="6-the-power-of-fake-progress">6. The Power Of “Fake” Progress</h2>
<p>One of the most surprising psychological hacks in UX is that users will complete a task faster if they believe they have already made progress. This is known as the <strong>Goal Gradient Effect</strong>.</p>
<p>In a classic study, researchers found that a 10-stamp coffee card with two stamps already “pre-filled” was completed significantly faster than an 8-stamp card with zero pre-fills, even though the total spend required was identical. In digital design, showing a progress bar that starts at 15% (simply for creating an account) <a href="https://lawsofux.com/goal-gradient-effect/">increases completion rates for onboarding by over 40%</a>. We aren’t just designing screens &mdash; we are managing the user’s dopamine and sense of momentum.</p>
<figure class="
  
    break-out article__image
  
  
  "></p>
<p>    <a href="https://files.smashing.media/articles/data-backed-truths-user-experience-roi/4-goal-gradient-effect.png"></p>
<p>    <img loading="lazy" width="800" height="287" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" alt="Goal Gradient Effect" class="lazyload" data-src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/data-backed-truths-user-experience-roi/4-goal-gradient-effect.png"></p>
<p>    </a><figcaption class="op-vertical-bottom">
      Increased motivation to reach the target based on current progress. (Image source: <a href="https://conversion-uplift.co.uk/glossary-of-conversion-marketing/goal-gradient-effect/">Conversion Uplift</a>) (<a href="https://files.smashing.media/articles/data-backed-truths-user-experience-roi/4-goal-gradient-effect.png">Large preview</a>)<br />
    </figcaption></figure>
<div class="partners__lead-place"></div>
<h2 id="7-make-your-content-readable">7. Make Your Content Readable</h2>
<p>Many stakeholders believe that cramming more text “above the fold” increases value. <a href="https://www.w3.org/WAI/WCAG21/Understanding/visual-presentation.html">Data proves the opposite</a>. Proper typography, specifically line spacing (leading) and paragraph width, can increase content comprehension and reading speed by up to <strong>20%</strong>.</p>
<p>Optimal line height (generally 1.5x the font size) reduces “visual noise,” allowing the brain to process information with less cognitive effort. When users struggle to read your text due to tight spacing or small fonts, their “perceived effort” increases, leading to a higher bounce rate. Legibility is a conversion tool: if it’s hard to read, it’s hard to buy.</p>
<p>There are many ways to display more legible text. For example, if line spacing (leading) is too small or the font is too heavy, this also impacts readability.</p>
<figure class="
  
  
  "></p>
<p>    <a href="https://files.smashing.media/articles/data-backed-truths-user-experience-roi/5-difference-readability.png"></p>
<p>    <img loading="lazy" width="800" height="618" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" alt="This example demonstrates the difference in readability between a light and a heavy font display." class="lazyload" data-src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/data-backed-truths-user-experience-roi/5-difference-readability.png"></p>
<p>    </a><figcaption class="op-vertical-bottom">
      This example demonstrates the difference in readability between a light and a heavy font display. (Image source: <a href="https://anchordigital.com.au/articles/read-between-the-lines-why-legibility-and-readability-is-essential-for-ux">Anchor</a>) (<a href="https://files.smashing.media/articles/data-backed-truths-user-experience-roi/5-difference-readability.png">Large preview</a>)<br />
    </figcaption></figure>
<h2 id="8-your-users-only-read-20-of-your-content">8. Your Users Only Read 20% of Your Content</h2>
<p>This truth meshes well with the previous one. Users do not read your website; <a href="https://www.nngroup.com/articles/how-users-read-on-the-web/">they scan it</a>. On a typical web page, users read only about <strong>20% to 28% of the text</strong>.</p>
<p>Because modern users scan in an <strong>F-pattern</strong> or <strong>Spotted pattern</strong>, designing for reading is a tactical error. We must design for scanning.</p>
<p>This requires the following:</p>
<ul>
<li>Bold headers that narrate the value proposition.</li>
<li>Bullet points for key benefits.</li>
<li>White space to connect users to key information (discussed in the previous truth).</li>
<li>High-contrast call-to-action (CTA) buttons. If your core message is buried in a paragraph, it is invisible to nearly 80% of your audience.</li>
</ul>
<h2 id="9-why-user-testing-with-5-people-is-the-magic-number">9. Why User Testing With 5 People Is the Magic Number</h2>
<p>I have heard of companies that waste six-figure budgets on massive user studies with 100 people, only to get buried in noise. The reality is that testing with just <strong>5 users</strong> typically uncovers <strong>85% of usability problems</strong>.</p>
<p>This is a mathematical sweet spot. After the fifth user, you reach the point of diminishing returns &mdash; you spend more money to find fewer new bugs. The competitive advantage belongs to small and frequent user testing activities. Test with 5 people, iterate, and test with 5 more. It is the <a href="https://www.nngroup.com/articles/why-you-only-need-to-test-with-5-users/">most cost-effective way</a> to build a bulletproof product.</p>
<p>Personally, I have followed this guideline many times during user testing activities, and I can confidently say that testing with 5 people does deliver the majority of issues in your design.</p>
<h2 id="10-the-financial-roi-of-9-900">10. The Financial ROI of 9,900%</h2>
<p>Last, but definitely not least, the most staggering statistic in our industry remains consistent. On average, every <strong>$1 invested in UX returns $100</strong>. This 9,900% ROI isn’t magic, but the sum of increased conversion and reduced support.</p>
<p>A fully optimised UX design can <a href="https://www.forrester.com/report/The-Six-Steps-For-Justifying-Better-UX/RES117708">improve conversion rates by up to <strong>400%</strong></a>. Furthermore, intuitive design significantly lowers customer support requirements. When a product is self-explanatory, you don’t need a massive call centre to explain how to use it.</p>
<h2 id="the-depth-of-ux-investment">The Depth of UX Investment</h2>
<p>Beyond these individual statistics, we must address the cumulative effect of a mature UX practice. In my years of practising, the most successful firms are those that treat UX as a continuous improvement loop rather than a one-off project. <a href="https://www.mckinsey.com/business-functions/mckinsey-design/our-insights/the-business-value-of-design">The data shows</a> that companies with high design maturity see <strong>32% higher revenue growth</strong> and <strong>56% higher total returns to shareholders</strong> compared to their less design-focused peers.</p>
<p>This discrepancy exists because mature UX organisations move beyond “user delight” and into “user efficiency.” When you shave 30 seconds off a workflow for a team of 1,000 employees, you aren’t just making them happier; you are reclaiming hundreds of thousands of dollars in annual productivity. This internal ROI is often overlooked, but it is just as vital as consumer-facing conversion rates.</p>
<p>Furthermore, the “experience gap” is real. <a href="https://www.bain.com/insights/keeping-up-with-your-customers">80% of companies</a> believe they deliver a “superior experience,” but only 8% of customers agree. This massive disconnect represents a significant market opportunity for those willing to look at the hard data. By bridging this gap through continuous user testing and performance optimisation, you aren’t just improving a product but capturing market share that your competitors are leaving on the table.</p>
<div class="partners__lead-place"></div>
<h2 id="the-impact-of-ai">The Impact of AI</h2>
<p>Today, we cannot talk about UX without talking about AI. However, AI hasn’t replaced these 10 facts, but <a href="https://business.adobe.com/au/resources/sdk/the-state-of-customer-experience-in-an-ai-driven-world-b2c.html">it has accelerated the solution on some of these</a>.</p>
<ul>
<li><strong>Agentic UX</strong><br />
<a href="https://www.databricks.com/resources/ebook/state-of-ai-agents">60% of designers are now building “AI agents”</a> that take actions on behalf of the user, drastically reducing the impact of <strong>Hick’s Law</strong> by narrowing down choices before the user even sees them.</li>
<li><strong>Real-Time Personalisation</strong><br />
32% of teams use AI to personalise interfaces in real-time, meaning the <strong>F-Pattern scanning</strong> habits are catered to by moving the most relevant content to exactly where that specific user’s eyes are likely to land.</li>
<li><strong>Automated ROI</strong><br />
93% of designers are using generative AI tools to prototype faster, which brings the <strong>1:100 Cost Ratio</strong> even lower by allowing us to find and fix errors before a single line of production code is written.</li>
</ul>
<p>AI has turned UX from a static map into a living, breathing guide for users. But the fundamental rules of human psychology, such as our 50ms judgments and our need for white space, remain unchanged.</p>
<h2 id="conclusion">Conclusion</h2>
<p>In summary, here is a list of the key truths to remember:</p>
<ol>
<li>Fixing issues in the design phase is 100 times cheaper.</li>
<li>Performance impacts user experience.</li>
<li>Your site has 50 milliseconds to impress your customers.</li>
<li>Hick’s Law: The cost of overwhelm.</li>
<li>White space improves comprehension.</li>
<li>The power of “fake” progress.</li>
<li>Make your content readable.</li>
<li>Your users only read 20% of your content.</li>
<li>Why user testing with 5 people is the magic number.</li>
<li>The financial ROI of 9,900%.</li>
</ol>
<p>As we move deeper into the late 2020s, the line between “design” and “business strategy” has vanished. The data is in, and <a href="https://www.mckinsey.com/business-functions/mckinsey-design/our-insights/the-business-value-of-design">companies that lead in design outperform their competitors</a> by <strong>1.7x in revenue growth</strong>.</p>
<blockquote class="pull-quote">
<p>
    <a class="pull-quote__link" aria-label="Share on Twitter" href="https://twitter.com/share?text=%0aUX%20design%20is%20no%20longer%20a%20team%20you%20hire%20to%20%e2%80%9cmake%20things%20look%20nice.%e2%80%9d%20It%20is%20the%20research-driven,%20data-backed%20discipline%20that%20ensures%20your%20digital%20product%20isn%e2%80%99t%20just%20a%20cost%20centre,%20but%20a%20revenue-generating%20machine.%0a&amp;url=https://smashingmagazine.com%2f2026%2f05%2fdata-backed-truths-user-experience-roi%2f"></p>
<p>UX design is no longer a team you hire to “make things look nice.” It is the research-driven, data-backed discipline that ensures your digital product isn’t just a cost centre, but a revenue-generating machine.</p>
<p>    </a>
  </p>
<div class="pull-quote__quotation">
<div class="pull-quote__bg">
      <span class="pull-quote__symbol">“</span></div>
</p></div>
</blockquote>
<p>In fact, this has always been the case, but I hope that in presenting these cold, hard truths, it now becomes a reality for your business.</p>
<p>As I have found over the years, implementing factual design improvements does make a difference that intuition alone can’t replicate. We are past the era of subjective opinions. The data is clear, the psychology is proven, and the ROI is undeniable. The only question left is whether you’re ready to let the facts lead your design, or if you’ll let your competitors do it first.</p>
<h3 id="further-reading-on-smashingmag">Further Reading On SmashingMag</h3>
<ul>
<li>“<a href="https://www.smashingmagazine.com/2025/02/human-element-using-research-psychology-elevate-data-storytelling/">The Human Element: Using Research And Psychology To Elevate Data Storytelling</a>”, Victor Yocco &amp; Angelica Lo Duca</li>
<li>“<a href="https://www.smashingmagazine.com/2025/10/ai-ux-achieve-more-with-less/">AI In UX: Achieve More With Less</a>”, Paul Boag</li>
<li>“<a href="https://www.smashingmagazine.com/2025/11/practical-guide-ux-strategy/">Six Key Components of UX Strategy</a>”, Vitaly Friedman</li>
<li>“<a href="https://www.smashingmagazine.com/2024/07/designing-sustainable-e-commerce-experiences/">When Friction Is A Good Thing: Designing Sustainable E-Commerce Experiences</a>”, Anna Rátkai</li>
</ul>
<div class="signature">
  <img src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" alt="Smashing Editorial" width="35" height="46" loading="lazy" class="lazyload" data-src="https://www.smashingmagazine.com/images/logo/logo--red.png"><br />
  <span>(yk)</span>
</div>
</article>
]]></content:encoded>
					
					<wfw:commentRss>http://computercoursesonline.com/index.php/2026/05/15/ten-data-backed-truths-of-user-experience-roi/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Practical Interface Patterns For AI Transparency (Part 2)</title>
		<link>http://computercoursesonline.com/index.php/2026/05/13/practical-interface-patterns-for-ai-transparency-part-2/</link>
					<comments>http://computercoursesonline.com/index.php/2026/05/13/practical-interface-patterns-for-ai-transparency-part-2/#respond</comments>
		
		<dc:creator><![CDATA[.]]></dc:creator>
		<pubDate>Wed, 13 May 2026 13:00:00 +0000</pubDate>
				<category><![CDATA[Ux]]></category>
		<guid isPermaLink="false">http://computercoursesonline.com/?p=1221</guid>

					<description><![CDATA[Practical Interface Patterns For AI Transparency (Part 2) Practical Interface Patterns For AI Transparency (Part 2) Victor Yocco 2026-05-13T13:00:00+00:00 2026-05-14T20:42:34+00:00 In the first part of this series, we talked about the Decision Node Audit. We mapped out the internal workings of our AI system to pinpoint the exact moments it makes decisions based on probabilities....]]></description>
										<content:encoded><![CDATA[<p>              <title>Practical Interface Patterns For AI Transparency (Part 2)</title></p>
<article>
<header>
<h1>Practical Interface Patterns For AI Transparency (Part 2)</h1>
<address>Victor Yocco</address>
<p>                  2026-05-13T13:00:00+00:00<br />
                  2026-05-14T20:42:34+00:00<br />
                </header>
<p>In the <a href="https://www.smashingmagazine.com/2026/04/identifying-necessary-transparency-moments-agentic-ai-part1/">first part of this series</a>, we talked about the <strong>Decision Node Audit</strong>. We mapped out the internal workings of our AI system to pinpoint the exact moments it makes decisions based on probabilities. This told us when the system needs to be transparent with the user. Now, the big question is <em>how</em> to share that information.</p>
<p>You’ve got your <strong>Transparency Matrix</strong> ready. You know which behind-the-scenes API calls need a visible status update. Your engineers are on board with the technical aspects. The next step is designing the visual container for those updates.</p>
<p>We face a legacy problem. For thirty years, interface designers have relied on a single pattern to handle latency: <strong>the spinner</strong>. The spinning wheel, the throbber, the progress bar. These patterns communicate a specific technical reality. They tell the user that the system is retrieving data. The delay is caused by bandwidth or file size.</p>
<p>AI agents introduce a new kind of wait time. When an agent pauses for twenty seconds, it’s not just downloading something; it’s <em>thinking</em>. It’s figuring out the best steps, weighing options, and creating the content you asked for.</p>
<p>If we use a basic spinning icon for this “thinking time,” users get confused and anxious. They watch a looping animation and can’t tell if the system is stalled or crashed. They don’t know if the agent is handling a very complicated task or if it has simply failed.</p>
<p>To build user trust, we need to turn this waiting time into a <strong>moment for reassurance</strong>. Instead of a passive <em>“something is happening,”</em> we need to communicate an active, <em>“Here is exactly how I am working to solve your problem.”</em></p>
<h2 id="writing-clear-status-updates">Writing Clear Status Updates</h2>
<p>We often think of transparency as a visual design problem, but it’s really about the <strong>words</strong> we use. Simple, clear explanations (the microcopy) are what build trust and separate a reliable AI from one that feels broken.</p>
<p>We need to retire generic placeholders like <em>Loading</em> or <em>Working</em>. These words are remnants of the era of static software. Instead, we must construct our status updates using a specific formula that mirrors the agency of the system. Let’s stop using vague words like “Loading” or “Working.” Those terms belong to the past, when software was simple and static. Instead, we should create status updates that clearly tell the user what the system is <em>actually doing</em> and make the system’s actions transparent.</p>
<p>Imagine, for the sake of an example, you are deploying agentic AI that will help team members organize their calendars and plan recurring meetings on their behalf, once prompted.</p>
<p>When an AI displays a message like “Checking availability” for an unknown amount of time, users often feel lost because it doesn’t offer enough information. While they understand the AI is looking at a calendar, they don’t know <em>whose</em> calendar it is, what other steps are involved (before or after), or if the AI even remembered the people and purpose of the scheduling request. Waiting for the final result can be a tense, uneasy experience, like anticipating a gift that you suspect might be a prank.</p>
<p>Perplexity AI provides a strong example of doing status updates right. Figure 1 below shows that when users ask a question, the interface displays exactly what it is doing in real time. You see a list of activities updating as they are accomplished. Users do not need to guess what is happening as the AI works.</p>
<figure class="
  
    break-out article__image
  
  
  "></p>
<p>    <a href="https://files.smashing.media/articles/practical-interface-patterns-ai-transparency/1-perplexity-ai.png"></p>
<p>    <img loading="lazy" width="800" height="354" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" alt="Perplexity AI example" class="lazyload" data-src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/practical-interface-patterns-ai-transparency/1-perplexity-ai.png"></p>
<p>    </a><figcaption class="op-vertical-bottom">
      Figure 1: Perplexity AI is a good example of showing users the AI’s status, including what terms it is using to search the web. (Image source: <a href="https://www.saasui.design/application/perplexity-ai">SaaSUI</a>) (<a href="https://files.smashing.media/articles/practical-interface-patterns-ai-transparency/1-perplexity-ai.png">Large preview</a>)<br />
    </figcaption></figure>
<div data-audience="non-subscriber" data-remove="true" class="feature-panel-container">
<aside class="feature-panel">
<div class="feature-panel-left-col">
<div class="feature-panel-description">
<p>Meet <strong><a data-instant href="https://www.smashingconf.com/online-workshops/">Smashing Workshops</a></strong> on <strong>front-end, design &amp; UX</strong>, with practical takeaways, live sessions, <strong>video recordings</strong> and a friendly Q&amp;A. With Brad Frost, Stéph Walter and <a href="https://smashingconf.com/online-workshops/workshops">so many others</a>.</p>
<p><a data-instant href="smashing-workshops" class="btn btn--green btn--large">Jump to the workshops&nbsp;↬</a></div>
</div>
<div class="feature-panel-right-col"><a data-instant href="smashing-workshops" class="feature-panel-image-link"></p>
<div class="feature-panel-image">
<img loading="lazy" class="feature-panel-image-img lazyload" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" alt="Feature Panel" width="257" height="355" data-src="/images/smashing-cat/cat-scubadiving-panel.svg"></p>
</div>
<p></a>
</div>
</aside>
</div>
<h2 id="the-agentic-update-formula">The Agentic Update Formula</h2>
<p>To give people useful status updates, we need to connect what the system is <em>doing</em> with <em>why</em> it’s doing it. Keeping with our scheduling agent example, the system should break down that waiting period into at least four clear, separate steps.</p>
<ul>
<li>First, the interface displays <em>Checking your calendar to find open times for a recurring Thursday call with [Name(s)]</em>.</li>
<li>Then, it updates to: <em>Cross-checking availability with [Name(s)] calendars</em>.</li>
<li>Next, it might display: <em>Syncing [Name(s)] schedules to secure your meeting time on [Data and Time]</em>.</li>
<li>Finally, at the conclusion, the agent might state they have successfully completed the task and request the user check their email to confirm the invite that’s been shared with the group having the recurring meeting.</li>
</ul>
<p>This communication process grounds the technical process in the user’s actual life.</p>
<p>Making an AI’s progress easy to understand boils down to a three-part structure: a strong <strong>Action Word</strong>, what the AI is working on (the <strong>Specific Item</strong>), and any <strong>Limits</strong> or rules it has to follow.</p>
<p>Think about an AI helping you book a trip. A weak, unhelpful update would just be: <em>Searching for flights&hellip;</em></p>
<p>A much better update uses the formula:</p>
<ul>
<li><strong>Action Word:</strong> <em>Scanning</em></li>
<li><strong>Specific Item:</strong> <em>the prices on Lufthansa and United</em></li>
<li><strong>Limits/Rules:</strong> <em>to find anything under $600.</em></li>
</ul>
<p>This approach clearly shows the user that the AI understood their request and is working within the set boundaries.</p>
<figure class="
  
    break-out article__image
  
  
  "></p>
<p>    <a href="https://files.smashing.media/articles/practical-interface-patterns-ai-transparency/2-agentic-ai-status-update.png"></p>
<p>    <img loading="lazy" width="800" height="437" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" alt="The Anatomy of an Agentic AI Status Update" class="lazyload" data-src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/practical-interface-patterns-ai-transparency/2-agentic-ai-status-update.png"></p>
<p>    </a><figcaption class="op-vertical-bottom">
      Figure 2: The Anatomy of an Agentic AI Status Update. (<a href="https://files.smashing.media/articles/practical-interface-patterns-ai-transparency/2-agentic-ai-status-update.png">Large preview</a>)<br />
    </figcaption></figure>
<h2 id="matching-tone-to-the-risk-matrix">Matching Tone to the Risk Matrix</h2>
<p>Should an AI sound like a person or act like a robot? The right answer depends on the task’s importance, which we can figure out using the <strong>Impact/Risk Matrix</strong> from our <strong>Decision Node Audit</strong>.</p>
<p>For simple, low-risk tasks, a friendly, conversational tone works best. For example, a scheduling assistant can say it’s checking your calendar for the best time. This creates a comfortable, easygoing experience for the user.</p>
<p>However, high-stakes tasks demand clear, mechanical accuracy. If the AI is managing a big financial transfer or a complicated database migration, users don’t want a playful interface; they want precision. A screen that says <em>“I am thinking hard about your money”</em> would possibly cause panic. Instead, the interface should use straightforward language like <em>“Verifying account routing numbers.”</em> By adjusting the AI’s “personality” to match the level of risk, we give users exactly the experience they need in that moment. While the Impact/Risk Matrix provides a necessary starting point, the ultimate determinant of the appropriate AI voice and tone is rigorous <strong>user research</strong>.</p>
<p>It’s impossible for any set of rules to predict the exact words or tone that will build trust or cause stress for every group of users or in every situation. That’s why hands-on research is essential. You need to:</p>
<ul>
<li><strong><a href="https://medium.com/@alienoghli/the-essential-guide-to-a-b-testing-a84b853c16e0">Run A/B tests</a></strong> on different ways the AI “talks” to people.</li>
<li><strong><a href="https://uxplanet.org/usability-testing-the-complete-guide-e162898f68db">Conduct usability studies</a></strong> to see how users react emotionally to the system’s messages.</li>
<li><strong><a href="https://ixdf.org/literature/article/how-to-conduct-user-interviews">Perform interviews</a></strong> to truly understand what users expect from an AI in terms of openness.</li>
</ul>
<p>This kind of research ensures the AI’s “personality” is comfortable and appropriate for the actual people who will be using the system in their specific context.</p>
<p>We’ve now covered the <em>“what”</em> &mdash; the critical microcopy, the clear action words, and the necessary limits that make an AI status update honest and informative. But words alone aren’t enough. A perfect sentence hidden in a poor interface is still a failure of transparency.</p>
<p>The next challenge is the <em>“how”</em> &mdash; designing the physical delivery system for that message. You can think of the status update formula as the engine, and the interface pattern as the car. A powerful engine needs a reliable, well-designed chassis to carry it down the road.</p>
<h2 id="interface-patterns-a-library-for-agents">Interface Patterns: A Library For Agents</h2>
<p>Once we have the right words, we need <strong>the right container</strong>. The key is matching the message’s weight to the pattern’s visibility. A tiny background task (like an agent gently tidying up your files) doesn’t need a loud, flashing banner. That message is best delivered subtly. A high-stakes, multi-step process (like moving money) potentially demands a more robust container that forces the user to pay attention.</p>
<p>By creating a library of these patterns, we ensure the right level of transparency is delivered at the right moment, turning the anxiety of waiting into a moment of informed confidence. Let’s review a few common, critical patterns.</p>
<h3 id="the-living-breadcrumb-ai-working-in-the-background">The Living Breadcrumb: AI Working in the Background</h3>
<p>For those low-importance tasks that an AI is handling quietly in the background, we need a way to show users it’s working without constantly distracting them. We can call this the living breadcrumb.</p>
<p>Think of an email app where an AI is drafting a reply for you. You don’t want a disruptive pop-up message. Instead, a small, subtle status indicator pulses within the application’s border or menu area.</p>
<p>The solution needs to go beyond a static icon. The living breadcrumb smoothly transitions between different text updates. It might pulse from <em>Reading email</em> to <em>Drafting reply</em> to <em>Checking tone</em>. It’s there if you want to check on its progress, offering a quiet assurance that the task is underway, but it won’t demand your immediate attention.</p>
<figure class="
  
    break-out article__image
  
  
  "></p>
<p>    <a href="https://files.smashing.media/articles/practical-interface-patterns-ai-transparency/3-living-breadcrumb.jpg"></p>
<p>    <img loading="lazy" width="800" height="437" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" alt="" class="lazyload" data-src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/practical-interface-patterns-ai-transparency/3-living-breadcrumb.jpg"></p>
<p>    </a><figcaption class="op-vertical-bottom">
      Figure 3: A subtle, living breadcrumb provides assurance in the background for low-priority tasks, showing the AI is working without demanding constant attention. (<a href="https://files.smashing.media/articles/practical-interface-patterns-ai-transparency/3-living-breadcrumb.jpg">Large preview</a>)<br />
    </figcaption></figure>
<h3 id="dynamic-checklists">Dynamic Checklists</h3>
<p>When dealing with critical, high-stakes tasks &mdash; like processing a complex financial transaction or migrating a large, intricate dataset &mdash; we recommend using a <strong>Dynamic Checklist</strong> (illustrated in Figure 3).</p>
<p>This pattern serves as a powerful anchor for the user, providing clarity and confidence about the <strong>process’s progress</strong>. Instead of a simple bar, the Dynamic Checklist lays out every planned step the AI agent will take. It clearly highlights the step that is currently in progress, marks preceding steps as complete, and lists future actions as pending.</p>
<p>For example:</p>
<ul>
<li><strong>Step 1</strong>: Verify Account Balance <strong>[Complete]</strong>.</li>
<li><strong>Step 2</strong>: Convert Currency <strong>[Processing]</strong>.</li>
<li><strong>Step 3</strong>: Transfer Funds <strong>[Pending]</strong>.</li>
</ul>
<p>The Dynamic Checklist offers a significant advantage over a traditional progress bar because it expertly manages unpredictable time. If the currency conversion (Step 2) unexpectedly requires an extra ten seconds, the user won’t feel sudden anxiety or panic. They have full visibility into the system’s exact location, understanding that the delay is occurring during the <em>Converting Currency</em> step. Because they recognize this is a potentially complex action, they are naturally more patient and trusting of the system’s ongoing work.</p>
<figure class="
  
    break-out article__image
  
  
  "></p>
<p>    <a href="https://files.smashing.media/articles/practical-interface-patterns-ai-transparency/4-devin-ai.png"></p>
<p>    <img loading="lazy" width="800" height="652" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" alt="Devin.ai example" class="lazyload" data-src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/practical-interface-patterns-ai-transparency/4-devin-ai.png"></p>
<p>    </a><figcaption class="op-vertical-bottom">
      Figure 4: Devin (AI) shows users a dynamic checklist of the tasks it’s accomplished, as well as what remains to be done. (Image source: <a href="https://docs.devin.ai/get-started/devin-intro">Devin</a>) (<a href="https://files.smashing.media/articles/practical-interface-patterns-ai-transparency/4-devin-ai.png">Large preview</a>)<br />
    </figcaption></figure>
<p>The pattern itself is a compelling UI idea, but designers must remember that its implementation transforms the task into a full-stack design requirement. Unlike a simple loading flag, the dynamic checklist requires a robust front-end state management system to listen for step-completion events, which are typically triggered by a back-end webhook structure. This ensures the interface is always reflecting the agent’s real-time position in the workflow.</p>
<figure class="
  
    break-out article__image
  
  
  "></p>
<p>    <a href="https://files.smashing.media/articles/practical-interface-patterns-ai-transparency/5-dynamic-checklist.jpg"></p>
<p>    <img loading="lazy" width="800" height="437" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" alt="A dynamic checklist example" class="lazyload" data-src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/practical-interface-patterns-ai-transparency/5-dynamic-checklist.jpg"></p>
<p>    </a><figcaption class="op-vertical-bottom">
      Figure 5: A dynamic checklist is excellent  to show transparency in what the AI is doing. It clearly shows the user what steps the AI has already finished, what it’s currently working on, and what it plans to do next. (<a href="https://files.smashing.media/articles/practical-interface-patterns-ai-transparency/5-dynamic-checklist.jpg">Large preview</a>)<br />
    </figcaption></figure>
<div class="partners__lead-place"></div>
<h3 id="the-thinking-toggle">The Thinking Toggle</h3>
<p>Some users with higher information needs or higher needs for transparency may not trust a simple summary; they want to see the system’s raw processing. For this audience, we’ve designed the <strong>Thinking Toggle</strong>.</p>
<p>This is a simple progressive disclosure UI control, like a chevron or a “View Logs” button, that lets the user expand a friendly status update into a raw terminal view. It displays the sanitized logic logs of the AI agent, such as:</p>
<ul>
<li><em>Querying API endpoint /v2/search</em>;</li>
<li><em>Response received: 200 OK</em>;</li>
<li><em>Filtering results by relevance score &gt; 0.8</em>.</li>
</ul>
<p>Many people will never open this view. However, for the user who needs deep transparency, the very presence of this toggle is a signal of trust. It reassures them that the system is not concealing anything.</p>
<p>Keep in mind, with this deep transparency comes a critical technical risk. Even for your most expert audience, you must sanitize and abstract these raw logs before display. This step is non-negotiable to prevent accidentally exposing proprietary business logic, internal data structure names, or security tokens that could be exploited. This process ensures trust is built through honesty, not security vulnerability.</p>
<h3 id="designing-for-partial-success">Designing For Partial Success</h3>
<p>In standard software, things are often black or white. A file either saves or it doesn’t. But with AI agents, things are often grey. An agent might plan most of a trip perfectly, yet struggle to book that one special restaurant.</p>
<p>We need to design for when the AI is mostly successful.</p>
<p>Standard binary (yes or no) error messages are trust-killers because they suggest the AI failed completely. If an agent does 90% of a task and only misses the last 10%, a big red “Request Failed” banner is misleading.</p>
<p>Instead, the interface should clearly show what worked and what didn’t:</p>
<ul>
<li><em>Flight booked: UA 492</em> [Success].</li>
<li><em>Hotel reserved: Marriott Downtown</em> [Success].</li>
<li><em>Car rental: Hertz</em> [Failed &mdash; No inventory].</li>
</ul>
<p>This way, you only have to step in and fix the parts that failed, like booking the car yourself, while keeping all the good work the agent already did.</p>
<figure class="
  
    break-out article__image
  
  
  "></p>
<p>    <a href="https://files.smashing.media/articles/practical-interface-patterns-ai-transparency/6-designing-partial-success.jpg"></p>
<p>    <img loading="lazy" width="800" height="437" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" alt="" class="lazyload" data-src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/practical-interface-patterns-ai-transparency/6-designing-partial-success.jpg"></p>
<p>    </a><figcaption class="op-vertical-bottom">
      Figure 6: We need to be more granular in defining how much of a task has been completed when designing content for agentic AI experiences. (<a href="https://files.smashing.media/articles/practical-interface-patterns-ai-transparency/6-designing-partial-success.jpg">Large preview</a>)<br />
    </figcaption></figure>
<h3 id="disentangling-the-tool">Disentangling The Tool</h3>
<p>When an AI system doesn’t perform as expected, it’s crucial to be absolutely clear about the true reason for the failure. Users often mistakenly blame the AI itself for problems that are actually caused by an external service or tool the AI relies on.</p>
<p>For example, imagine a virtual assistant tries to look at your schedule, but the connection to the Google Calendar API is down. The error message shouldn’t make the assistant look like it failed to do its job.</p>
<ul>
<li><strong>Less helpful:</strong> <em>“I could not check your calendar.”</em> (This suggests the assistant is incompetent.)</li>
<li><strong>More helpful and honest:</strong> <em>“The Google Calendar connection is not responding. I will automatically try again in 30 seconds.”</em></li>
</ul>
<p>The first message is frustrating because it makes the AI look like it failed. The second message, though, is much clearer. It explains that the AI is capable, but a broken tool outside its control is causing the issue. This distinction is really important because it keeps the user from losing faith in the AI, even when things go wrong.</p>
<h3 id="the-audit-trail-trust-after-the-fact">The Audit Trail: Trust After The Fact</h3>
<p>Real-time transparency is fleeting. If a user walks away from their desk while the agent is working, they miss the Dynamic Checklist. They return to a finished screen. If the result looks odd, they have no way to verify the work. This is why every agentic workflow requires a persistent Audit Trail.</p>
<p>We need to design a <em>Show Work</em> interaction. On the final result screen, provide a link or history log that allows the user to replay the decision logic.</p>
<ul>
<li><em>See how this price was calculated</em>;</li>
<li><em>View search sources</em>.</li>
</ul>
<p>This receipt is the ultimate safety net. It allows the user to spot-check the validity of the output. Even if they never click it, the mere presence of the receipt tells the user that the system stands behind its work.</p>
<figure class="
  
    break-out article__image
  
  
  "></p>
<p>    <a href="https://files.smashing.media/articles/practical-interface-patterns-ai-transparency/7-audit-trail-design-pattern.jpg"></p>
<p>    <img loading="lazy" width="800" height="437" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" alt="The Audit trail design pattern" class="lazyload" data-src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/practical-interface-patterns-ai-transparency/7-audit-trail-design-pattern.jpg"></p>
<p>    </a><figcaption class="op-vertical-bottom">
      Figure 7: The Audit trail design pattern provides the needed transparency to show users how an outcome was achieved. (<a href="https://files.smashing.media/articles/practical-interface-patterns-ai-transparency/7-audit-trail-design-pattern.jpg">Large preview</a>)<br />
    </figcaption></figure>
<p>ChatGPT provides an example of how now providing users with an easy way to audit the information AI uses can cause confusion or user frustration. ChatGPT remembers you in the way a file cabinet quietly fills up with notes about everything you’ve ever said, then uses those notes to shape every future conversation without telling you. This is called memory. According to developer <a href="https://simonwillison.net/2025/May/21/chatgpt-new-memory/">Simon Willison</a>, in April 2025, that memory was getting fed into every new conversation automatically.</p>
<p>The problem with ChatGPT’s memory at that time was that you couldn’t see what it remembers, or when it’s using that information, or how it’s influencing what you get back. There’s no log. No timeline. No plain-language list of <em>“here’s what the AI has decided about you.”</em></p>
<p>The only way to glimpse the dossier was to know a specific prompt trick &mdash; essentially asking the model to quote its own hidden instructions back to you. Most users will never discover this. They’ll just notice, as Willison did, that ChatGPT placed a “Half Moon Bay” sign in the background of an image they generated (Figure 8) because it had silently cross-referenced their location from previous conversations. This is the absence of transparency (the ability to audit the memory with ease) disguised as personalization. You need to provide users with both.</p>
<figure class="
  
  
  "></p>
<p>    <a href="https://files.smashing.media/articles/practical-interface-patterns-ai-transparency/8-chatgpt-output.png"></p>
<p>    <img loading="lazy" width="800" height="636" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" alt="" class="lazyload" data-src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/practical-interface-patterns-ai-transparency/8-chatgpt-output.png"></p>
<p>    </a><figcaption class="op-vertical-bottom">
      Figure 8: Willison asked ChatGPT to put the dog in a pelican costume, but he did not ask for the “Half Moon Bay” sign. Proving that ChatGPT was tracking information on users without clearly allowing users to access what information was being stored. (Image source: <a href="https://simonwillison.net/2025/May/21/chatgpt-new-memory/">Simon Willison’s Weblog</a>) (<a href="https://files.smashing.media/articles/practical-interface-patterns-ai-transparency/8-chatgpt-output.png">Large preview</a>)<br />
    </figcaption></figure>
<p><strong>The Audit Trail pattern</strong> is the ultimate solution to the memory audit problem demonstrated by ChatGPT. It is one of four core design solutions that, together, create a library of options for improving AI transparency.</p>
<p>Here is a quick summary of the key interface patterns discussed in this article, which are designed to transform AI waiting time from a moment of anxiety into an opportunity to build user confidence:</p>
<table class="tablesaw break-out">
<thead>
<tr>
<th>Pattern</th>
<th>Best Use Case</th>
<th>The User’s Anxiety</th>
<th>The Trust Signal</th>
</tr>
</thead>
<tbody>
<tr>
<td>The Living Breadcrumb</td>
<td>Low-stakes, background tasks (e.g., drafting emails, sorting files).</td>
<td>Did the system stall or freeze?</td>
<td>I am active, but I won&#8217;t disturb you.</td>
</tr>
<tr>
<td>The Dynamic Checklist</td>
<td>High-stakes workflows with variable time (e.g., financial transfers, booking travel).</td>
<td>Is it stuck? What step is taking so long?</td>
<td>I have a plan, and I am currently executing Step 2.</td>
</tr>
<tr>
<td>The Thinking Toggle</td>
<td>Expert tools or complex data analysis (e.g., code generation, market research).</td>
<td>Is this hallucinating or using real data?</td>
<td>I have nothing to hide; here are my raw logs.</td>
</tr>
<tr>
<td>The Audit Trail</td>
<td>Post-task review for any outcome (e.g., final reports, completed bookings).</td>
<td>How do I know this result is accurate?</td>
<td>Here is the receipt of my work for you to verify.</td>
</tr>
</tbody>
</table>
<p><strong><em>Table 1:</em></strong> <em>Four design patterns enhancing transparency.</em></p>
<div class="partners__lead-place"></div>
<h3 id="the-reality-of-attention-when-users-ignore-the-interface">The Reality of Attention: When Users Ignore the Interface</h3>
<p>Even the most perfectly designed checklist or the clearest status message may still go ignored by many users.</p>
<p>When people are working on tons of tasks, especially professionals, they often tune out the interface. Think of an insurance underwriter creating fifty quotes a day &mdash; they’re not watching a progress bar. They click “Generate,” switch tabs to answer an email, and only come back when the task is done.</p>
<p>My research with these experts shows they judge the system based entirely on the final result. They have a good idea of what the answer <em>should</em> be. If a salesperson expects a premium between $500 and $600, and the system returns $550, they accept it right away, and trust is established.</p>
<p>These experts tell me that over time, as the AI continues to provide what they perceive as accurate outputs, usage will increase, and they will save time versus manual quoting. Essentially, the system is now viewed as an efficient accelerator of an otherwise monotonous yet mandatory task.</p>
<p>But if the system returns $900, the user stops. The output is not aligned with expectations, and that’s a problem they must solve. At that moment, the user switched tabs; they missed the little explanation about the high-risk surcharge that popped up in real-time. They didn’t see the specific rule that was triggered. If that explanation disappeared with the progress bar, the user has no way to understand the difference between expectation and outcome. They certainly won’t run the query again just to watch the animation play out.</p>
<p>They will run the quote by hand, effectively treating the AI’s output as useless and initiating a complete rework of their effort. This manual recalculation feels like a waste of time, which further erodes their confidence in the tool. Once this happens, the user is not interested in <em>why</em> the system chose $900; they are focused purely on validating or invalidating the system’s accuracy against their own, trusted methods. This lack of transparency, especially in moments of disagreement, is a primary barrier to adoption and consistent use. The audit trail allows us to provide persistent transparency and is the mechanism that prevents the AI from creating more work.</p>
<p>We need to keep this in mind, particularly when delivering AI-powered tools meant for enterprise use. If the tool delivers a result that misaligns with expectations, you rarely get a second chance. If the user must spend ten minutes investigating why the AI provided that number, they will stop using the AI.</p>
<h3 id="predictability-reliability-and-understanding-are-the-product">Predictability, Reliability, and Understanding Are The Product</h3>
<p>We are not building magic tricks. A magic trick relies on misdirection and hidden mechanics. We are building colleagues.</p>
<p>Think of a good colleague, they keep you in the loop. They let you know what they’re up to, what’s taking their time, and when they hit a snag. That honesty is what helps you trust them.</p>
<p>We can apply this to AI. By using the practical patterns we discussed: giving specific updates, showing a dynamic checklist, acknowledging partial wins, and keeping an audit trail, we stop seeing AI as a mysterious black box that just needs a nice coat of paint. Instead, we start treating it like a team member we can rely on and manage, which builds trust and a clear understanding.</p>
<p>The main reason for using these interface ideas is to achieve real transparency, going beyond explaining the AI’s complicated inner workings. Here, transparency means showing the user the <strong>AI’s process and performance</strong> right when they need to see it. This involves plainly communicating the <strong>AI’s current status</strong>, its known <strong>limits</strong>, and an easy-to-follow <strong>history of its decisions</strong>. This level of openness changes the interaction from just accepting what the AI does to actively working with it. It lets users understand why they got a certain result and how they can best step in or guide the system for the best possible outcome.</p>
<h3 id="references">References</h3>
<ul>
<li>“<a href="https://medium.com/@alienoghli/the-essential-guide-to-a-b-testing-a84b853c16e0">The Essential Guide to A/B Testing</a>”, Ali E. Noghli</li>
<li>“<a href="https://uxplanet.org/usability-testing-the-complete-guide-e162898f68db">Usability testing: the complete guide</a>”, Andrew Tipp</li>
<li>“<a href="https://ixdf.org/literature/article/how-to-conduct-user-interviews">How to Conduct User Interviews</a>”, IxDF</li>
</ul>
<div class="signature">
  <img src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" alt="Smashing Editorial" width="35" height="46" loading="lazy" class="lazyload" data-src="https://www.smashingmagazine.com/images/logo/logo--red.png"><br />
  <span>(yk)</span>
</div>
</article>
]]></content:encoded>
					
					<wfw:commentRss>http://computercoursesonline.com/index.php/2026/05/13/practical-interface-patterns-for-ai-transparency-part-2/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Designing Stable Interfaces For Streaming Content</title>
		<link>http://computercoursesonline.com/index.php/2026/04/30/designing-stable-interfaces-for-streaming-content/</link>
					<comments>http://computercoursesonline.com/index.php/2026/04/30/designing-stable-interfaces-for-streaming-content/#respond</comments>
		
		<dc:creator><![CDATA[.]]></dc:creator>
		<pubDate>Thu, 30 Apr 2026 08:00:00 +0000</pubDate>
				<category><![CDATA[Ux]]></category>
		<guid isPermaLink="false">http://computercoursesonline.com/?p=1210</guid>

					<description><![CDATA[Designing Stable Interfaces For Streaming Content Designing Stable Interfaces For Streaming Content Joas Pambou 2026-04-30T08:00:00+00:00 2026-04-30T20:36:58+00:00 More interfaces now render while the response is still being generated. The UI begins in one state, then updates as more data comes in. You see this in chat apps, logs, transcription tools, and other real-time systems. The tricky...]]></description>
										<content:encoded><![CDATA[<p>              <title>Designing Stable Interfaces For Streaming Content</title></p>
<article>
<header>
<h1>Designing Stable Interfaces For Streaming Content</h1>
<address>Joas Pambou</address>
<p>                  2026-04-30T08:00:00+00:00<br />
                  2026-04-30T20:36:58+00:00<br />
                </header>
<p>More interfaces now render while the response is still being generated. The UI begins in one state, then updates as more data comes in. You see this in chat apps, logs, transcription tools, and other real-time systems.</p>
<p>The tricky part is that the <strong>interface is not in a fixed state</strong>; it keeps changing as new content comes in. It grows where lines become longer and new blocks appear. Something that was just below the screen can suddenly move, and the user’s scroll position becomes harder to manage. Parts of the UI might even be incomplete while the user is already interacting with it.</p>
<p>In this article, we’ll take a simple interface and make it handle this properly. We’ll look at how to keep things stable, manage scrolling, and render partial content without breaking the reading experience.</p>
<h2 id="what-does-a-streaming-ui-actually-look-like">What Does A Streaming UI Actually Look Like?</h2>
<p>I’ve built three demos that stream content in different ways: a chat bubble, a log feed, and a transcription view. They look different on the surface, but they all run into the same three problems.</p>
<p>The first is <strong>scroll</strong>. When content is streaming in, most interfaces keep the viewport pinned to the bottom. That works if you are just watching, but the moment you scroll up to read something, the page snaps back down. You did not ask for that. The interface decided for you, and now you’re fighting it instead of reading.</p>
<p>The second is <strong>layout shift</strong>. Streaming content means containers are constantly growing, and as they do, everything below shifts downward. A button you were about to click is no longer where it was. A line you were reading has moved. The page is not broken; it is just that nothing stays still long enough to interact with comfortably.</p>
<p>The third is <strong>render frequency</strong>. Browsers paint the screen around 60 times per second, but streams can arrive much faster than that. This means the DOM, which is the browser’s internal representation of everything on the page, ends up being updated for frames the user will never actually see. Each update still costs something, and that cost adds up quietly until performance starts to slip.</p>
<p>As you go through each demo, pay attention to where things start feeling off. That small moment of friction when the interface starts getting in your way. This is exactly what we are here to fix.</p>
<div data-audience="non-subscriber" data-remove="true" class="feature-panel-container">
<aside class="feature-panel">
<div class="feature-panel-left-col">
<div class="feature-panel-description">
<p>Meet <strong><a data-instant href="https://www.smashingconf.com/online-workshops/">Smashing Workshops</a></strong> on <strong>front-end, design &amp; UX</strong>, with practical takeaways, live sessions, <strong>video recordings</strong> and a friendly Q&amp;A. With Brad Frost, Stéph Walter and <a href="https://smashingconf.com/online-workshops/workshops">so many others</a>.</p>
<p><a data-instant href="smashing-workshops" class="btn btn--green btn--large">Jump to the workshops&nbsp;↬</a></div>
</div>
<div class="feature-panel-right-col"><a data-instant href="smashing-workshops" class="feature-panel-image-link"></p>
<div class="feature-panel-image">
<img loading="lazy" class="feature-panel-image-img lazyload" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" alt="Feature Panel" width="257" height="355" data-src="/images/smashing-cat/cat-scubadiving-panel.svg"></p>
</div>
<p></a>
</div>
</aside>
</div>
<h2 id="example-1-streaming-ai-chat-responses">Example 1: Streaming AI Chat Responses</h2>
<p>This is the most familiar case. You click <strong>Stream</strong>, and the message starts growing token by token, just like a typical AI chat interface.</p>
<figure class="
  
  
  "></p>
<p>    <a href="https://codesandbox.io/embed/swmjpl?view=preview"></p>
<p>    <img loading="lazy" width="800" height="566" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" alt="Streaming AI Chat Responses" class="lazyload" data-src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/designing-stable-interfaces-streaming-content/1-streaming-ai-chat-responses.png"></p>
<p>    </a><figcaption class="op-vertical-bottom">
      Open in <a href="https://codesandbox.io/embed/swmjpl?view=preview">CodeSandbox</a>. (<a href="https://files.smashing.media/articles/designing-stable-interfaces-streaming-content/1-streaming-ai-chat-responses.png">Large preview</a>)<br />
    </figcaption></figure>
<p>Here’s what I want you to try:</p>
<ul>
<li>Click the <strong>Stream</strong> button.</li>
<li>Try scrolling upwards while the message is streaming.</li>
<li>Increase the speed (to something like 10ms).</li>
</ul>
<p>You will notice something subtle but important: the UI keeps trying to pull you back down. Basically, it is making a decision for you about where your attention should be.</p>
<p>That’s one example. Let’s look at another.</p>
<h2 id="example-2-live-processing-in-a-log-viewer">Example 2: Live Processing In A Log Viewer</h2>
<p>This example looks different on the surface, but the problem is actually very similar to the first example. Rather than a message that gets longer over time, new lines are appended continuously, like a terminal or a log stream.</p>
<p>The interesting part here is the tail toggle. It makes the trade-off between interaction and stable interfaces very clear:</p>
<figure class="
  
  
  "></p>
<p>    <a href="https://codesandbox.io/embed/cytscf?view=preview"></p>
<p>    <img loading="lazy" width="800" height="515" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" alt="Live Processing In A Log Viewer" class="lazyload" data-src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/designing-stable-interfaces-streaming-content/2-live-processing-log-viewer.png"></p>
<p>    </a><figcaption class="op-vertical-bottom">
      Open in <a href="https://codesandbox.io/embed/cytscf?view=preview">CodeSandbox</a>. (<a href="https://files.smashing.media/articles/designing-stable-interfaces-streaming-content/2-live-processing-log-viewer.png">Large preview</a>)<br />
    </figcaption></figure>
<p>Again, here is what I want you to try:</p>
<ul>
<li>Click the <strong>Start</strong> button.</li>
<li>Allow the logs to stream past the container’s height.</li>
<li>Scroll up to the beginning.</li>
<li>Stop the stream and disable the “tail” option.</li>
</ul>
<p>Notice that, when tail is enabled, the UI follows the new content. But you’re unable to scroll up and stay in place. Instead, you need to stop the stream or enable “tail” to explore the content.</p>
<h2 id="example-3-dashboard-displaying-real-time-metrics">Example 3: Dashboard Displaying Real-Time Metrics</h2>
<p>In this case, the UI updates in place:</p>
<ul>
<li>Numbers change,</li>
<li>Charts shift,</li>
<li>Values refresh continuously.</li>
</ul>
<figure class="
  
  
  "></p>
<p>    <a href="https://codesandbox.io/embed/8rtsrm?view=preview"></p>
<p>    <img loading="lazy" width="800" height="402" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" alt="Dashboard Displaying Real-Time Metrics" class="lazyload" data-src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/designing-stable-interfaces-streaming-content/3-dashboard-display-real-time-metrics.png"></p>
<p>    </a><figcaption class="op-vertical-bottom">
      Open in <a href="https://codesandbox.io/embed/8rtsrm?view=preview">CodeSandbox</a>. (<a href="https://files.smashing.media/articles/designing-stable-interfaces-streaming-content/3-dashboard-display-real-time-metrics.png">Large preview</a>)<br />
    </figcaption></figure>
<p>There is no scroll tension this time, but a different issue shows up. That’s what we’ll get into next.</p>
<h2 id="why-the-ui-feels-unstable-and-how-to-fix-it">Why The UI Feels Unstable And How To Fix It</h2>
<p>If you tried the chat demo and scrolled upward while the responses were coming in, you may have spotted the first issue right away: the UI keeps pulling you back down to the latest streamed content as it updates. This takes you out of context and never allows you the time to fully digest the content once it has passed.</p>
<p>We see that exact same issue in the second example, the log viewer. Without the tail toggle, the streamed content overrides your scroll position.</p>
<p>These aren’t bugs in the traditional sense that they produce code errors; rather, they are accessibility issues that affect <em>all</em> users. That said, they can be fixed and prevented with careful UX considerations as you plan and test your work.</p>
<h3 id="ensure-predictable-scroll-behavior">Ensure Predictable Scroll Behavior</h3>
<p>This is the goal:</p>
<ul>
<li>Enable auto-scrolling when detecting that the user is at the bottom of the stream.</li>
<li>Stop auto-scrolling when the user has scrolled upwards.</li>
<li>Resume auto-scrolling if the user scrolls back to the bottom of the stream.</li>
</ul>
<p>To do that, we need to know whether the user has intentionally moved away from the bottom, which we can assume is true when the scroll position is manually changed. We can track that behavior with a flag.</p>
<pre><code class="language-javascript">let userScrolled = false;

chatEl.addEventListener('scroll', () =&gt; {
  const gap = chatEl.scrollHeight
            - chatEl.scrollTop
            - chatEl.clientHeight;

  userScrolled = gap &gt; 60;
});
</code></pre>
<p>That <code>60px</code> threshold matters. Without it, tiny layout changes (like a new line) would briefly create a gap and break auto-scroll, even if the user didn’t actually scroll.</p>
<p>Now let’s make sure that we enable auto-scrolling only when the user’s scroll position is equal to the stream’s scroll height, i.e., the user is at the bottom of the stream:</p>
<pre><code class="language-javascript">function autoScroll() {
  if (!userScrolled) {
    chatEl.scrollTop = chatEl.scrollHeight;
  }
}
</code></pre>
<p>One small thing that’s easy to miss: we need to reset <code>userScrolled</code> once a new stream begins. Otherwise, one scroll from a previous message can silently disable auto-scroll for the next one.</p>
<h3 id="solidify-layout-stability">Solidify Layout Stability</h3>
<p>We saw this in the first example as well. As new content streams in, the layout jumps, or shifts, taking you out of your current context. To be specific about what’s shifting: it’s not the page layout in a broad sense, it’s the content directly below the chat bubble.</p>
<p>There’s also a subtler artifact worth calling out before we look at the code: cursor flicker. Because we’re wiping <code>innerHTML</code> and recreating every element on every tick, the cursor is being destroyed and re-added constantly, up to 80 times per second at fast speeds.</p>
<p>At normal speed, it’s easy to miss, but slow the slider down to around 30ms, and you’ll see a faint but persistent flicker at the end of the text. Once we fix the rebuild pattern, the flicker disappears entirely.</p>
<figure class="video-embed-container break-out">
<div class="video-embed-container--wrapper"></div>
</figure>
<p>That rebuild pattern is right here; this is what runs on every single incoming character:</p>
<pre><code class="language-javascript">bubble.innerHTML = '';

fullText.split('n').forEach(line =&gt; {
  const p = document.createElement('p');
  p.textContent = line || 'u00A0';
  bubble.appendChild(p);
});

bubble.appendChild(cursorEl);
</code></pre>
<p>This works, but it’s expensive. Every update wipes the DOM and rebuilds it, forcing layout recalculation each time.</p>
<p>Now we write directly into a live node:</p>
<pre><code class="language-javascript">let currentP = null;

function initBubble(bubble, cursor) {
  currentP = document.createElement('p');
  currentP.appendChild(document.createTextNode(''));
  bubble.insertBefore(currentP, cursor);
}
</code></pre>
<p>What we can do next is to create one paragraph with an empty text node and insert it before the cursor. That gives us a live node we can write into directly.</p>
<p>Then, for each character that arrives:</p>
<pre><code class="language-javascript">function appendChar(char, bubble, cursor) {
  if (char === 'n') {
    currentP = document.createElement('p');
    currentP.appendChild(document.createTextNode(''));
    bubble.insertBefore(currentP, cursor);
  } else {
    currentP.firstChild.textContent += char;
  }
}
</code></pre>
<p>For a regular character, we extend the text node by one character. The browser doesn’t need to recalculate the layout for that; the text grew, but nothing moved. For a newline, we create a fresh paragraph and move <code>currentP</code> forward. Layout recalculates once for that new paragraph, and that’s it.</p>
<div class="partners__lead-place"></div>
<h3 id="render-frequency">Render Frequency</h3>
<p>This one is most visible in the first example, the chat UI. Even with scrolling and a layout fixed, we’re still writing to the DOM on every single incoming character.</p>
<p>When the stream is moving fast, you end up hammering the DOM with updates that don’t actually matter. The fix is straightforward: hold the incoming text in a buffer instead of writing it out immediately. Once you’ve collected enough, write it all to the DOM in one go; that’s what a <strong>flush</strong> is.</p>
<p>To pull this off, we keep a simple buffer and make sure we only schedule a single update at a time. When it fires, <code>requestAnimationFrame</code> takes everything that has built up and writes it to the DOM in one shot.</p>
<pre><code class="language-javascript">let pending   = '';
let rafQueued = false;
</code></pre>
<p>When a new character streams in, we then add it to the buffer. If no flush is scheduled yet, we queue one:</p>
<pre><code class="language-javascript">function onChar(char) {
  pending += char;

  if (!rafQueued) {
    rafQueued = true;
    requestAnimationFrame(flush);
  }
}
</code></pre>
<p>The <code>rafQueued</code> flag is important. Without it, every character would schedule its own frame, and you’d end up with dozens of unnecessary flushes.</p>
<p>When the flush fires, it drains the entire buffer in one pass:</p>
<pre><code class="language-javascript">function flush() {
  for (const char of pending) {
    appendChar(char);
  }
  pending   = '';
  rafQueued = false;
  autoScroll();
}
</code></pre>
<p>All the characters that arrive after the last frame are then rendered together, right before the browser paints them. Then we clear the buffer, reset the flag, and run auto-scroll once.</p>
<pre><code class="language-javascript">let userScrolled = false;

chatEl.addEventListener('scroll', () =&gt; {
  const gap = chatEl.scrollHeight
            - chatEl.scrollTop
            - chatEl.clientHeight;

  userScrolled = gap &gt; 60;
});

function autoScroll() {
  if (!userScrolled) {
    chatEl.scrollTop = chatEl.scrollHeight;
  }
}
</code></pre>
<p>If the gap is small, we keep auto-scrolling. If it grows, we assume the user scrolled up, and we stop. That small threshold helps avoid jitter when new lines slightly change the height. Also, remember to reset <code>userScrolled</code> when a new stream starts.</p>
<p>Once scrolling is under control, another issue becomes obvious. As the message grows, it keeps shifting:</p>
<ul>
<li>It starts as one line,</li>
<li>It expands, then</li>
<li>It pushes everything below it.</li>
</ul>
<p>Nothing is technically broken, but it doesn’t feel stable. A common approach is to rebuild the whole message on every update:</p>
<pre><code class="language-javascript">bubble.innerHTML = '';

fullText.split('n').forEach(line =&gt; {
  const p = document.createElement('p');
  p.textContent = line || 'u00A0';
  bubble.appendChild(p);
});

bubble.appendChild(cursorEl);
</code></pre>
<p>This works, but it is doing too much work. Every update destroys and rebuilds the DOM, forcing layout recalculation each time. That’s why everything keeps shifting. The idea is to write into the current paragraph and only create a new one when we actually hit a line break.</p>
<pre><code class="language-javascript">let currentP = null;

function initBubble(bubble, cursor) {
  currentP = document.createElement('p');
  currentP.appendChild(document.createTextNode(''));
  bubble.insertBefore(currentP, cursor);
}
</code></pre>
<p>And then update it character by character:</p>
<pre><code class="language-javascript">function appendChar(char, bubble, cursor) {
  if (char === 'n') {
    currentP = document.createElement('p');
    currentP.appendChild(document.createTextNode(''));
    bubble.insertBefore(currentP, cursor);
  } else {
    currentP.firstChild.textContent += char;
  }
}
</code></pre>
<p>Now we’re no longer rebuilding everything. Most updates just extend a text node, which is cheap and doesn’t trigger large layout shifts. It also fixes the small cursor flicker you might have noticed earlier, since we’re no longer removing and re-adding it.</p>
<p>At this point, the UI already feels better, but there is still something subtle going on. We are still updating the DOM on every character. At higher speeds, that becomes a lot of small updates, many of which you never actually see.</p>
<p>Instead of rendering immediately, we can buffer the incoming characters and apply them once per frame.</p>
<pre><code class="language-javascript">let pending = '';
let rafQueued = false;

function onChar(char) {
  pending += char;

  if (!rafQueued) {
    rafQueued = true;
    requestAnimationFrame(flush);
  }
}
</code></pre>
<p>At this point, we’re not touching the DOM yet, but only collecting characters as they arrive. Then, right before the next frame is painted, we flush everything at once:</p>
<pre><code class="language-javascript">function flush() {
  for (const char of pending) {
    appendChar(char);
  }

  pending = '';
  rafQueued = false;

  autoScroll();
}
</code></pre>
<p>These separate two things that were previously tied together:</p>
<ol>
<li>How fast data arrives, and</li>
<li>When the UI updates.</li>
</ol>
<p>The result looks the same, but the browser does less work, resulting in the UI feeling smoother, especially when the stream is set to a faster speed.</p>
<figure class="
  
  
  "></p>
<p>    <a href="https://codesandbox.io/embed/pk7tk5?view=preview"></p>
<p>    <img loading="lazy" width="800" height="566" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" alt="Broken vs. fixed" class="lazyload" data-src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/designing-stable-interfaces-streaming-content/4-broken-vs-fixed.png"></p>
<p>    </a><figcaption class="op-vertical-bottom">
      Open in <a href="https://codesandbox.io/embed/pk7tk5?view=preview">CodeSandbox</a>. (<a href="https://files.smashing.media/articles/designing-stable-interfaces-streaming-content/4-broken-vs-fixed.png">Large preview</a>)<br />
    </figcaption></figure>
<p>None of these changes is a big effort on its own. But once they are in place, the interface stops reacting blindly to every update. It becomes easier to read, easier to control, and a lot less distracting, even though the content is still coming in continuously.</p>
<p>There are even more considerations to take into account for ensuring a stable, predictable, and good user experience. For example, what happens if the stream is canceled mid-flow? And what can we do to ensure that user preferences are respected for things like reduced motion, keyboard navigation, and screen reader accessibility? Let’s get into those next.</p>
<h2 id="handling-interrupted-streams">Handling Interrupted Streams</h2>
<p>Most streaming interfaces include a way to stop or cancel the stream. We saw that in the demos. But stopping often leaves the UI in an awkward state. The cursor might keep blinking, buttons don’t update, and the message just freezes mid-stream with no clear indication that it didn’t finish.</p>
<p>The problem is that the stop is usually wired to do one thing: cancel the timer. That’s not enough. You also need to (1) clear the pending buffer, (2) remove the cursor, (3) mark the response as incomplete, and (4) reset the buttons. Here’s how we accomplish those.</p>
<h3 id="1-stop-the-stream-cleanly">1. Stop The Stream Cleanly</h3>
<p>Here’s what <code>stopStream</code> needs to do, in order:</p>
<ol>
<li>Cancel the timer and flip the <code>isStreaming</code> flag so no more ticks run.</li>
<li>Clear the <code>requestAnimationFrame</code> (RAF) buffer so nothing still queued gets written on the next frame.</li>
</ol>
<pre><code class="language-javascript">function stopStream() {
  clearTimeout(streamTimer);
  isStreaming = false;
  pending     = '';
  rafQueued   = false;
}
</code></pre>
<p>Clearing the <code>pending</code> property matters because there might be characters buffered from the last stream instance that haven’t been flushed yet. If you don’t clear it, the next <code>requestAnimationFrame</code> fires, drains the buffer, and writes those characters to the DOM after the stream has officially stopped.</p>
<p>Now we move on to removing the cursor by calling <code>markStopped</code> on the bubble:</p>
<pre><code class="language-javascript">if (cursorEl &amp;&amp; cursorEl.parentNode) cursorEl.remove();
  markStopped(aiBubble);

  stopBtn.style.display  = 'none';
  retryBtn.style.display = '';
  playBtn.style.display  = '';
  setStatus('Stopped', 'stopped');
  chat.removeEventListener('scroll', onScroll);
}
</code></pre>
<p>The <code>cursorEl.parentNode</code> check is there because <code>stopStream</code> is also called internally when a new message fires mid-stream, at which point the cursor might already be gone. Calling <code>remove()</code> on a detached node throws, so we check first.</p>
<p><code>markStopped</code> appends a small label to the bottom of the bubble so the user knows the response didn’t finish:</p>
<pre><code class="language-javascript">function markStopped(bubble) {
  if (!bubble) return;
  bubble.classList.add('stopped');

  const label = document.createElement('span');
  label.className = 'stopped-label';
  label.textContent = 'response stopped';
  bubble.appendChild(label);
}
</code></pre>
<p>The null check on <code>bubble</code> handles the edge case where stop fires before the AI message element has been initialized, which can happen if the user clicks stop during the 300ms delay before the bubble appears.</p>
<h3 id="provide-a-retry-option">Provide A Retry Option</h3>
<p>If the stream simply stops &mdash; perhaps due to a network issue or some other unexpected error &mdash; we ought to provide the user with a path to re-attempt the stream. What that basically means is preventing the UI from doing the expensive work needed to scroll back up to the top, re-read the prompt, and retype it. With a retry option, the user only needs to click a button, and the stream restarts from the current position.</p>
<p>To make that work, we need to hold onto the question when the stream starts:</p>
<pre><code class="language-javascript">let lastQuestion = '';

function startStream(question, answer) {
  lastQuestion = question;
  // rest of setup...
}
</code></pre>
<p>Then, when the retry attempt runs, we reset everything and start fresh:</p>
<pre><code class="language-javascript">function retryStream() {
  if (currentMsgEl &amp;&amp; currentMsgEl.parentNode) {
    currentMsgEl.remove();
  }

  charIndex    = 0;
  userScrolled = false;
  pending      = '';
  rafQueued    = false;
  isStreaming  = true;

  retryBtn.style.display = 'none';
  stopBtn.style.display  = '';
  setStatus('Streaming...', 'streaming');

  chat.addEventListener('scroll', onScroll, { passive: true });

  setTimeout(() =&gt; {
    initAIMsg();
    tick(lastAnswer);
  }, 200);
}
</code></pre>
<p>The reset is critical. Every piece of state needs to go back to its initial value, just like a brand new stream.</p>
<p><strong>Note:</strong> We remove the entire message row (<code>currentMsgEl</code>), not just the bubble. If only the bubble is removed, the layout wrapper and avatar remain persistent and break the structure.</p>
<h3 id="send-a-new-message-mid-stream">Send A New Message Mid-Stream</h3>
<p>There’s one more edge case that’s easy to miss. If the user sends a new message while a stream is still running, you end up with two loops writing to the DOM at the same time. The result is messy, and characters from different responses get mixed together.</p>
<p>Here’s what to do: stop the current stream before starting a new one.</p>
<pre><code class="language-javascript">function startStream(question, answer) {
  if (isStreaming) {
    clearTimeout(streamTimer);
    isStreaming = false;
    pending     = '';
    rafQueued   = false;
    if (cursorEl &amp;&amp; cursorEl.parentNode) cursorEl.remove();
    chat.removeEventListener('scroll', onScroll);
  }

  // now reset and start fresh
  charIndex    = 0;
  userScrolled = false;
  isStreaming  = true;
  lastQuestion = question;
  // ...
}
</code></pre>
<p>Here, we inline the cleanup rather than calling <code>stopStream</code> directly because <code>stopStream</code> also calls <code>markStopped</code> and resets the buttons. The next demo has all three behaviors wired up. You can start a stream, hit “Stop” mid-stream, and the cursor disappears, the “response stopped” label appears, and a “Retry” buttons displayed.</p>
<figure class="
  
  
  "></p>
<p>    <a href="https://codesandbox.io/embed/9cfy92?view=preview"></p>
<p>    <img loading="lazy" width="800" height="505" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" alt="Interruptible stream" class="lazyload" data-src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/designing-stable-interfaces-streaming-content/5-interruptible-stream.png"></p>
<p>    </a><figcaption class="op-vertical-bottom">
      Open in <a href="https://codesandbox.io/embed/9cfy92?view=preview">CodeSandbox</a>. (<a href="https://files.smashing.media/articles/designing-stable-interfaces-streaming-content/5-interruptible-stream.png">Large preview</a>)<br />
    </figcaption></figure>
<div class="partners__lead-place"></div>
<h2 id="accessibility">Accessibility</h2>
<p>Streaming interfaces are often built and tested with a mouse, so they may feel just fine in a browser, but break down in other situations that may not have been considered, like whether a screen reader announces new content at all. Or navigating with a keyboard might get stuck or lose focus as things update. And, of course, moving text can be uncomfortable &mdash; or even disabling &mdash; for <a href="https://www.smashingmagazine.com/2021/10/respecting-users-motion-preferences/">those with motion sensitivities</a>.</p>
<p>The good part is that you do not need to rebuild everything to accommodate these things; they can be fixed with solutions that sit on top of what is already there.</p>
<h3 id="accommodating-assistive-technology-with-live-regions">Accommodating Assistive Technology With Live Regions</h3>
<p>Screen readers don’t automatically announce content that shows up on its own. They usually read things when the user moves to them. So, in a streaming UI, where text builds up over time, nothing gets announced. The content is there, but the user doesn’t hear anything.</p>
<p>The fix is <a href="https://w3c.github.io/aria/#aria-live"><code>aria-live</code></a>. It tells the browser to watch a container and announce updates as they happen, without the user needing to move focus.</p>
<pre><code class="language-html">&lt;div
  id="chat"
  role="log"
  aria-live="polite"
  aria-atomic="false"
  aria-label="Chat messages"
&gt;&lt;/div&gt;
</code></pre>
<ul>
<li><code>role=&quot;log&quot;</code> tells assistive tech this is a stream of updates, like a running transcript. Some tools handle this automatically, but it’s safer to be explicit so behavior stays consistent.</li>
<li><code>aria-atomic=&quot;false&quot;</code> makes sure only the new content is announced. Without it, some screen readers try to read the whole message again on every update, which quickly becomes unusable.</li>
<li><code>aria-live=&quot;polite&quot;</code> queues updates instead of interrupting. Use <code>assertive</code> only for things that really need immediate attention, like errors.</li>
</ul>
<h3 id="handling-incomplete-states">Handling Incomplete States</h3>
<p>Earlier, we inserted a “Response Stopped” label to the message when the stream stops mid-stream. Visually, that’s enough. But for a screen reader, that change needs to be announced.</p>
<p>Since the message is inside a live region with <code>aria-live=&quot;polite&quot;</code>, the label will be automatically announced as new content when it’s added to the DOM. The live region already handles the announcement, so no additional ARIA is needed on the label itself.</p>
<p>The <strong>Retry</strong> button that appears next also needs context. If a screen reader simply says “Retry, button,” it’s not clear what action that refers to. You can fix that by adding an <code>aria-label</code> that includes the original question:</p>
<pre><code class="language-javascript">retryBtn.setAttribute(
  'aria-label',
  `Retry: ${lastQuestion.slice(0, 60)}`
);
</code></pre>
<p>What you can do here is to set this label when the button appears, not on page load:</p>
<pre><code class="language-javascript">retryBtn.style.display = 'inline-block';
retryBtn.setAttribute(
  'aria-label',
  `Retry: ${lastQuestion.slice(0, 60)}`
);
</code></pre>
<p>We also call <code>retryBtn.focus()</code> after stopping. That way, keyboard users don’t have to <code>Tab</code> around with the keyboard to find the next action.</p>
<p><strong>Testing with assistive technology:</strong> Don’t rely on assumptions about how screen readers announce this. Test with actual tools like NVDA (Windows), JAWS (Windows), or VoiceOver (Mac/iOS). Browser DevTools can show you what’s exposed in the accessibility tree, but they can’t tell you how the content <em>sounds</em>. A real screen reader will reveal whether the announcement is happening at the right time and in the right way.</p>
<h3 id="account-for-keyboard-navigation">Account For Keyboard Navigation</h3>
<p>The controls need to work with the keyboard while the UI is live, so the Stop button has to be reachable. For someone not using a mouse, <kbd>Tab</kbd> + <kbd>Enter</kbd> is the only way to cancel a running stream.</p>
<p>Using <code>display: none</code> is fine for hiding buttons; it removes them from the tab order. The problem is using things like <code>opacity: 0</code> or <code>visibility: hidden</code>. Those hide elements visually, but they can still receive focus, so users end up tabbing onto something they can’t see.</p>
<p>Use <code>:focus-visible</code> so the focus ring shows up for keyboard navigation, but not for mouse clicks:</p>
<pre><code class="language-css">btn:focus-visible {
  outline: 2px solid &#035;1d9e75;
  outline-offset: 2px;
}
</code></pre>
<p>The cursor inside the message should have <code>aria-hidden=&quot;true&quot;</code>. It’s just visual. Without that, some screen readers try to read it as text, which gets distracting.</p>
<h3 id="motion-sensitivity">Motion Sensitivity</h3>
<p>The typewriter effect we see in practically every AI interface produces constant motion. As we’ve already discussed, certain amounts of motion can be disabling. Thankfully, browsers expose <code>prefers-reduced-motion</code>, which detects a user’s motion preferences at the operating system level.</p>
<p>For streaming, the best approach is simple: skip the animation and render the full response at once. The content stays the same, only without the motion.</p>
<pre><code class="language-javascript">const reducedMotion = window.matchMedia(
  '(prefers-reduced-motion: reduce)'
).matches;
</code></pre>
<pre><code class="language-javascript">if (reducedMotion) {
  initAIMsg();
  for (const char of text) appendChar(char);
  if (cursorEl &amp;&amp; cursorEl.parentNode) cursorEl.remove();
  done();
  return;
}
tick(text); // normal animation
</code></pre>
<p>In CSS, the cursor blink also needs to stop. Despite being a minor detail, a blinking cursor element counts as <a href="https://www.w3.org/WAI/WCAG21/Understanding/three-flashes-or-below-threshold.html">flashing content</a>.</p>
<pre><code class="language-css">@media (prefers-reduced-motion: reduce) {
  .cursor { animation: none; opacity: 1; }
}
</code></pre>
<p>There we go! The demo below puts everything from this article together, so you can see how these patterns work in practice. It also includes a reduced motion toggle, so you can test the instant render version easily.</p>
<figure class="
  
  
  "></p>
<p>    <a href="https://codesandbox.io/embed/vd9mnk?view=preview"></p>
<p>    <img loading="lazy" width="800" height="594" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" alt="Accessible streaming" class="lazyload" data-src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/designing-stable-interfaces-streaming-content/6-accessible-streaming.png"></p>
<p>    </a><figcaption class="op-vertical-bottom">
      Open in <a href="https://codesandbox.io/embed/vd9mnk?view=preview">CodeSandbox</a>. (<a href="https://files.smashing.media/articles/designing-stable-interfaces-streaming-content/6-accessible-streaming.png">Large preview</a>)<br />
    </figcaption></figure>
<h2 id="conclusion">Conclusion</h2>
<p>Streaming itself is mostly solved. Getting data from the server to the client is not the hard part anymore. What breaks is the UI on top of it.</p>
<p>When content updates continuously, small things start to matter, like scroll behavior, layout stability, render timing, and how the interface responds to user actions. If those aren’t handled well, the UI feels unstable and hard to use.</p>
<p>The patterns in this article fix that by:</p>
<ul>
<li>Keeping scroll position under the user’s control,</li>
<li>Updating only what has changed,</li>
<li>Batching renders per frame,</li>
<li>Handling stop and retry actions, and</li>
<li>Making the interface accessible.</li>
</ul>
<p>You don’t need all of these every time. But when streaming is involved, these are the places things usually go wrong.</p>
<h3 id="further-reading">Further Reading</h3>
<ul>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events">Using Server-Sent Events</a><br />
How to open a connection, handle events, and reconnect when needed. This is the transport layer, everything here builds on.</li>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/API/Streams_API">Streams API</a><br />
Streaming data directly from <code>fetch</code>. Useful when you need more control than SSE.</li>
<li><a href="https://developer.chrome.com/docs/devtools/performance">Chrome DevTools Performance panel</a><br />
Helps you see layout recalculations and paint costs, so you can verify performance improvements.</li>
<li>“<a href="https://web.dev/articles/dom-size-and-interactivity">How Large DOM Sizes Affect Interactivity, And What You Can Do About It</a>”, Jeremy Wagner<br />
Why large DOM trees slow things down, and how to keep them under control in long streaming sessions.</li>
</ul>
<div class="signature">
  <img src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" alt="Smashing Editorial" width="35" height="46" loading="lazy" class="lazyload" data-src="https://www.smashingmagazine.com/images/logo/logo--red.png"><br />
  <span>(yk)</span>
</div>
</article>
]]></content:encoded>
					
					<wfw:commentRss>http://computercoursesonline.com/index.php/2026/04/30/designing-stable-interfaces-for-streaming-content/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>The UX Designer’s Nightmare: When “Production-Ready” Becomes A Design Deliverable</title>
		<link>http://computercoursesonline.com/index.php/2026/04/22/the-ux-designers-nightmare-when-production-ready-becomes-a-design-deliverable/</link>
					<comments>http://computercoursesonline.com/index.php/2026/04/22/the-ux-designers-nightmare-when-production-ready-becomes-a-design-deliverable/#respond</comments>
		
		<dc:creator><![CDATA[.]]></dc:creator>
		<pubDate>Wed, 22 Apr 2026 10:00:00 +0000</pubDate>
				<category><![CDATA[Ux]]></category>
		<guid isPermaLink="false">http://computercoursesonline.com/?p=1206</guid>

					<description><![CDATA[The UX Designer’s Nightmare: When “Production-Ready” Becomes A Design Deliverable The UX Designer’s Nightmare: When “Production-Ready” Becomes A Design Deliverable Carrie Webster 2026-04-22T10:00:00+00:00 2026-04-23T20:34:20+00:00 In early 2026, I noticed that the UX designer’s toolkit seemed to shift overnight. The industry standard “Should designers code?” debate was abruptly settled by the market, not through a consensus...]]></description>
										<content:encoded><![CDATA[<p>              <title>The UX Designer’s Nightmare: When “Production-Ready” Becomes A Design Deliverable</title></p>
<article>
<header>
<h1>The UX Designer’s Nightmare: When “Production-Ready” Becomes A Design Deliverable</h1>
<address>Carrie Webster</address>
<p>                  2026-04-22T10:00:00+00:00<br />
                  2026-04-23T20:34:20+00:00<br />
                </header>
<p>In early 2026, I noticed that the UX designer’s toolkit seemed to shift overnight. The industry standard <em>“Should designers code?”</em> debate was abruptly settled by the market, not through a consensus of our craft, but through the brute force of job requirements. If you browse LinkedIn today, you’ll notice a stark change: UX roles increasingly demand <strong><a href="https://www.refontelearning.com/blog/ui-ux-designer-engineering-in-2026-crafting-future-ready-user-experiences">AI-augmented development</a></strong>, <strong>technical orchestration,</strong> and <strong>production-ready prototyping.</strong></p>
<p>For many, including myself, this is the ultimate design job nightmare. We are being asked to deliver both the “vibe” and the “code” simultaneously, using AI agents to bridge a technical gap that previously took years of computer science knowledge and coding experience to cross. But as the industry rushes to meet these new expectations, they are discovering that AI-generated functional code is not always <em>good</em> code.</p>
<h2 id="the-linkedin-pressure-cooker-role-creep-in-2026">The LinkedIn Pressure Cooker: Role Creep In 2026</h2>
<p>The job market is sending a clear signal. While traditional graphic design roles are expected to grow by only <strong>3%</strong> through 2034, UX, UI, and <a href="https://www.nobledesktop.com/careers/designer/job-outlook#:~:text=The%20projected%20future%20growth%20figures%20for%20Digital,job%20growth%20(which%20lies%20somewhere%20around%205%25).">Product Design roles</a> are projected to grow by <strong>16%</strong> over the same period.</p>
<p>However, this growth is increasingly tied to the rise of <strong>AI product development</strong>, where “design skills” have recently become the #1 most in-demand capability, even ahead of coding and cloud infrastructure. Companies building these platforms are no longer just looking for visual designers; they need professionals who can “<a href="https://humbldesign.io/blog-posts/will-ai-replace-designers-2026">translate technical capability into human-centered experiences</a>.”</p>
<p>This creates a high-stakes environment for the UX designer. We are no longer just responsible for the interface; we are expected to understand the technical logic well enough to ensure that complex AI capabilities feel intuitive, safe, and useful for the human on the other side of the screen. Designers are being pushed toward a <strong>“design engineer” model</strong>, where we must bridge the gap between abstract <a href="https://www.refontelearning.com/blog/ui-ux-designer-engineering-in-2026-crafting-future-ready-user-experiences#skills-and-competencies-for-the-2026-uiux-designer-3">AI logic and user-facing code</a>.</p>
<p>A <a href="https://www.lyssna.com/blog/ux-design-trends/">recent survey</a> found that <strong>73% of designers</strong> now view AI as a primary collaborator rather than just a tool. However, this “collaboration” often looks like “role creep.” Recruiters are often not just looking for someone who understands user empathy and information architecture &mdash; they want someone who can also prompt a React component into existence and push it to a repository!</p>
<p>This shift has created a <strong>competency gap</strong>.</p>
<blockquote><p>As an experienced senior designer who has spent decades mastering the nuances of cognitive load, accessibility standards, and ethnographic research, I am suddenly finding myself being judged on my ability to debug a CSS Flexbox issue or manage a Git branch.</p></blockquote>
<p>The nightmare isn’t the technology itself. It’s the <strong>reallocation of value</strong>.</p>
<blockquote class="pull-quote">
<p>
    <a class="pull-quote__link" aria-label="Share on Twitter" href="https://twitter.com/share?text=%0aBusinesses%20are%20beginning%20to%20value%20the%20speed%20of%20output%20over%20the%20quality%20of%20the%20experience,%20fundamentally%20changing%20what%20it%20means%20to%20be%20a%20%e2%80%9csuccessful%e2%80%9d%20designer%20in%202026.%0a&amp;url=https://smashingmagazine.com%2f2026%2f04%2fproduction-ready-becomes-design-deliverable-ux%2f"></p>
<p>Businesses are beginning to value the speed of output over the quality of the experience, fundamentally changing what it means to be a “successful” designer in 2026.</p>
<p>    </a>
  </p>
<div class="pull-quote__quotation">
<div class="pull-quote__bg">
      <span class="pull-quote__symbol">“</span></div>
</p></div>
</blockquote>
<figure class="
  
    break-out article__image
  
  
  "></p>
<p>    <a href="https://files.smashing.media/articles/ux-designer-nightmare-production-ready-becomes-design-deliverable/1-figma.jpg"></p>
<p>    <img loading="lazy" width="800" height="450" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" alt="Figma to AI code ad" class="lazyload" data-src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/ux-designer-nightmare-production-ready-becomes-design-deliverable/1-figma.jpg"></p>
<p>    </a><figcaption class="op-vertical-bottom">
      Tools that allow designers to switch from design to code. (Image source: <a href="https://www.figma.com/community/plugin/1443774571835235184/figma-to-ai-code-by-designcode">Figma</a>) (<a href="https://files.smashing.media/articles/ux-designer-nightmare-production-ready-becomes-design-deliverable/1-figma.jpg">Large preview</a>)<br />
    </figcaption></figure>
<h3 id="the-competence-trap-two-job-skill-sets-one-average-result">The Competence Trap: Two Job Skill Sets, One Average Result</h3>
<p>There is potentially a very dangerous myth circulating in boardrooms that AI makes a designer “equal” to an engineer. This narrative suggests that because an LLM can generate a functional JavaScript event handler, the person prompting it doesn’t need to understand the underlying logic. In reality, attempting to master two disparate, deep fields simultaneously will most likely lead to being <strong>averagely competent</strong> at both.</p>
<h3 id="the-averagely-competent-dilemma">The “Averagely Competent” Dilemma</h3>
<p>For a senior UX designer to become a senior-level coder is like asking a master chef to also be a master plumber because “they both work in the kitchen.” You might get the water running, but you won’t know why the pipes are rattling.</p>
<ul>
<li><strong>The “cognitive offloading” risk.</strong><br />
Research shows that while AI can speed up task completion, it often leads to a significant decrease in conceptual mastery. In a controlled study, participants using AI assistance scored <a href="https://www.psychologytoday.com/au/blog/the-asymmetric-brain/202602/cognitive-offloading-using-ai-reduces-new-skill-formation">17% lower</a> on comprehension tests than those who coded by hand.</li>
<li><strong>The debugging gap.</strong><br />
The largest performance gap between AI-reliant users and hand-coders is in <a href="https://www.anthropic.com/research/AI-assistance-coding-skills">debugging</a>. When a designer uses AI to write code they don’t fully understand, they don’t have the ability to identify <em>when</em> and <em>why</em> it fails.</li>
</ul>
<figure class="
  
    break-out article__image
  
  
  "></p>
<p>    <a href="https://files.smashing.media/articles/ux-designer-nightmare-production-ready-becomes-design-deliverable/2-ai-assistance-coding-skills-speed.png"></p>
<p>    <img loading="lazy" width="800" height="451" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" alt="A chart showing how AI assistance impacts coding speed and skill formation " class="lazyload" data-src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/ux-designer-nightmare-production-ready-becomes-design-deliverable/2-ai-assistance-coding-skills-speed.png"></p>
<p>    </a><figcaption class="op-vertical-bottom">
      Using AI tools impedes coding skill formation. (Image source: <a href="https://www.anthropic.com/research/AI-assistance-coding-skills">Anthropic</a>) (<a href="https://files.smashing.media/articles/ux-designer-nightmare-production-ready-becomes-design-deliverable/2-ai-assistance-coding-skills-speed.png">Large preview</a>)<br />
    </figcaption></figure>
<p>So, if a designer ships an AI-generated component that breaks during a high-traffic event and cannot manually trace the logic, they are no longer an expert. They are now a liability.</p>
<h3 id="the-high-cost-of-unoptimised-code">The High Cost Of Unoptimised Code</h3>
<p>Any experienced code engineer will tell you that creating code with AI without the right prompt leads to a lot of rework. Because most designers lack the technical foundation to audit the code the AI gives them, they are inadvertently shipping massive amounts of <a href="https://gocrossbridge.com/blog/ai-generated-code/">“Quality Debt”</a>.</p>
<h2 id="common-issues-in-designer-generated-ai-code">Common Issues In Designer-Generated AI Code</h2>
<ul>
<li><strong>The security flaw</strong><br />
Recent reports indicate that up to <a href="https://www.sherlockforensics.com/pages/ai-code-security-report-2026.html">92% of AI-generated codebases</a> contain at least one critical vulnerability. A designer might see a functioning login form, unaware that it has an 86% failure rate in XSS defense, which are the security measures aimed at preventing attackers from injecting malicious scripts into trusted websites.</li>
<li><strong>The accessibility illusion</strong><br />
AI often generates “functional” applications that lack semantic integrity. A designer might prompt a “beautiful and functional toggle switch,” but the AI may provide a non-semantic <code>&lt;div&gt;</code> that lacks keyboard focus and screen-reader compatibility, creating <a href="https://www.levelaccess.com/blog/accessibility-debt-in-software-development-and-how-to-engineer-it-out/">Accessibility Debt</a> that is expensive to fix later.</li>
<li><strong>The performance penalty</strong><br />
AI-generated code tends to be verbose. AI is linked to <a href="https://www.netcorpsoftwaredevelopment.com/blog/ai-generated-code-statistics">4x more code duplication</a> than human-written code. This verbosity slows down page loads, creates massive CSS files, and negatively impacts SEO. To a business, the task looks “done.” To a user with a slow connection or a screen reader, the site is a nightmare.</li>
</ul>
<h2 id="creating-more-work-not-less">Creating More Work, Not Less</h2>
<p>The promise of AI was that designers could ship features without bothering the engineers. The reality has been the birth of a <strong>“Rework Tax”</strong> that is draining engineering resources across the industry.</p>
<ul>
<li><strong>Cleaning up</strong><br />
Organisations are finding that while velocity increases, incidents per Pull Request are also rising by <a href="https://blog.exceeds.ai/ai-code-analysis-benchmark-reports/">23.5%</a>. Some engineering teams now spend a significant portion of their week cleaning up “AI slop” delivered by design teams who skipped a rigorous review process.</li>
<li><strong>The communication gap</strong><br />
Only <a href="https://www.lyssna.com/blog/ux-design-trends/">69% of designers</a> feel AI improves the quality of their work, compared to <strong>82% of developers</strong>. This gap exists because “code that compiles” is not the same as “code that is maintainable.”</li>
</ul>
<p>When a designer hands off AI-generated code that ignores a company’s internal naming conventions or management patterns, they aren’t helping the engineer; they are creating a puzzle that someone else has to solve later.</p>
<figure class="
  
    break-out article__image
  
  
  "></p>
<p>    <a href="https://files.smashing.media/articles/ux-designer-nightmare-production-ready-becomes-design-deliverable/3-issues-developers-face-ai-generated-code.jpg"></p>
<p>    <img loading="lazy" width="800" height="600" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" alt="Typical issues that developers face with AI-generated code" class="lazyload" data-src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/ux-designer-nightmare-production-ready-becomes-design-deliverable/3-issues-developers-face-ai-generated-code.jpg"></p>
<p>    </a><figcaption class="op-vertical-bottom">
      Typical issues that developers face with AI-generated code. (Image source: <a href="https://www.netcorpsoftwaredevelopment.com/blog/ai-generated-code-statistics">Netcorp</a>) (<a href="https://files.smashing.media/articles/ux-designer-nightmare-production-ready-becomes-design-deliverable/3-issues-developers-face-ai-generated-code.jpg">Large preview</a>)<br />
    </figcaption></figure>
<h3 id="the-solution">The Solution</h3>
<p>We need to move away from the nightmare of the “<strong>Solo Full-Stack Designer</strong>” and toward a model of <strong>designer/coder collaboration</strong>.</p>
<p><strong>The ideal reality:</strong></p>
<ul>
<li><strong>The Partnership</strong><br />
Instead of designers trying to be mediocre coders, they should work in a <strong>human-AI-human loop</strong>. A senior UX designer should work <em>with</em> an engineer to use AI; the designer creates prompts for <strong>intent, accessibility, and user flow</strong>, while the engineer creates prompts for <strong>architecture and performance</strong>.</li>
<li><strong>Design systems as guardrails</strong><br />
To prevent accessibility debt from spreading at scale, <a href="https://webaim.org/projects/million/">accessible components must be the default</a> in your design system. AI should be used to feed these tokens into your UI, ensuring that even generated code stays within the “source of truth.”</li>
</ul>
<h2 id="beyond-the-prompt">Beyond The Prompt</h2>
<p>The industry is currently in a state of “AI Infatuation,” but the pendulum will eventually swing back toward quality.</p>
<blockquote class="pull-quote">
<p>
    <a class="pull-quote__link" aria-label="Share on Twitter" href="https://twitter.com/share?text=%0aThe%20UX%20designer%e2%80%99s%20nightmare%20ends%20when%20we%20stop%20trying%20to%20compete%20with%20AI%20tools%20at%20what%20they%20do%20best%20%28generating%20syntax%29%20and%20keep%20our%20focus%20on%20what%20they%20cannot%20do%20%28understanding%20human%20complexity%29.%0a&amp;url=https://smashingmagazine.com%2f2026%2f04%2fproduction-ready-becomes-design-deliverable-ux%2f"></p>
<p>The UX designer’s nightmare ends when we stop trying to compete with AI tools at what they do best (generating syntax) and keep our focus on what they cannot do (understanding human complexity).</p>
<p>    </a>
  </p>
<div class="pull-quote__quotation">
<div class="pull-quote__bg">
      <span class="pull-quote__symbol">“</span></div>
</p></div>
</blockquote>
<p>Businesses that prioritise “designer-shipped code” without engineering oversight will eventually face a reckoning of technical debt, security breaches, and accessibility lawsuits. The designers who thrive in 2026 and beyond will be those who refuse to be “prompt operators” and instead position themselves as the <strong>guardians of the user experience</strong>. This is the perfect outcome for experienced designers and for the industry.</p>
<p>Our value has always been our ability to advocate for the human on the other side of the screen. We must use AI to augment our design thinking, allowing us to test more ideas and iterate faster, but we must never let it replace the specialised engineering expertise that ensures our designs technically <em>work</em> for everyone.</p>
<h3 id="summary-checklist-for-ux-designers">Summary Checklist for UX Designers</h3>
<ul>
<li><strong>Work Together.</strong><br />
Use AI-made code as a starting point to talk with your developers. Don’t use it as a shortcut to avoid working with them. Ask them to help you with prompts for code creation for the best outcomes.</li>
<li><strong>Understand the “Why”.</strong><br />
Never submit code you don’t understand. If you can’t explain how the AI-generated logic works, don’t include it in your work.</li>
<li><strong>Build for Everyone.</strong><br />
Good design is more than just looks. Use AI to check if your code works for people using screen readers or keyboards, not just to make things look pretty.</li>
</ul>
<div class="signature">
  <img src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" alt="Smashing Editorial" width="35" height="46" loading="lazy" class="lazyload" data-src="https://www.smashingmagazine.com/images/logo/logo--red.png"><br />
  <span>(yk)</span>
</div>
</article>
]]></content:encoded>
					
					<wfw:commentRss>http://computercoursesonline.com/index.php/2026/04/22/the-ux-designers-nightmare-when-production-ready-becomes-a-design-deliverable/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>What’s in WordPress 7.0</title>
		<link>http://computercoursesonline.com/index.php/2026/04/20/whats-in-wordpress-7-0/</link>
					<comments>http://computercoursesonline.com/index.php/2026/04/20/whats-in-wordpress-7-0/#respond</comments>
		
		<dc:creator><![CDATA[.]]></dc:creator>
		<pubDate>Mon, 20 Apr 2026 15:00:00 +0000</pubDate>
				<category><![CDATA[Wordpress]]></category>
		<guid isPermaLink="false">http://computercoursesonline.com/?p=1212</guid>

					<description><![CDATA[WordPress 7.0 brings several major changes for developers, site owners, and content teams. This release adds real-time collaboration, extends the Gutenberg editor, introduces new AI infrastructure, and changes a few long-standing WordPress conventions. Here’s what is coming and what to prepare for. 1. Real-time collaboration in the block editor The centerpiece of WordPress 7.0 is...]]></description>
										<content:encoded><![CDATA[<p>WordPress 7.0 brings several major changes for developers, site owners, and content teams.</p>
<p>This release adds real-time collaboration, extends the <a href="https://www.hongkiat.com/blog/all-you-need-to-know-about-wordpress-gutenberg-editor/">Gutenberg editor</a>, introduces new AI infrastructure, and changes a few long-standing WordPress conventions.</p>
<p>Here’s what is coming and what to prepare for.</p>
<h2>1. Real-time collaboration in the block editor</h2>
<p>The centerpiece of WordPress 7.0 is <strong><a rel="nofollow noopener" target="_blank" href="https://make.wordpress.org/core/2023/07/03/real-time-collaboration/">real-time collaboration (RTC)</a></strong>.</p>
<p>Multiple users can edit the same post simultaneously, with changes syncing instantly across all editors. You’ll see cursors, selections, and edits from other users in real time.</p>
<p>It handles conflict resolution gracefully so when two people edit the same paragraph, changes merge intelligently rather than overwriting each other.</p>
<h3>Is it enabled by default?</h3>
<p><strong>No.</strong></p>
<p>For security and compatibility reasons, real-time collaboration is <strong>opt-in</strong> rather than enabled by default. Site administrators must explicitly enable it for their sites.</p>
<ol>
<li>Go to <strong>Settings &gt; Writing</strong> in your WordPress admin dashboard</li>
<li>Scroll to the “Collaboration” section</li>
<li>Check the box labeled <strong>“Enable real-time collaboration in the block editor”</strong></li>
<li>Click <strong>Save Changes</strong></li>
</ol>
<figure>
        <img loading="lazy" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" alt="WordPress Writing settings showing the Collaboration section" width="1000" height="600" class="lazyload" data-src="https://computercoursesonline.com/wp-content/uploads/2026/04/settings-collaborations.jpg"><br />
    </figure>
<p>For multisite networks, network administrators can control whether real-time collaboration is available to site administrators through network settings.</p>
<p>Once enabled, you’ll see collaboration features appear in the block editor. You’ll need at least two user accounts with editing permissions to test the collaborative features properly.</p>
<figure>
        <img loading="lazy" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" alt="WordPress block editor with multiple collaborators editing a post" width="1000" height="600" class="lazyload" data-src="https://computercoursesonline.com/wp-content/uploads/2026/04/block-editor-collaboration.jpg"><br />
    </figure>
<h2>2. PHP-only block registration</h2>
<p>WordPress 7.0 removes a major barrier for developers who want to build blocks without a JavaScript-heavy workflow.</p>
<p><strong>You can now register blocks using only PHP</strong>, without needing React, Node.js, or a build toolchain. This removes a major barrier for traditional PHP WordPress developers who have avoided block development because of the JavaScript complexity.</p>
<p>With PHP-only block registration, you write your block in PHP and WordPress automatically generates the inspector controls (the settings panel in the editor sidebar) for you. This is perfect for blocks that don’t need complex client-side interactivity.</p>
<p>Here’s a basic example of registering a block with PHP:</p>
<pre>
add_action( 'init', function() {
    register_block_type( __DIR__ . '/build/my-block', array(
        'api_version' =&gt; 3,
        'title'       =&gt; __( 'My Custom Block', 'my-plugin' ),
        'description' =&gt; __( 'A simple block registered with PHP.', 'my-plugin' ),
        'category'    =&gt; 'widgets',
        'icon'        =&gt; 'smiley',
        'supports'    =&gt; array(
            'html' =&gt; false,
        ),
        'attributes'  =&gt; array(
            'content' =&gt; array(
                'type'    =&gt; 'string',
                'default' =&gt; '',
            ),
            'alignment' =&gt; array(
                'type'    =&gt; 'string',
                'default' =&gt; 'none',
            ),
        ),
        'render_callback' =&gt; function( $attributes, $content, $block ) {
            $classes = array( 'my-custom-block' );
            if ( ! empty( $attributes['alignment'] ) ) {
                $classes[] = 'has-text-align-' . $attributes['alignment'];
            }
            
            return sprintf(
                '&lt;div class="%s"&gt;%s&lt;/div&gt;',
                esc_attr( implode( ' ', $classes ) ),
                wp_kses_post( $attributes['content'] )
            );
        },
    ) );
} );
</pre>
<p>For more complex blocks, you can still mix PHP registration with JavaScript for the editor interface. But for simple content blocks, PHP-only registration means faster development, lighter plugins, and no build toolchain headaches.</p>
<h2>3. Introducing the Connectors API</h2>
<p>WordPress 7.0 introduces the <strong><a rel="nofollow noopener" target="_blank" href="https://make.wordpress.org/core/2026/03/18/introducing-the-connectors-api-in-wordpress-7-0/">Connectors API</a></strong>.</p>
<p>This is a new framework for registering and managing connections to external services, providing standardized API key management, provider discovery, and admin UI for configuring services.</p>
<p>The Connectors API works hand-in-hand with the built-in AI Client. It automatically discovers AI providers from the WP AI Client registry and creates connectors with proper metadata. Plugins using the AI Client do not need to handle credentials directly. They describe what they need, and WordPress routes requests to configured providers.</p>
<p>Plugins can register custom connectors or override existing ones using the <code>wp_connectors_init</code> action hook.</p>
<p>Here’s a basic example of registering a custom connector:</p>
<pre>
add_action( 'wp_connectors_init', function ( $registry ) {
    $connector = array(
        'name'           =&gt; 'My Custom Service',
        'description'    =&gt; 'Connect to my custom API service.',
        'type'           =&gt; 'custom_provider',
        'authentication' =&gt; array(
            'method'          =&gt; 'api_key',
            'credentials_url' =&gt; 'https://example.com/api-keys',
            'setting_name'    =&gt; 'connectors_custom_my_service_api_key',
        ),
        'plugin'         =&gt; array(
            'file' =&gt; 'my-custom-service/plugin.php',
        ),
    );
    
    $registry-&gt;register( 'my_custom_service', $connector );
} );
</pre>
<p>The API provides three main functions for developers:</p>
<pre>
// Check if a connector is registered
if ( wp_is_connector_registered( 'anthropic' ) ) {
    // The Anthropic connector is available
}

// Get a single connector's data
$connector = wp_get_connector( 'anthropic' );
if ( $connector ) {
    echo $connector['name']; // 'Anthropic'
}

// Get all registered connectors
$connectors = wp_get_connectors();
foreach ( $connectors as $id =&gt; $connector ) {
    printf( '%s: %s', $connector['name'], $connector['description'] );
}
</pre>
<p>API keys can be provided via environment variables, PHP constants, or database settings.</p>
<p>WordPress already ships with an example Connectors implementation, which you can find under <strong>Settings &gt; Connectors</strong> in the admin.</p>
<figure>
        <img loading="lazy" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" alt="WordPress Connectors settings screen in the admin area" width="1000" height="600" class="lazyload" data-src="https://computercoursesonline.com/wp-content/uploads/2026/04/connectors.jpg"><br />
    </figure>
<p>This API is designed to expand beyond AI providers to support payment gateways, social media integrations, and other external services in future releases. That should make it easier for more plugins to plug into the same connection model.</p>
<h2>4. Unified AI interface</h2>
<p>WordPress 7.0 includes a built-in AI Client that provides a provider-agnostic PHP API for plugins to send prompts to AI models and receive results through a consistent interface. This is the engine that powers AI features across WordPress, working hand-in-hand with the Connectors API.</p>
<p>The AI Client handles provider communication, model selection, and response normalization. Your plugin describes what it needs and how it needs it. WordPress handles routing the request to a suitable model from a provider the site owner has configured.</p>
<p>Every interaction starts with the <code>wp_ai_client_prompt()</code> function:</p>
<pre>
// Basic text generation
$text = wp_ai_client_prompt( 'What is the capital of France?' )
    -&gt;using_temperature( 0.8 )
    -&gt;generate_text();

if ( is_wp_error( $text ) ) {
    // Handle error
    return;
}

echo wp_kses_post( $text );
</pre>
<p>The AI Client supports multiple modalities, for example, image generation:</p>
<pre>
$image_file = wp_ai_client_prompt( 'A futuristic WordPress logo in neon style' )
    -&gt;generate_image();

if ( is_wp_error( $image_file ) ) {
    return;
}

echo '&lt;img src="' . esc_url( $image_file-&gt;getDataUri() ) . '" alt=""&gt;';

// JSON-structured responses.
$schema = array(
    'type'  =&gt; 'array',
    'items' =&gt; array(
        'type'       =&gt; 'object',
        'properties' =&gt; array(
            'plugin_name' =&gt; array( 'type' =&gt; 'string' ),
            'category'    =&gt; array( 'type' =&gt; 'string' ),
        ),
        'required' =&gt; array( 'plugin_name', 'category' ),
    ),
);

$json = wp_ai_client_prompt( 'List 5 popular WordPress plugins with their primary category.' )
    -&gt;as_json_response( $schema )
    -&gt;generate_text();
</pre>
<p>Before showing AI-powered UI, check whether the feature can work:</p>
<pre>
$builder = wp_ai_client_prompt( 'test' )
    -&gt;using_temperature( 0.7 );

if ( $builder-&gt;is_supported_for_text_generation() ) {
    // Safe to show text generation UI
}
</pre>
<p>These checks use deterministic logic to match the builder’s configuration against the capabilities of available models. They don’t make API calls, so they’re fast and cost nothing. If you want a related developer-facing example of how WordPress is exposing structured capabilities, the <a href="https://www.hongkiat.com/blog/wordpress-abilities-api-tutorial/">WordPress Abilities API</a> is a useful companion.</p>
<h3>AI Provider Plugins</h3>
<p>Keep in mind that the AI Client architecture <strong>consists of two layers</strong>:</p>
<ol>
<li><strong>PHP AI Client</strong>: A provider-agnostic PHP SDK bundled in Core as an external library. This handles provider communication, model selection, and response normalization.</li>
<li><strong>WordPress wrapper</strong>: Core’s <code>WP_AI_Client_Prompt_Builder</code> class wraps the PHP AI Client with WordPress conventions: snake_case methods, <code>WP_Error</code> returns, and integration with WordPress HTTP transport, the Abilities API, the Connectors infrastructure, and the WordPress hooks system.</li>
</ol>
<p>WordPress Core doesn’t bundle any AI providers directly. Instead, they’re developed and maintained as plugins, which allows for more flexible and rapid iteration. The WordPress project has developed three initial flagship implementations:</p>
<ul>
<li><a rel="nofollow noopener" target="_blank" href="https://wordpress.org/plugins/ai-provider-for-anthropic/">AI Provider for Anthropic</a></li>
<li><a rel="nofollow noopener" target="_blank" href="https://wordpress.org/plugins/ai-provider-for-google/">AI Provider for Google</a></li>
<li><a rel="nofollow noopener" target="_blank" href="https://wordpress.org/plugins/ai-provider-for-openai/">AI Provider for OpenAI</a></li>
</ul>
<p>For developers who have been using the <code>wordpress/php-ai-client</code> or <code>wordpress/wp-ai-client</code> packages, the simplest path is to update your plugin’s “Requires at least” header to 7.0 and replace any <code>AI_Client::prompt()</code> calls with <code>wp_ai_client_prompt()</code>.</p>
<h2>5. No new default theme</h2>
<p><strong>WordPress 7.0 breaks with tradition.</strong></p>
<p>There will be <strong>no “Twenty Twenty-Six”</strong> default theme. The focus shifts to improving existing block themes like Twenty Twenty-Five through the Site Editor and Phase 3 collaboration tools.</p>
<p>This change signals a maturing approach to WordPress theming. The goal is to show users that you don’t need a new theme every year. You can evolve the one you have <a rel="nofollow noopener" target="_blank" href="https://wordpress.org/documentation/article/site-editor/">using the Site Editor</a>.</p>
<p>This reflects a broader trend in WordPress development. Moving from rigid, theme-controlled designs to flexible, user-customizable layouts. With the Site Editor, users can modify templates, create custom patterns, and adjust styles without touching code. A new default theme each year becomes less necessary when users have these tools at their fingertips.</p>
<h2>Breaking changes and compatibility requirements</h2>
<p>Real-time collaboration introduces significant compatibility requirements that plugin and theme developers must address.</p>
<h3>1. Minimum PHP version bump to 7.4</h3>
<p>WordPress 7.0 raises the minimum supported PHP version to 7.4, dropping support for PHP 7.2 and 7.3. This change is necessary to support modern libraries required for collaboration features and AI APIs.</p>
<p>The WordPress core team recommends PHP 8.2 or 8.3 for best performance and security. If your sites run PHP 7.2 or 7.3, you need to upgrade before installing WordPress 7.0. Test the upgrade thoroughly in a <a href="https://www.hongkiat.com/blog/staging-wordpress-development/">staging environment</a> first. Check for deprecated functions, incompatible plugins, and theme issues.</p>
<p>Here’s how to check your current PHP version and prepare for the upgrade:</p>
<pre>
php -v
</pre>
<p>Most hosting providers offer PHP version selection in their control panels. If you’re on shared hosting, check your provider’s documentation for how to switch PHP versions. Some hosts may automatically update sites to compatible versions, but it’s better to test first.</p>
<h3>2. Meta boxes disable collaboration</h3>
<p>This is likely the biggest compatibility issue in WordPress 7.0 for many existing plugins.</p>
<p><strong>The real-time collaboration feature is automatically disabled when classic meta boxes are present on a post</strong>. Since the system can’t sync classic meta box content, it turns off collaboration entirely when meta boxes are detected.</p>
<p>This affects thousands of plugins that still use the traditional meta box approach for custom fields and settings. If your plugin adds meta boxes, users won’t be able to use real-time collaboration on posts where those meta boxes appear.</p>
<p>The solution is to migrate from meta boxes to registered post meta with <code>show_in_rest: true</code>. This allows the data to sync through the WordPress REST API, which the collaboration system can track.</p>
<p>Here’s an example of migrating from a traditional meta box to registered post meta:</p>
<pre>
// OLD: Traditional meta box approach (breaks collaboration)
add_action( 'add_meta_boxes', function() {
    add_meta_box(
        'my_custom_field',
        'Custom Field',
        'render_my_custom_field',
        'post',
        'side',
        'high'
    );
} );

function render_my_custom_field( $post ) {
    $value = get_post_meta( $post-&gt;ID, '_my_custom_field', true );
    echo '&lt;input type="text" name="my_custom_field" value="' . esc_attr( $value ) . '" /&gt;';
}

add_action( 'save_post', function( $post_id ) {
    if ( isset( $_POST['my_custom_field'] ) ) {
        update_post_meta( $post_id, '_my_custom_field', sanitize_text_field( $_POST['my_custom_field'] ) );
    }
} );

// NEW: Registered post meta (works with collaboration)
add_action( 'init', function() {
    register_post_meta( 'post', '_my_custom_field', array(
        'type'         =&gt; 'string',
        'single'       =&gt; true,
        'show_in_rest' =&gt; true, // REQUIRED for collaboration
        'auth_callback' =&gt; function() {
            return current_user_can( 'edit_posts' );
        }
    ) );
} );

// Use in block editor with useSelect
import { useSelect } from '@wordpress/data';
import { store as coreStore } from '@wordpress/core-data';

function MyCustomFieldComponent() {
    const metaValue = useSelect( ( select ) =&gt; {
        return select( coreStore ).getEditedEntityRecord( 'postType', 'post', postId )?.meta?._my_custom_field || '';
    }, [ postId ] );
    
    // Render your field component
}
</pre>
<p>The <a rel="nofollow noopener" target="_blank" href="https://developer.wordpress.org/block-editor/how-to-guides/metabox/">Block Editor Handbook has a complete migration guide</a> that walks through the process in detail.</p>
<h3>3. Plugin architecture requirements</h3>
<p>Beyond meta boxes, plugins need to follow specific patterns to work correctly with real-time collaboration. Custom meta field interfaces must use the WordPress data store via <code>useSelect</code> instead of local React state. If you copy store data into component state, your UI won’t update when other collaborators make changes.</p>
<p>Here’s the difference between the wrong approach (local state) and the right approach (useSelect):</p>
<pre>
// WRONG: Local state breaks collaboration
import { useState, useEffect } from '@wordpress/element';

function WrongComponent( { postId } ) {
    const [metaValue, setMetaValue] = useState( '' );
    
    // This only loads once and won't update when collaborators change the value
    useEffect( () =&gt; {
        apiFetch( { path: `/wp/v2/posts/${postId}` } ).then( ( post ) =&gt; {
            setMetaValue( post.meta._my_custom_field || '' );
        } );
    }, [postId] );
    
    return &lt;div&gt;{metaValue}&lt;/div&gt;;
}

// RIGHT: useSelect enables real-time updates
import { useSelect } from '@wordpress/data';
import { store as coreStore } from '@wordpress/core-data';

function RightComponent( { postId } ) {
    // This automatically updates when any collaborator changes the value
    const metaValue = useSelect( ( select ) =&gt; {
        const post = select( coreStore ).getEditedEntityRecord( 'postType', 'post', postId );
        return post?.meta?._my_custom_field || '';
    }, [ postId ] );
    
    return &lt;div&gt;{metaValue}&lt;/div&gt;;
}
</pre>
<p>The key difference is that <code>useSelect</code> subscribes to the WordPress data store, which is synchronized across all collaborators. Local state only reflects the initial value and won’t update when others make changes.</p>
<p>Blocks with side effects on insertion need special consideration too. Since block content syncs immediately to all collaborators, auto-opening modals or triggering animations on insertion will affect everyone editing the post. The recommendation is to use placeholders with explicit user actions instead of automatic behaviors.</p>
<h2>What’s Next?</h2>
<p>WordPress 7.0 represents a bold step forward for the platform.</p>
<p>Real-time collaboration transforms WordPress from a tool for an individual blogger into a platform for a team. The architectural changes required to make this work will have ripple effects through the plugin and theme ecosystem, but the result is a more modern, capable content management system.</p>
<p>As you prepare for WordPress 7.0, <strong>focus on meta box migration first</strong>. For many plugins, that is the most immediate compatibility issue. Then <strong>test your interfaces in collaborative mode</strong> to catch synchronization problems.</p>
<p>These extra efforts now will ensure your site works seamlessly when it’s finally used in WordPress 7.0.</p>
<p>    <!-- END HERE --></p>
<p>The post <a href="https://www.hongkiat.com/blog/whats-coming-in-wordpress-7/">What&#8217;s in WordPress 7.0</a> appeared first on <a href="https://www.hongkiat.com/blog">Hongkiat</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>http://computercoursesonline.com/index.php/2026/04/20/whats-in-wordpress-7-0/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Session Timeouts: The Overlooked Accessibility Barrier In Authentication Design</title>
		<link>http://computercoursesonline.com/index.php/2026/04/20/session-timeouts-the-overlooked-accessibility-barrier-in-authentication-design/</link>
					<comments>http://computercoursesonline.com/index.php/2026/04/20/session-timeouts-the-overlooked-accessibility-barrier-in-authentication-design/#respond</comments>
		
		<dc:creator><![CDATA[.]]></dc:creator>
		<pubDate>Mon, 20 Apr 2026 13:00:00 +0000</pubDate>
				<category><![CDATA[Ux]]></category>
		<guid isPermaLink="false">http://computercoursesonline.com/?p=1208</guid>

					<description><![CDATA[Session Timeouts: The Overlooked Accessibility Barrier In Authentication Design Session Timeouts: The Overlooked Accessibility Barrier In Authentication Design Eleanor Hecks 2026-04-20T13:00:00+00:00 2026-04-23T20:34:20+00:00 For web professionals, session management is a balancing act between user experience, cybersecurity, and resource usage. For people with disabilities, it is more than that &#8212; it is a barrier to buying digital...]]></description>
										<content:encoded><![CDATA[<p>              <title>Session Timeouts: The Overlooked Accessibility Barrier In Authentication Design</title></p>
<article>
<header>
<h1>Session Timeouts: The Overlooked Accessibility Barrier In Authentication Design</h1>
<address>Eleanor Hecks</address>
<p>                  2026-04-20T13:00:00+00:00<br />
                  2026-04-23T20:34:20+00:00<br />
                </header>
<p>For web professionals, session management is a balancing act between user experience, cybersecurity, and resource usage. For people with disabilities, it is more than that &mdash; it is a barrier to buying digital tickets, scrolling on social media, or applying for a loan online. <strong>Session timeout accessibility</strong> can be the difference between a bad day and a good day for those with disabilities.</p>
<p>For many, getting halfway through an important form only to be unceremoniously kicked back to the login screen is a common experience. Such incidents can lead to exasperation and even abandonment of the website entirely. With some backend work, web professionals can ensure no one has to experience this frustration.</p>
<h2 id="why-session-timeouts-disproportionately-affect-users-with-disabilities">Why Session Timeouts Disproportionately Affect Users With Disabilities</h2>
<p>A considerable portion of the global population has cognitive, motor, or vision impairments. Worldwide, <a href="https://designerly.com/mobile-app-accessibility-checklist/">around 1.3 billion people</a> have significant disabilities. Whether they possess motor, cognitive, or visual impairments, their disabilities affect their ability to interact with technology easily. They can all be disproportionately affected by session timeouts, making session timeout accessibility a critical issue.</p>
<p>Session timeouts are inaccessible for a large percentage of the population. An <a href="https://www.hanoversearch.com/blog/inclusive-recruitment-a-focus-on-neurodiverse-talent/.">estimated 20% of people are</a> neurodivergent, meaning timeout barriers don’t just affect a small subset of users &mdash; they impact a <strong>substantial portion of any website’s audience</strong>. As a result, some users may look inactive when they are not. <strong>Strict timeouts create undue pressure.</strong></p>
<h3 id="motor-impairments-and-slower-input-speeds">Motor Impairments and Slower Input Speeds</h3>
<p>For instance, someone with cerebral palsy tries to purchase tickets online for an upcoming concert. Due to coordination difficulties and muscle stiffness, they may enter their information more slowly than a non-disabled person would. They select the date, choose their seats, and fill out personal information. Before they can enter their credit card details, a timeout pop-up appears. They have been logged out due to “inactivity” and must restart the entire process.</p>
<p>This situation is not entirely hypothetical. Matthew Kayne is a disability rights advocate, broadcaster, and contributor to The European magazine. He describes the effort required to navigate websites as someone with cerebral palsy. He explains how the user interface <a href="https://the-european.eu/story-53902/this-one-digital-glitch-is-pushing-disabled-people-to-breaking-point.html">is often poorly designed</a> for adaptive devices, and he worries his equipment won’t respond correctly. After carefully navigating each page, he is suddenly logged out. In a moment, one timed form can erase hours of work, and it’s not just a matter of inconvenience. A single failed attempt can delay support or cause him to miss appointments.</p>
<p>Motor impairments can <strong>slow input speed</strong>, making it appear the user is not at their computer. As such, people who experience stiffness, hand tremors, coordination challenges, involuntary movements, or muscle weakness are disproportionately affected by session timeouts. According to the DWP Accessibility Manual, it <a href="https://accessibility-manual.dwp.gov.uk/tools-and-resources/basic-accessibility-checks/10-session-timeouts-impact-on-users">can take multiple attempts</a> for adaptive technology to register input, slowing users down considerably. Even if they receive a warning, they may not be able to act fast enough to prove they are still active.</p>
<h3 id="cognitive-impairments-and-processing-time">Cognitive Impairments and Processing Time</h3>
<p>Session timeouts can also create accessibility barriers for those with various types of cognitive differences. Strict timeouts can create undue pressure that assumes everyone processes information at the same speed. Users may appear inactive when they are actually reading, thinking, or processing.</p>
<p>Cognitive differences encompass a wide range of experiences, including neurodivergences like autism and ADHD, developmental disabilities like Down syndrome, and learning disabilities like dyslexia. Many people are born with cognitive differences. In fact, an <a href="https://www.hanoversearch.com/blog/inclusive-recruitment-a-focus-on-neurodiverse-talent/.">estimated 20% of people are</a> neurodivergent, making up a large portion of any website’s audience. Others acquire cognitive disabilities later in life through traumatic brain injury or conditions like dementia.</p>
<p>People with cognitive disabilities often need more time to complete online tasks &mdash; not because of any deficit, but because they process information differently. Design choices that work well for neurotypical users can create unnecessary obstacles for people with ADHD, dyslexia, autism, or memory-related conditions.</p>
<p>Invisible session timeouts are particularly problematic for people who experience memory loss, language processing differences, or <strong>time blindness</strong>. For example, neurodivergent technology leader Kate Carruthers says ADHD <a href="https://katecarruthers.com/life-with-adhd-time-blindness-or-why-i-lose-hours-not-just-my-keys/">has affected her perception</a> of time. She has time blindness and can’t reliably track how much time has passed, making estimates unhelpful.</p>
<blockquote class="pull-quote">
<p>
    <a class="pull-quote__link" aria-label="Share on Twitter" href="https://twitter.com/share?text=%0aWhen%20websites%20depend%20on%20users%20estimating%20remaining%20time%20before%20a%20session%20expires,%20they%20quietly%20exclude%20people%20%e2%80%94%20not%20just%20those%20with%20formal%20ADHD%20diagnoses,%20but%20anyone%20who%20experiences%20time%20differently%20or%20processes%20information%20at%20a%20different%20pace.%0a&amp;url=https://smashingmagazine.com%2f2026%2f04%2fsession-timeouts-accessibility-barrier-authentication-design%2f"></p>
<p>When websites depend on users estimating remaining time before a session expires, they quietly exclude people — not just those with formal ADHD diagnoses, but anyone who experiences time differently or processes information at a different pace.</p>
<p>    </a>
  </p>
<div class="pull-quote__quotation">
<div class="pull-quote__bg">
      <span class="pull-quote__symbol">“</span></div>
</p></div>
</blockquote>
<h3 id="vision-impairments-and-screen-reader-navigation-overhead">Vision Impairments and Screen Reader Navigation Overhead</h3>
<p>Since blind or low-vision users cannot visually scan a page to find what they need, they must listen to links, headings, and form fields, which is inherently <strong>more time-consuming</strong>. More than <a href="https://int.livhospital.com/complete-top-5-reasons-for-vision-loss-worldwide/">43 million people worldwide</a> are affected by blindness, while 295 million have moderate to severe vision impairment, which makes this a significant accessibility concern for any global-facing website.</p>
<p>As a result, these users’ sessions may expire even if they are active. <strong>Live timers and 30-second warnings do little to help</strong>, as they are not built with screen readers in mind.</p>
<p>Bogdan Cerovac, a web developer passionate about digital accessibility, experienced this firsthand. The countdown timer informed him how long he had left before being logged out due to inactivity. By all accounts, it worked fine. However, he describes the <a href="https://cerovac.com/a11y/2025/07/countdowns-and-timers-forgotten-detail-that-can-make-your-users-really-hate-your-product/">screen reader experience as horrible</a>, as it notified him of the remaining time every single second. He couldn’t navigate the page because he was spammed by constant status messages.</p>
<h2 id="common-timeout-patterns-that-fail-accessibility-requirements">Common Timeout Patterns That Fail Accessibility Requirements</h2>
<p>According to the National Institute of Standards and Technology, <a href="https://pages.nist.gov/800-63-4/sp800-63b/session/">session management is preferable</a> to continually preserving credentials, which would incentivize users to create authentication workarounds that could threaten security. However, several common timeout patterns fail to meet modern standards for session timeout accessibility.</p>
<figure class="
  
    break-out article__image
  
  
  "></p>
<p>    <a href="https://files.smashing.media/articles/session-timeouts-accessibility-barrier-authentication-design/1-timeout-pattern-fail-accessibility.png"></p>
<p>    <img loading="lazy" width="800" height="480" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" alt="A session expired window with a “Back to main page” button." class="lazyload" data-src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/session-timeouts-accessibility-barrier-authentication-design/1-timeout-pattern-fail-accessibility.png"></p>
<p>    </a><figcaption class="op-vertical-bottom">
      Image source: <a href="https://github.com/alfio-event/alf.io/issues/903">princekwame</a>. (<a href="https://files.smashing.media/articles/session-timeouts-accessibility-barrier-authentication-design/1-timeout-pattern-fail-accessibility.png">Large preview</a>)<br />
    </figcaption></figure>
<h3 id="silent-timeouts-and-insufficient-warnings">Silent Timeouts and Insufficient Warnings</h3>
<p>Many websites either provide no warning before logging users out, or they display a brief, seconds-long pop-up that appears too late to be actionable. For users who navigate via screen reader, these warnings may not be announced in time. For those with motor impairments, a 30-second countdown may not provide enough time to respond.</p>
<p>Let’s consider the Consular Electronic Application Center’s DS-260 page, which is used to apply for or renew U.S. nonimmigrant visas. If an application <a href="https://travel.state.gov/content/travel/en/us-visas/visa-information-resources/forms/online-immigrant-visa-forms/ds-260-faqs.html">is idle for around 20 minutes</a>, it will log the user off without warning. The FAQ page only provides an approximate time estimate. Someone’s work only saves when they complete the page, so they may lose significant progress.</p>
<h3 id="nonextendable-sessions">Nonextendable Sessions</h3>
<p>An abrupt “session expired” message is frustrating even for individuals without disabilities. If there is no option to continue, users are forced to log back in and restart their work, wasting time and energy.</p>
<h3 id="form-data-loss-on-expiration">Form Data Loss on Expiration</h3>
<p>Unless the website automatically saves progress, visitors will lose everything when the session expires. For someone with disabilities, this does not simply waste time. It can make their day immeasurably harder. Imagine spending an hour on a service request, job application, or purchase order only for all progress to be completely erased with little to no warning.</p>
<h2 id="design-patterns-that-balance-security-and-accessibility">Design Patterns That Balance Security and Accessibility</h2>
<p>Inconsistent timeout periods and a lack of warnings lead to the sudden, unexpected loss of all unsaved work. For long, complex forms, like the DS-260, a poor user experience is extremely frustrating. In comparison, the United Kingdom’s application for pension credit is highly accessible. It warns users <a href="https://design-system.dwp.gov.uk/patterns/manage-a-session-timeout">at least two minutes</a> in advance and allows them to extend the session. It meets level AA of the WCAG 2.2 success criteria, indicating its accessibility.</p>
<figure class="
  
    break-out article__image
  
  
  "></p>
<p>    <a href="https://files.smashing.media/articles/session-timeouts-accessibility-barrier-authentication-design/2-timeout-pattern-accessibility.png"></p>
<p>    <img loading="lazy" width="800" height="450" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" alt="A tab session expired window with a refresh button." class="lazyload" data-src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/session-timeouts-accessibility-barrier-authentication-design/2-timeout-pattern-accessibility.png"></p>
<p>    </a><figcaption class="op-vertical-bottom">
      Image source: <a href="https://experienceleaguecommunities.adobe.com/adobe-workfront-23/pop-up-message-saying-session-has-expired-129862">Experience League</a>. (<a href="https://files.smashing.media/articles/session-timeouts-accessibility-barrier-authentication-design/2-timeout-pattern-accessibility.png">Large preview</a>)<br />
    </figcaption></figure>
<p>People with disabilities are disproportionately affected by the unintended consequences of poor session management. Thankfully, session timeouts’ inaccessibility is not a matter of fact. With a few small changes, web professionals can significantly improve their website’s accessibility.</p>
<h3 id="advance-warning-systems-and-extend-functionality">Advance Warning Systems and Extend Functionality</h3>
<p>Websites should clearly state the time limit’s existence and duration before the session starts. For instance, if someone is filling out a bank form, the first page should exist solely to inform them that it has a 60-minute time limit. A live counter that updates regularly can help them track how much time remains. Also, users should be told whether they can adjust the session timeout length.</p>
<h3 id="activity-based-vs-absolute-timeouts">Activity-Based vs. Absolute Timeouts</h3>
<p>An activity-based timeout logs users out due to inactivity, while an absolute timeout logs them out regardless of activity. For an office, a 24-hour absolute timer might make sense, since workers only need to log in when they get to work. As long as users know when their session will expire, the latter is more accessible than the former.</p>
<h3 id="auto-save-and-progress-preservation">Auto-Save and Progress Preservation</h3>
<p>Cookies, localStorage, and sessionStorage are temporary, client-side storage mechanisms that allow web applications to store data for the duration of a single browser session. They are powerful, lightweight tools. Web developers can use them to automatically save users’ progress at frequent intervals, ensuring data is restored upon reauthentication.</p>
<p>This way, even if someone’s session expires by accident, they are not penalized. Once they log back in, they can finish filling out their credit card details or pick up where they left off with an online form.</p>
<h2 id="testing-and-wcag-compliance-considerations">Testing and WCAG Compliance Considerations</h2>
<p>The Web Content Accessibility Guidelines (WCAG) is a collection of internationally accepted internet accessibility standards published by the W3C. It acts as the arbiter of session timeout accessibility. Web developers should pay special attention to <strong>Guideline 2.9.2</strong>, <a href="https://www.w3.org/TR/wcag-3.0/#adequate-time">which outlines best practices for adequate</a> time.</p>
<p>The <strong>timeout adjustable mechanism</strong> should extend the time limit before the session expires or allow it to be turned off completely. For the former option, a dialog box should appear asking users if they need more time, allowing them to continue with one click. The WC3 notes that exceptions exist.</p>
<p>For example, when a website conducts a live ticket sale, users can only hold tickets in their carts for 10 minutes to give others a chance to purchase limited inventory. Alternatively, session timeouts may be necessary on shared computers. If librarians allowed everyone to stay logged in instead of automatically signing them out overnight, they would risk security issues.</p>
<p>Some processes should not have time limits at all. When browsing social media, reading a news article, or searching for items on an e-commerce site, there is no reason a session should expire within an arbitrary time frame. Meanwhile, in a timed exam, it may be necessary. However, in this case, administrators can extend time limits for students with disabilities.</p>
<p>When web developers make session management accessible, they are not catering to a small group. Pew Research Center data shows <a href="https://www.pewresearch.org/short-reads/2021/09/10/americans-with-disabilities-less-likely-than-those-without-to-own-some-digital-devices/">62% of adults with disabilities</a> own a computer. 72% have high-speed home internet. These figures do not differ statistically from the percentage of non-disabled adults who say the same.</p>
<h2 id="overcoming-the-session-timeout-accessibility-barrier">Overcoming the Session Timeout Accessibility Barrier</h2>
<p>The WCAG provides additional resources that web developers can review to understand session management accessibility better:</p>
<ul>
<li><a href="https://www.w3.org/TR/UNDERSTANDING-WCAG20/time-limits-required-behaviors.html">WCAG SC 2.2.1 Timing Adjustable</a></li>
<li><a href="https://www.w3.org/TR/UNDERSTANDING-WCAG20/time-limits-server-timeout.html">WCAG SC 2.2.5 Re-authenticating</a></li>
<li><a href="https://www.w3.org/WAI/WCAG22/Understanding/timeouts.html">WCAG SC 2.2.6 Timeouts</a></li>
</ul>
<p>In addition to following these guidelines, there is a wealth of information from leading educational institutions, authorities on open web technologies, and government agencies. They provide a great starting place for those with intermediate web development knowledge.</p>
<p>Web professionals should consider the following resources to learn more about tools and techniques they can use to make session management more accessible:</p>
<ul>
<li><a href="https://accessibility.huit.harvard.edu/technique-session-extension">Harvard University’s Session Extension Technique</a></li>
<li><a href="https://accessibility-manual.dwp.gov.uk/tools-and-resources/basic-accessibility-checks/10-session-timeouts-how-to-test">DWP Accessibility Manual: How to test session timeouts</a></li>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/API/Window/sessionStorage">Window: sessionStorage property</a></li>
</ul>
<blockquote class="pull-quote">
<p>
    <a class="pull-quote__link" aria-label="Share on Twitter" href="https://twitter.com/share?text=%0aSession%20timeout%20accessibility%20is%20not%20only%20an%20industry%20best%20practice%20but%20an%20ethical%20web%20development%20standard.%0a&amp;url=https://smashingmagazine.com%2f2026%2f04%2fsession-timeouts-accessibility-barrier-authentication-design%2f"></p>
<p>Session timeout accessibility is not only an industry best practice but an ethical web development standard.</p>
<p>    </a>
  </p>
<div class="pull-quote__quotation">
<div class="pull-quote__bg">
      <span class="pull-quote__symbol">“</span></div>
</p></div>
</blockquote>
<p>Those who prioritize it will appeal to a wider audience, <strong>improve usability</strong>, and attract more website visitors and longer sessions.</p>
<p>The main takeaway is that a website with inaccessible session timeouts sends a clear message that it doesn’t value the user’s time or effort, a problem that creates significant barriers for people with disabilities. However, this is a solvable issue. With a few simple changes, such as providing session extension warnings and auto-saving progress, web developers can build a more considerate, accessible, and respectful internet for everyone.</p>
<h3 id="further-reading-on-smashingmag">Further Reading On SmashingMag</h3>
<ul>
<li>“<a href="https://www.smashingmagazine.com/2025/04/what-mean-site-be-keyboard-navigable/">What Does It Really Mean For A Site To Be Keyboard Navigable</a>”, Eleanor Hecks</li>
<li>“<a href="https://www.smashingmagazine.com/2025/06/designing-for-neurodiversity/">Designing For Neurodiversity</a>”, Vitaly Friedman</li>
<li>“<a href="https://www.smashingmagazine.com/2025/06/what-i-wish-someone-told-me-aria/">What I Wish Someone Told Me When I Was Getting Into ARIA</a>”, Eric Bailey</li>
<li>“<a href="https://www.smashingmagazine.com/2024/02/web-designer-accessibility-advocacy-toolkit/">A Designer’s Accessibility Advocacy Toolkit</a>”, Yichan Wang</li>
</ul>
<div class="signature">
  <img src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" alt="Smashing Editorial" width="35" height="46" loading="lazy" class="lazyload" data-src="https://www.smashingmagazine.com/images/logo/logo--red.png"><br />
  <span>(yk)</span>
</div>
</article>
]]></content:encoded>
					
					<wfw:commentRss>http://computercoursesonline.com/index.php/2026/04/20/session-timeouts-the-overlooked-accessibility-barrier-in-authentication-design/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>How To Improve UX In Legacy Systems</title>
		<link>http://computercoursesonline.com/index.php/2026/04/10/how-to-improve-ux-in-legacy-systems/</link>
					<comments>http://computercoursesonline.com/index.php/2026/04/10/how-to-improve-ux-in-legacy-systems/#respond</comments>
		
		<dc:creator><![CDATA[.]]></dc:creator>
		<pubDate>Fri, 10 Apr 2026 13:00:00 +0000</pubDate>
				<category><![CDATA[Ux]]></category>
		<guid isPermaLink="false">http://computercoursesonline.com/?p=1203</guid>

					<description><![CDATA[How To Improve UX In Legacy Systems How To Improve UX In Legacy Systems Vitaly Friedman 2026-04-10T13:00:00+00:00 2026-04-16T21:02:41+00:00 Imagine that you need to improve the UX of a legacy system. A system that has been silently working in the background for almost a decade. It’s slow, half-broken, unreliable, and severely outdated &#8212; a sort of...]]></description>
										<content:encoded><![CDATA[<p>              <title>How To Improve UX In Legacy Systems</title></p>
<article>
<header>
<h1>How To Improve UX In Legacy Systems</h1>
<address>Vitaly Friedman</address>
<p>                  2026-04-10T13:00:00+00:00<br />
                  2026-04-16T21:02:41+00:00<br />
                </header>
<p>Imagine that you need to improve the <strong>UX of a legacy system</strong>. A system that has been silently working in the background for almost a decade. It’s slow, half-broken, unreliable, and severely outdated &mdash; a sort of “black box” that everyone relies upon, but nobody really knows what’s happening under the hood.</p>
<p><strong>Where would you even start?</strong> Legacy stories are often daunting, adventurous, and utterly confusing. They represent a mixture of fast-paced decisions, quick fixes, and accumulating UX debt.</p>
<p>There is no one-fits-all solution to tackle them, but there are ways to make progress, albeit slowly, while respecting the <strong>needs and concerns</strong> of users and stakeholders. Now, let’s see how we can do just that.</p>
<h2 id="the-actual-challenges-of-legacy-ux">The Actual Challenges Of Legacy UX</h2>
<p>It might feel that legacy products are waiting to be deprecated at any moment. But in reality, they are often <strong>critical for daily operations</strong>. Many legacy systems are heavily customized for the needs of the organization, often built externally by a supplier and often without rigorous usability testing.</p>
<p>It’s common for enterprises to spend <strong>40–60% of their time</strong> managing, maintaining, and fine-tuning legacy systems. They are essential, critical &mdash; but also very expensive to keep alive.</p>
<figure class="
  
  
  "></p>
<p>    <a href="https://files.smashing.media/articles/how-improve-ux-legacy-systems/1-cash-register.jpg"></p>
<p>    <img loading="lazy" width="800" height="500" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" alt="A detailed electronic medical record (EMR) screen for an ophthalmology patient, displaying their visit summary including chief complaint, past medical history, medications, and optical test results." class="lazyload" data-src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/how-improve-ux-legacy-systems/1-cash-register.jpg"></p>
<p>    </a><figcaption class="op-vertical-bottom">
      Cash registers are frequently designed once and rarely touched again. Replacing them across 1000s of stores is remarkably expensive. (<a href="https://files.smashing.media/articles/how-improve-ux-legacy-systems/1-cash-register.jpg">Large preview</a>)<br />
    </figcaption></figure>
<h3 id="1-legacy-must-co-exist-with-products-built-around-them">1. Legacy Must Co-Exist With Products Built Around Them</h3>
<p>Running in a <strong>broken, decade-old ecosystem</strong>, legacy still works, yet nobody knows exactly how and why it still does. People who have set it up originally probably have left the company years ago, leaving a lot of unknowns and poorly documented work behind.</p>
<p>With them come <strong>fragmented and inconsistent design choices</strong>, stuck in old versions of old design tools that have long been discontinued.</p>
<figure class="
  
  
  "></p>
<p>    <a href="https://files.smashing.media/articles/how-improve-ux-legacy-systems/2-legacy-system-healthcare.jpg"></p>
<p>    <img loading="lazy" width="800" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" alt="A detailed electronic medical record (EMR) screen for an ophthalmology patient, displaying their visit summary including chief complaint, past medical history, medications, and optical test results." class="lazyload" data-src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/how-improve-ux-legacy-systems/2-legacy-system-healthcare.jpg"></p>
<p>    </a><figcaption class="op-vertical-bottom">
      One of many: a legacy system used by EMR systems in healthcare. (<a href="https://files.smashing.media/articles/how-improve-ux-legacy-systems/2-legacy-system-healthcare.jpg">Large preview</a>)<br />
    </figcaption></figure>
<p>Still, legacy systems must neatly <strong>co-exist within modern digital products</strong> built around them. In many ways, the end result resembles a Frankenstein &mdash; many bits and pieces glued together, often a mixture of modern UIs and painfully slow and barely usable fragments here and there &mdash; especially when it comes to validation, error messages, or processing data.</p>
<h3 id="2-legacy-systems-make-or-break-ux">2. Legacy Systems Make or Break UX</h3>
<p>Once you sprinkle a little bit of quick bugfixing, unresolved business logic issues, and unresponsive layouts, you have a <strong>truly frustrating experience</strong>, despite the enormous effort put into the rest of the application.</p>
<p>If one single step in a complex user flow feels <strong>utterly broken and confusing</strong>, then the entire product appears to be broken as well, despite the incredible efforts the design teams have put together in the rest of the product.</p>
<p>Well, eventually, you’ll have to tackle legacy. And that’s where we need to consider available options for your <strong>UX roadmap</strong>.</p>
<h2 id="ux-roadmap-for-tackling-legacy-projects">UX Roadmap For Tackling Legacy Projects</h2>
<h3 id="don-t-dismiss-legacy-build-on-existing-knowledge">Don’t Dismiss Legacy: Build on Existing Knowledge</h3>
<p>Because legacy systems are often big unknowns that cause a lot of frustration to everyone, from stakeholders to designers to engineers to users. The initial thought might be to remove it entirely and <strong>redesign it from scratch</strong>, but in practice, that’s not always feasible. Big-bang-redesign is a <strong>remarkably expensive</strong> and very time-consuming endeavor.</p>
<figure class="
  
    break-out article__image
  
  
  "></p>
<p>    <a href="https://files.smashing.media/articles/how-improve-ux-legacy-systems/3-questions-ask-legacy-system.png"></p>
<p>    <img loading="lazy" width="800" height="467" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" alt="An overview of questions to ask key stakeholders to understand the legacy system, its key features, workflows, and priorities." class="lazyload" data-src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/how-improve-ux-legacy-systems/3-questions-ask-legacy-system.png"></p>
<p>    </a><figcaption class="op-vertical-bottom">
      First things first: map legacy features, workflows, and priorities as a part of discovery. (<a href="https://files.smashing.media/articles/how-improve-ux-legacy-systems/3-questions-ask-legacy-system.png">Large preview</a>)<br />
    </figcaption></figure>
<p>Legacy systems <strong>hold valuable knowledge</strong> about the business practice, and they do work &mdash; and a new system must perfectly match years of knowledge and customization done behind the scenes. That’s why stakeholders and users (in B2B) are typically <strong>heavily attached to legacy systems</strong>, despite all their well-known drawbacks and pains.</p>
<p>To most people, because such systems are at the very heart of the business, operating on them seems to be extremely risky and will require a significant amount of <strong>caution and preparation</strong>. Corporate users don’t want big risks. So instead of dismissing legacy entirely, we might start by gathering existing knowledge first.</p>
<h3 id="map-existing-workflows-and-dependencies">Map Existing Workflows and Dependencies</h3>
<p>The best place to start is to understand how and where exactly legacy systems are in use. You might discover that some bits of the legacy systems are used all over the place &mdash; not only in your product, but also in business dashboards, by external agencies, and by other companies that integrate your product into their services.</p>
<figure class="
  
    break-out article__image
  
  
  "></p>
<p>    <a href="https://creative.navy/case-studies/ux-ui-design-technical-software-users"></p>
<p>    <img loading="lazy" width="800" height="464" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" alt="An overview of users’ behavior, frequency of use for features, and the complexity of the flow." class="lazyload" data-src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/how-improve-ux-legacy-systems/4-testing-session.jpg"></p>
<p>    </a><figcaption class="op-vertical-bottom">
      Testing sessions to understand where users struggle, and how difficult tasks are to complete for them. From a fantastic case study by <a href="https://creative.navy/case-studies/ux-ui-design-technical-software-users">CreativeNavy</a>. (<a href="https://files.smashing.media/articles/how-improve-ux-legacy-systems/4-testing-session.jpg">Large preview</a>)<br />
    </figcaption></figure>
<p>Very often, legacy systems have dependencies on their own, integrating other legacy systems that might be much older and in a much worse state. Chances are high that you might not even consider them in the big-bang redesign &mdash; mostly because you don’t know just <strong>how many black boxes</strong> are in there.</p>
<figure class="
  
    break-out article__image
  
  
  "></p>
<p>    <a href="https://creative.navy/case-studies/ux-ui-design-technical-software-users"></p>
<p>    <img loading="lazy" width="800" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" alt="An overview of users’ behavior, frequency of use for features, and the complexity of the flow." class="lazyload" data-src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/how-improve-ux-legacy-systems/5-map-workflows-user-behavior.jpg"></p>
<p>    </a><figcaption class="op-vertical-bottom">
      Map existing workflows by tracking user behavior, frequency, desired outcome, complexity, patterns, and user needs. From a fantastic case study by <a href="https://creative.navy/case-studies/ux-ui-design-technical-software-users">CreativeNavy</a>. (<a href="https://files.smashing.media/articles/how-improve-ux-legacy-systems/5-map-workflows-user-behavior.jpg">Large preview</a>)<br />
    </figcaption></figure>
<p>Set up a board to <a href="https://www.linkedin.com/pulse/breaking-down-complexity-task-analysis-ux-vitaly-friedman-sjt4f/">document current workflows and dependencies</a> to get a better idea of how everything works together. Include stakeholders, and <strong>involve heavy users in the conversation</strong>. You won’t be able to open the black box, but you can still shed some light on it from the perspectives of different people who may be relying on legacy for their work.</p>
<figure class="
  
  
  "></p>
<p>    <a href="https://files.smashing.media/articles/how-improve-ux-legacy-systems/6-prioritizing-migrated-features.png"></p>
<p>    <img loading="lazy" width="800" height="375" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" alt="Prioritizing migrated features and features by impact and urgency." class="lazyload" data-src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/how-improve-ux-legacy-systems/6-prioritizing-migrated-features.png"></p>
<p>    </a><figcaption class="op-vertical-bottom">
      Priorities matter. You won’t need to migrate everything, but you need to discover critical parts that must be migrated. (<a href="https://files.smashing.media/articles/how-improve-ux-legacy-systems/6-prioritizing-migrated-features.png">Large preview</a>)<br />
    </figcaption></figure>
<p>Once you’ve done that, set up a meeting to <strong>reflect to users and stakeholders</strong> what you have discovered. You will need to build confidence and trust that you aren’t missing anything important, and you need to visualize the dependencies that a legacy tool has to everyone involved.</p>
<p>Replacing a legacy system is <strong>never about legacy alone</strong>. It’s about the dependencies and workflows that rely on it, too.</p>
<h3 id="choose-your-ux-migration-strategy">Choose Your UX Migration Strategy</h3>
<p>Once you have a <strong>big picture</strong> in front of you, you need to decide on what to do next. Big-bang relaunch or a small upgrade? Which approach would work best? You might <strong>consider the following options</strong> before you decide on how to proceed:</p>
<figure class="
  
  
  "></p>
<p>    <a href="https://files.smashing.media/articles/how-improve-ux-legacy-systems/7-legacy-migration-strategies.jpg"></p>
<p>    <img loading="lazy" width="800" height="804" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" alt="A diagram titled ‘Legacy Migration Strategies’, showing five different approaches to migrating from an old system to a new system using arrows and descriptions." class="lazyload" data-src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/how-improve-ux-legacy-systems/7-legacy-migration-strategies.jpg"></p>
<p>    </a><figcaption class="op-vertical-bottom">
      The different legacy migration strategies. You never migrate just a system &mdash; you also migrate workflows, habits, processes, and ways of working. (<a href="https://files.smashing.media/articles/how-improve-ux-legacy-systems/7-legacy-migration-strategies.jpg">Large preview</a>)<br />
    </figcaption></figure>
<ul>
<li><strong>Big-bang relaunch</strong>.<br />
Sometimes the only available option, but it’s very risky, expensive, and can take years, without any improvements to the existing setup in the meantime.</li>
<li><strong>Incremental migration</strong>.<br />
Slowly retire pieces of legacy by replacing small bits with new designs. This offers quicker wins in a <code>Frankenstein</code> style but can make the system unstable.</li>
<li><strong>Parallel migration</strong>.<br />
Run a public beta of the replacement alongside the legacy system to involve users in shaping the new design. Retire the old system when the new one is stable, but be prepared for the cost of maintaining both.</li>
<li><strong>Incremental parallel migration</strong>.<br />
List all business requirements the legacy system fulfills, then build a new product to meet them reliably, matching the old system from day one. Test early with power users, possibly offering an option to switch systems until the old one is fully retired.</li>
<li><strong>Legacy UI upgrade + public beta</strong>.<br />
Perform low-risk fine-tuning on the legacy system to align UX, while incrementally building a new system with a public beta. This yields quicker and long-term wins, ideal for fast results.</li>
</ul>
<p>Replacing a system that has been carefully refined and heavily customized for a decade is a monolithic task. You can’t just rebuild something from scratch within a few weeks that others have been working on for years.</p>
<p>So whenever possible, try to <strong>increment gradually</strong>, involving users and stakeholders and engineers along the way &mdash; and with enough <strong>buffer time</strong> and <strong>continuous feedback loops</strong>.</p>
<h2 id="wrapping-up">Wrapping Up</h2>
<p>With legacy projects, failure is often not an option. You’re migrating not just components, but <strong>users and workflows</strong>. Because you operate on the <strong>very heart of the business</strong>, expect a lot of attention, skepticism, doubts, fears, and concerns. So build <strong>strong relationships</strong> with key stakeholders and key users and share ownership with them. You will need their support and their buy-in to bring your UX work in action.</p>
<p>Stakeholders will request old and new features. They will focus on <strong>edge cases, exceptions, and tiny tasks</strong>. They will question your decisions. They will send mixed signals and change their opinions. And they will expect the new system to run flawlessly from day one.</p>
<p>And the best thing you can do is to work with them throughout the entire design process, right from the very beginning. Run a successful pilot project to <strong>build trust</strong>. Report your progress repeatedly. And account for <strong>intense phases of rigorous testing</strong> with legacy users.</p>
<p>Revamping a legacy system is a tough challenge. But there is rarely any project that can have so much impact on such a scale. Roll up your sleeves and get through it successfully, and your team will be <strong>remembered, respected, and rewarded</strong> for years to come.</p>
<h2 id="meet-measure-ux-amp-design-impact">Meet “Measure UX &amp; Design Impact”</h2>
<p>Meet <a href="https://measure-ux.com"><strong>Measure UX &amp; Design Impact</strong></a>, Vitaly’s practical guide <strong>for designers and UX leads</strong> on how to track and visualize the incredible <strong>impact</strong> of your UX work on business &mdash; with a <a href="https://smashingconf.com/online-workshops/workshops/vitaly-friedman-impact-design/">live UX training</a> later this year. <a href="https://measure-ux.com/">Jump to details</a>.</p>
<figure class="break-out" style="margin-bottom:0;padding-bottom:0">
    <a href="https://measure-ux.com/" title="How To Measure UX and Design Impact, with Vitaly Friedman"><br />
    <img loading="lazy" width="900" height="466" style="border-radius: 11px" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" alt="How to Measure UX and Design Impact, with Vitaly Friedman." class="lazyload" data-src="https://computercoursesonline.com/wp-content/uploads/2025/04/measure-ux-and-design-impact-course.png"><br />
    </a><figcaption class="op-vertical-bottom">Meet <a href="https://measure-ux.com/">Measure UX and Design Impact</a>, a practical video course for designers and UX leads.</figcaption></figure>
<div class="book-cta__inverted">
<div class="book-cta" data-handler="ContentTabs" data-mq="(max-width: 480px)">
<nav class="content-tabs content-tabs--books">
<ul>
<li class="content-tab"><a href="#"><button class="btn btn--small btn--white btn--white--bordered"><br />
Video + UX Training</button></a></li>
<li class="content-tab"><a href="#"><button class="btn btn--small btn--white btn--white--bordered">Video only</button></a></li>
</ul>
</nav>
<div class="book-cta__col book-cta__hardcover content-tab--content">
<h3 class="book-cta__title"><span>Video + UX Training</span></h3>
<p><span class="book-cta__price"><span><span class=""><span class="currency-sign">$</span>&nbsp;<span>495<sup class="sup">.00</sup></span></span> <span class="book-cta__price--old"><span class="currency-sign">$</span>&nbsp;<span>799<sup class="sup">.00</sup></span></span></span></span><br />
<a href="https://smart-interface-design-patterns.thinkific.com/enroll/3081832?price_id=3951439" class="btn btn--full btn--medium btn--text-shadow"><br />
Get Video + UX Training</p>
<div></div>
<p></a></p>
<p class="book-cta__desc">25 video lessons (8h) + <a href="https://smashingconf.com/online-workshops/workshops/vitaly-friedman-impact-design/">Live UX Training</a>.<br />100 days money-back-guarantee.</p>
</div>
<div class="book-cta__col book-cta__ebook content-tab--content">
<h3 class="book-cta__title"><span>Video only</span></h3>
<div data-audience="anonymous free supporter" data-remove="true"><span class="book-cta__price" data-handler="PriceTag"><span><span class=""><span class="currency-sign">$</span>&nbsp;<span>250<sup class="sup">.00</sup></span></span><span class="book-cta__price--old"><span class="currency-sign">$</span>&nbsp;<span>350<sup class="sup">.00</sup></span></span></span></span></div>
<p><a href="https://smart-interface-design-patterns.thinkific.com/enroll/3081832?price_id=3950630" class="btn btn--full btn--medium btn--text-shadow"><br />
Get the video course</p>
<div></div>
<p></a></p>
<p class="book-cta__desc" data-audience="anonymous free supporter" data-remove="true">25 video lessons (8h). Updated yearly.<br />Also available as a <a href="https://smart-interface-design-patterns.thinkific.com/enroll/3570306?price_id=4503439">UX Bundle with 3 video courses.</a></p>
</div>
<p><span></span></div>
</div>
<h2 id="useful-resources">Useful Resources</h2>
<ul>
<li><a href="https://blog.scottlogic.com/2021/07/16/UX-Migration-Strategy.html">UX Migration Strategy For Legacy Apps</a>, by Tamara Chehayeb Makarem</li>
<li><a href="https://uxdesign.cc/to-improve-legacy-systems-sometimes-you-need-to-take-a-restoration-mindset-d72f7b69442f?sk=v2%2F524df15a-3aca-48f6-adff-98588a64bda0">How To Improve Legacy Systems</a>, by Christopher Wong</li>
<li><a href="https://medium.com/enterprise-ux/designing-with-legacy-d0e4bef0d9ea">Designing With Legacy</a>, by Peter Zalman</li>
<li><a href="https://medium.com/design-bootcamp/redesigning-a-legacy-system-for-a-large-organisation-5089429f7e2e">Redesigning A Large Legacy System</a>, by Pawel Halicki</li>
<li><a href="https://understandlegacycode.com">How To Manage Legacy Code</a>, by Nicolas Carlo</li>
<li><a href="https://www.koruux.com/blog/transforming-legacy-system/">How To Transform Legacy</a>, by Bansi Mehta</li>
<li><a href="https://www.debt.design/">Design Debt 101</a>, by Alicja Suska</li>
<li><a href="https://www.linkedin.com/posts/vitalyfriedman_ux-enterprise-activity-7128696386841120769-VcPD">Practical Guide To Enterprise UX</a>, by Yours Truly</li>
<li><a href="https://www.linkedin.com/posts/vitalyfriedman_ux-design-healthcare-activity-7124347175395815424-Q8Xn">Healthcare UX Design Playbook</a>, by Yours Truly</li>
</ul>
<div class="signature">
  <img src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" alt="Smashing Editorial" width="35" height="46" loading="lazy" class="lazyload" data-src="https://www.smashingmagazine.com/images/logo/logo--red.png"><br />
  <span>(yk)</span>
</div>
</article>
]]></content:encoded>
					
					<wfw:commentRss>http://computercoursesonline.com/index.php/2026/04/10/how-to-improve-ux-in-legacy-systems/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>
