<?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>JavaScript Archives - The NOKIA Blog</title>
	<atom:link href="https://thenokiablog.com/tag/javascript/feed/" rel="self" type="application/rss+xml" />
	<link>https://thenokiablog.com/tag/javascript/</link>
	<description></description>
	<lastBuildDate>Tue, 06 Oct 2026 10:00:00 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=7.1.3</generator>

<image>
	<url>https://thenokiablog.com/wp-content/uploads/2024/03/cropped-Cjdowner-Cryptocurrency-Flat-ICON-ICX.512-32x32.png</url>
	<title>JavaScript Archives - The NOKIA Blog</title>
	<link>https://thenokiablog.com/tag/javascript/</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>A Practical Guide To Naming Things</title>
		<link>https://thenokiablog.com/a-practical-guide-to-naming-things/</link>
		
		<dc:creator><![CDATA[Mister Nokia]]></dc:creator>
		<pubDate>Tue, 06 Oct 2026 10:00:00 +0000</pubDate>
				<category><![CDATA[Design]]></category>
		<category><![CDATA[Color]]></category>
		<category><![CDATA[CSS]]></category>
		<category><![CDATA[Developer]]></category>
		<category><![CDATA[Figma]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[UI Components]]></category>
		<guid isPermaLink="false">https://thenokiablog.com/a-practical-guide-to-naming-things/</guid>

					<description><![CDATA[<p>A practical guide to naming UI components, with useful resources, naming conventions, and structures. Brought to...</p>
<p>The post <a href="https://thenokiablog.com/a-practical-guide-to-naming-things/">A Practical Guide To Naming Things</a> appeared first on <a href="https://thenokiablog.com">The NOKIA Blog</a>.</p>
]]></description>
										<content:encoded><![CDATA[<div class="c-garfield-summary">
<section aria-label="Quick summary" class="article__summary"><span id="article__start" class="summary__heading" aria-hidden="true"></span>A practical guide to naming UI components, with useful resources, naming conventions, and structures. Brought to you by Design Patterns For AI Interfaces, <strong>friendly video courses on UX</strong> and design patterns by Vitaly.</section>
</div>
<p>Naming is hard. The language we use shapes the way we think about things, but also the conversation we have about it. That&rsquo;s why we have so much confusion about colors, icons, UI components, features &mdash; and that&rsquo;s why we struggle to name design tokens, HTML classes, and variables.</p>
<p>Sometimes, the names we choose are <strong>too generic</strong>, so it&rsquo;s difficult to understand what exactly is meant. And sometimes they are <strong>too specific</strong>, leaving little room for flexibility and reuse. How do we choose the right names? And which naming conventions work best? Let&rsquo;s take a closer look.</p>
<h2 id="how-to-name-things">How To Name Things</h2>
<p>If you&rsquo;re looking for some inspiration for naming HTML classes, CSS properties, or JavaScript functions, Classnames is a wonderful resource jam-packed with ideas that get you <strong>thinking outside the box</strong>.</p>
<figure><img decoding="async" loading="lazy" width="800" height="887" src="https://thenokiablog.com/wp-content/uploads/2026/10/a-practical-guide-to-naming-things.jpg" alt="Inspiration For Naming Things"><figcaption class="op-vertical-bottom">Classnames provides thematically grouped lists of words you can use for naming. (Large preview)</figcaption></figure>
<p>The site provides thematically grouped lists of words perfect for naming. You&rsquo;ll find terms to describe different kinds of behavior, likeness between things, order, grouping, and association, but also <strong>themed collections of words</strong> that wouldn&rsquo;t instantly come to one&rsquo;s mind when it comes to code, among them words from nature, art, theater, music, architecture, fashion, and publishing.</p>
<h2 id="how-to-name-colors">How To Name Colors</h2>
<div class="internal-linking-related-contents"><a href="https://thenokiablog.com/rose-gold-design/" class="template-2"><span class="cta">Read more</span><span class="postTitle">Rose gold design</span></a></div><p>How would you choose the right name for a given color? Fortunately, David Aerne maintains a tremendous repository of color names, currently with <strong>30355 unique color names</strong>, sourced from various references and 1000s of user contributions.</p>
<figure><img decoding="async" loading="lazy" width="800" height="768" src="https://thenokiablog.com/wp-content/uploads/2026/10/a-practical-guide-to-naming-things-1.jpg" alt="Inspiration For Naming Colors"><figcaption class="op-vertical-bottom">Color Parrot provides a name for a given color (originally via a tweet, but you can type it in the URL as well). (Large preview)</figcaption></figure>
<p>Of course, the repository comes with a color picker and name search and plenty of other color pickers as well. Tremendous work, which is also a fantastic reference to keep nearby.</p>
<h2 id="how-to-name-layers-and-groups">How To Name Layers and Groups</h2>
<p>What makes a good name? Javier Cuello summarized a set of naming best practices that help you name your <strong>layers, groups, and components</strong> in a consistent and scalable way.</p>
<figure><img decoding="async" loading="lazy" width="800" height="760" src="https://thenokiablog.com/wp-content/uploads/2026/10/a-practical-guide-to-naming-things-2.jpg" alt="Inspiration For Naming Things"><figcaption class="op-vertical-bottom">Javier Cuello explores in all the fine details what makes an effective name. (Large preview)</figcaption></figure>
<p>As Javier points out, a good name has a logical structure, is short, meaningful, known by everyone, and not related to visual properties. He shares <strong>do&rsquo;s and don&rsquo;ts</strong> to illustrate how to achieve that and also takes a closer look at all the fine little details you need to consider when naming sizes, colors, groups, layers, and components.</p>
<h2 id="how-to-name-design-tokens">How To Name Design Tokens</h2>
<div class="internal-linking-related-contents"><a href="https://thenokiablog.com/passion-work/" class="template-2"><span class="cta">Read more</span><span class="postTitle">Passion &amp; work</span></a></div><p>How to build a flexible design token taxonomy that works across different products? That was the challenge that the team at Intuit faced. The parent company of products such as Mailchimp, QuickBooks, TurboTax, and Mint developed a <strong>flexible token system</strong> that goes beyond the brand theme to serve as the foundational system for a wide array of products.</p>
<figure><img decoding="async" loading="lazy" width="800" height="391" src="https://thenokiablog.com/wp-content/uploads/2026/10/a-practical-guide-to-naming-things.png" alt="Insiration For Naming Things"><figcaption class="op-vertical-bottom">Nate Baldwin shares insights into Intuit&rsquo;s flexible design token taxonomy. (Large preview)</figcaption></figure>
<p>Nate Baldwin wrote a case study in which he shares valuable insights into the making of <strong>Intuit&rsquo;s design token taxonomy</strong>. It dives deeper into the pain points of the old taxonomy system, the criteria they defined for the new system, and how it was created. Lots of takeaways for building your own robust and flexible token taxonomy are guaranteed.</p>
<h2 id="frequent-names-for-ui-components">Frequent Names For UI Components</h2>
<p>Looking at what other people call similar things is a great way to start when you&rsquo;re <strong>struggling with naming</strong>. And what better source could there be than other design systems? Before you end up in the design system rabbit hole, Iain Bean did the research for you and created the Component Gallery.</p>
<figure><img decoding="async" loading="lazy" width="800" height="468" src="https://thenokiablog.com/wp-content/uploads/2026/10/a-practical-guide-to-naming-things-1.png" alt="Inspiration For Naming Things"><figcaption class="op-vertical-bottom">The Component Gallery collects interface components from real-world design systems. (Large preview)</figcaption></figure>
<p>It&rsquo;s a collection that <strong>includes plenty of examples</strong> for more than 50 UI components &mdash; from accordion to visually hidden &mdash; and also lists other names that the UI components go by. A fantastic resource, and not only with regard to naming.</p>
<blockquote><p>Bonus: Name That UI, a <strong>visual dictionary</strong> of common UI components.</p></blockquote>
<h2 id="how-to-name-new-features">How To Name New Features</h2>
<div class="internal-linking-related-contents"><a href="https://thenokiablog.com/the-joy-of-art/" class="template-2"><span class="cta">Read more</span><span class="postTitle">The joy of art</span></a></div><p>Features often have low adoption due to <strong>poor discoverability</strong>. Feature adoption needs time &mdash; first the feature must be discovered, then understood, then tried, learned, and eventually built into <strong>existing workflow</strong>. Erin Gannon has written a helpful practical guide on how to make sure all of it happens.</p>
<figure><img decoding="async" loading="lazy" width="800" height="1298" src="https://thenokiablog.com/wp-content/uploads/2026/10/a-practical-guide-to-naming-things-3.jpg" alt="So What Do We Call That Feature?: illustrated guide to naming features that people discover, understand and actually use"><figcaption class="op-vertical-bottom">&lsquo;What Do We Call This Thing?&rsquo; by Erin Gannon: practical guidelines on naming features people can discover, understand and actually use. (Large preview)</figcaption></figure>
<p>Good names are driven by user needs and problems. They should signal the value or outcome of a feature (the job-to-be-done), and &mdash; most importantly! &mdash; tap into the user&rsquo;s own language. So ask people to explain a feature in their own words and then use the words they use.</p>
<h2 id="how-to-name-variables-taxonomy-map">How To Name Variables (Taxonomy Map)</h2>
<p>A wonderful example of naming guidelines for a complex multi-brand, multi-themed design system comes from the Vodafone UK Design System team. Their Variables Taxonomy Map breaks down the <strong>anatomy and categorization of a design token</strong> into a well-orchestrated system of collections.</p>
<figure><img decoding="async" loading="lazy" width="800" height="450" src="https://thenokiablog.com/wp-content/uploads/2026/10/a-practical-guide-to-naming-things-2.png" alt="Inspiration For Naming Things"><figcaption class="op-vertical-bottom">Vodafone&rsquo;s Variables Taxonomy Map accommodates a complex multi-brand, multi-themed design system. (Large preview)</figcaption></figure>
<p>The map illustrates four collections required to support the system and connections between tokens &mdash; <strong>from brand and primitives to semantics and pages</strong>. It builds on top of Nathan Curtis&rsquo; work on naming design tokens and enables everyone to gather insight about where a token is used and what it represents, just from its name.</p>
<p>If you want to explore more approaches to naming design tokens, I&rsquo;ve put together a list of useful Figma kits and resources that are worth checking out.</p>
<h2 id="design-token-naming-guide-inventory">Design Token Naming Guide + Inventory</h2>
<p>Romina Kavcic created a helpful little tool for <strong>naming design tokens</strong> &mdash; from learning how they work and how to name them well to configuring your own naming structure with components, categories, states, and roles.</p>
<figure><img decoding="async" loading="lazy" width="800" height="608" src="https://thenokiablog.com/wp-content/uploads/2026/10/a-practical-guide-to-naming-things-4.jpg" alt="Free Design Token Naming Guide + Builder &mdash; interactive tool for building your own token naming structure with components, categories, states and roles"><figcaption class="op-vertical-bottom">Free Design Token Naming Guide + Builder. (Large preview)</figcaption></figure>
<blockquote><p>Bonus: The Design Token Names Inventory spreadsheet, with a simple structure with four levels to give you a bird&rsquo;s-eye view of all your design tokens. You can easily add rows, themes, and modes without losing track, and <strong>filter your tokens</strong>.</p></blockquote>
<h2 id="naming-products-or-services">Naming Products or Services</h2>
<p>If you are looking for the right name for your product or service, Onym is a fantastic <strong>open-source repository</strong> that organizes tools and resources for finding names for brainstorming, vetting, guides, sprints, etymologies &mdash; all the way to cautionary tales and agencies that name things, as well as useful books to take a look at.</p>
<figure><img decoding="async" loading="lazy" width="800" height="795" src="https://thenokiablog.com/wp-content/uploads/2026/10/a-practical-guide-to-naming-things-5.jpg" alt="Inspiration For Naming Things"><figcaption class="op-vertical-bottom">The Onym, a neat little helper with tools, methodologies and prompts to encourage new ways of thinking. (Large preview)</figcaption></figure>
<h2 id="useful-bookmarks-for-naming-things">Useful Bookmarks for Naming Things</h2>
<p>A quick-reference list of tools and guides for naming just about&hellip; well, <em>anything</em>, in one place:</p>
<h2 id="wrapping-up">Wrapping Up</h2>
<p>The right name is the one that&rsquo;s well-understood and used by the team and users. So much time is wasted speaking about the very same thing but in <strong>different dialects</strong> &mdash; the designer&rsquo;s dialect, the developer&rsquo;s dialect, the product manager&rsquo;s dialect, and the user&rsquo;s dialect. That&rsquo;s what often causes frustration and confusion.</p>
<p>So if you see low adoption, it might be merely because of the name. And pay attention to potential <strong>naming conflicts</strong> and put them in a backlog of things to address later. It&rsquo;s one of those fine little details that can reduce frustration and confusion and help teams work together better.</p>
<h2 id="meet-design-patterns-for-ai-interfaces">Meet &ldquo;Design Patterns For AI Interfaces&rdquo;</h2>
<p>Meet Design Patterns For AI Interfaces, Vitaly&rsquo;s new <strong>video course</strong> with practical examples from real-life products &mdash; with a live UX training happening soon. Jump to a free preview.</p>
<figure class="article__image"><img decoding="async" loading="lazy" width="800" height="414" src="https://thenokiablog.com/wp-content/uploads/2026/10/a-practical-guide-to-naming-things-3.png" alt="Design Patterns For AI Interfaces promo picture"><figcaption class="op-vertical-bottom">Meet Design Patterns For AI Interfaces, Vitaly&rsquo;s video course on interface design &amp; UX.</figcaption></figure>
<div class="signature"><img decoding="async" src="https://thenokiablog.com/wp-content/uploads/2026/10/a-practical-guide-to-naming-things-4.png" alt="Smashing Editorial" width="35" height="46" loading="lazy"><br>
<span>(yk)</span></div>
<p>The post <a href="https://thenokiablog.com/a-practical-guide-to-naming-things/">A Practical Guide To Naming Things</a> appeared first on <a href="https://thenokiablog.com">The NOKIA Blog</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Stop Treating CSS Container Queries Like Traditional Media Queries</title>
		<link>https://thenokiablog.com/stop-treating-css-container-queries-like-traditional-media-queries/</link>
		
		<dc:creator><![CDATA[Mister Nokia]]></dc:creator>
		<pubDate>Wed, 16 Sep 2026 13:00:00 +0000</pubDate>
				<category><![CDATA[Design]]></category>
		<category><![CDATA[Color]]></category>
		<category><![CDATA[Containers]]></category>
		<category><![CDATA[CSS]]></category>
		<category><![CDATA[Events]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[News]]></category>
		<guid isPermaLink="false">https://thenokiablog.com/stop-treating-css-container-queries-like-traditional-media-queries/</guid>

					<description><![CDATA[<p>Despite broad browser support, container queries remain surprisingly underused and frequently misunderstood. Let&#8217;s look at how...</p>
<p>The post <a href="https://thenokiablog.com/stop-treating-css-container-queries-like-traditional-media-queries/">Stop Treating CSS Container Queries Like Traditional Media Queries</a> appeared first on <a href="https://thenokiablog.com">The NOKIA Blog</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p><section aria-label="Quick summary" class="article__summary"><span id="article__start" class="summary__heading" aria-hidden="true"></span>Despite broad browser support, container queries remain surprisingly underused and frequently misunderstood. Let&rsquo;s look at how they differ from media queries, when to reach for each, and how container queries help reusable components respond naturally to the contexts in which they appear.</section>
</p>
<p>To be completely honest with you, I missed the news when CSS Container Queries first shipped. And when I finally heard about it, my very first thought was, <em>&ldquo;Why exactly do I need this when media queries already exist?&rdquo;</em></p>
<p>I&rsquo;m not proud of that reaction, knowing what I know now, but it was comforting to know that I wasn&rsquo;t alone. In fact, there are legions of us out there.</p>
<p>What baffles me is that container queries aren&rsquo;t a new feature, as it currently sits at around 94% browser support. And yet, very few people are actually using it. According to the State of CSS survey, 86% of developers are aware of container queries, but only 41.4% actually use them. Surveys can be biased and not completely representative of our entire field, but this one is certainly the best indicator we&rsquo;ve got.</p>
<div class="internal-linking-related-contents"><a href="https://thenokiablog.com/rose-gold-design/" class="template-2"><span class="cta">Read more</span><span class="postTitle">Rose gold design</span></a></div><p>Kevin Powell also talked about this at SmashingConf Amsterdam 2026: <strong>Container Queries adoption has been terrible.</strong> And that is so strange to me, knowing that the ability for components to adapt to the size of their outer container has been at the top of so many CSS wishlists over the years.</p>
<p>I&rsquo;m not particularly interested in how many people are using container queries as much as in <em>how</em> they are using them. I can&rsquo;t account for everyone, but from what I&rsquo;ve seen &mdash; including in my own early attempts &mdash; many of us are using them <em>wrong</em>.</p>
<p>The bottom line is that incorrect use comes down to the same impression I had when learning about them: <strong>they absolutely look just like media queries at first glance</strong>. And since they look similar, it&rsquo;s easy to assume they serve similar purposes and work the same way.</p>
<p>They don&rsquo;t.</p>
<div class="internal-linking-related-contents"><a href="https://thenokiablog.com/passion-work/" class="template-2"><span class="cta">Read more</span><span class="postTitle">Passion &amp; work</span></a></div><p><strong>Note</strong>: I should state up front that what I&rsquo;m focusing on in this article is using container <em>size</em> queries, i.e., a responsive design technique for responding to the size of a particular container. There are also container <em>style</em> queries that respond to a container&rsquo;s computed styles (and are experimental at the time of this writing). You can catch up on those in Juan Diego&rsquo;s piece here on Smashing Magazine where he examines their possible use cases.</p>
<div data-audience="non-subscriber" data-remove="true" class="feature-panel-container"><img decoding="async" loading="lazy" class="feature-panel-image-img" src="https://thenokiablog.com/wp-content/uploads/2026/09/stop-treating-css-container-queries-like-traditional-media-queries.png" alt="Feature Panel" width="480" height="697"></div>


<p>The viewport is a proxy. It always has been. Media queries are what gave us the illusion that screen width alone is responsible for how responsive apps adapt to their environment.</p>
<p>Ask yourself this: When you write <code>@media</code>, what are you asking the browser?</p>
<pre><code class="language-css">@media (min-width: 1024px) {
  .card {
    display: flex;
  }
</code></pre>
<p>I, like most developers, am asking the browser: <em>How wide is the screen right now?</em> That&rsquo;s it.</p>
<div class="internal-linking-related-contents"><a href="https://thenokiablog.com/the-joy-of-art/" class="template-2"><span class="cta">Read more</span><span class="postTitle">The joy of art</span></a></div><p>Media queries answer that beautifully, but what happens when this <code>.card</code> component is placed in a grid cell that&rsquo;s <code>300px</code> wide on a <code>1920px</code> desktop screen?</p>
<p>The media query doesn&rsquo;t care; it does its job. The viewport is still <code>1920px</code>, so <code>min-width: 1024px</code> fires and the matched query styles are applied, even though the card only has <code>300px</code> of space to work with. Eventually, everything in the card deforms, overflows, or cramps up.</p>
<blockquote><p>&ldquo;Media queries are dumb. Not dumb in terms of the concept, but dumb in that they don&rsquo;t know very much. In fact, most people assume that they know more than they do.&rdquo;</p>
<p>&mdash; Kevin Powell</p>
</blockquote>
<p>It&rsquo;s common to think of responsive design purely as a system for updating complete page layouts, like going from two columns on a large screen to a single column on a small screen.</p>
<h2 id="container-queries-look-inward">Container Queries Look Inward</h2>
<p>Container queries are smarter than that. They make responsive layouts more reliant on what&rsquo;s happening <strong>inside</strong> a component rather than on the outer context that has no insight into a component&rsquo;s contents. It is more like: <em>&ldquo;How much space is available for me in this specific spot, right now?&rdquo;</em></p>
<p>Here is the same card code example we looked at in the last section, but with a container query:</p>
<pre><code class="language-css">.card-wrapper {
  container-name: card;
  container-type: inline-size;
}

@container card (min-width: 450px) {
  .card {
    display: flex;
    flex-direction: row;
  }
}
</code></pre>
<p>This changes everything. The card isn&rsquo;t influenced by the viewport; its only concern is whether the <code>.card</code> component&rsquo;s parent wrapper has at least <code>450px</code> of inline (i.e., horizontal in a left-to-right writing mode) space. If that condition is true, the component goes horizontal; if not, it goes to its default <code>block</code> display.</p>
<figure class="break-out">
<p data-height="480" data-theme-id="light" data-slug-hash="RNpoLNX" data-user="smashingmag" data-default-tab="result" class="codepen">See the Pen [Viewport vs Container [forked]](https://codepen.io/smashingmag/pen/01a0a73d-6f39-709f-8dae-037f6b588852) by Vayo.</p><figcaption>See the Pen Viewport vs Container [forked] by Vayo.</figcaption></figure>
<h2 id="macro-layout-vs-micro-layouts">&ldquo;Macro&rdquo; Layout Vs. &ldquo;Micro&rdquo; Layouts</h2>
<p>A very interesting way to distinguish <code>@media</code> and <code>@container</code> queries is the <strong>layout type</strong>.</p>
<p>Media queries are for the &ldquo;macro&rdquo; layout; they look outward. Stuff like page structure, the header that spans the window, footers, main grid layout, system preferences (<code>prefers-color-scheme</code>), device capabilities (touch screens). You know, anything that is majorly true to the entire page structure.</p>
<p>Container queries, I&rsquo;d say, are for &ldquo;micro&rdquo; layouts, i.e., most things that live inside the &ldquo;macro&rdquo; layout. We use them when any content needs to responsively fit whatever space it is allocated. Components that come to mind are things like cards, widgets, forms, navigation, and so on.</p>
<p>In other words, think <strong>&ldquo;page layout&rdquo; vs. &ldquo;component layout&rdquo;</strong>.</p>
<p>An element shouldn&rsquo;t magically become &ldquo;tablet-sized&rdquo; just because the width exceeds an abstract width threshold like <code>768px</code>. Instead, it should switch layout when it has enough space to do so, whether that happens on a mobile viewport or inside a desktop sidebar.</p>
<p>In case you&rsquo;re still not convinced, did you know there are over 2,300 unique viewport sizes on the modern web? Do you think it is possible to account for all of them?</p>
<blockquote class="pull-quote">
<p>I&rsquo;m not hating on media queries. It&rsquo;s that in this era of responsiveness and component re-use, layout logic is closer to the container than the viewport. When we think in terms of containers and components, we&rsquo;re effectively relying on the content to determine layout, not the viewport.</p>
</blockquote>
<p>This is how it should be.</p>
<h2 id="example-fluid-typography-inside-a-component">Example: Fluid Typography Inside A Component</h2>
<p>Responsive typography is a good example of something many of us have relied on media queries for. The fact that media queries come with their own CSS units &mdash; e.g., <code>vw</code>, <code>vh</code>, and so on &mdash; that are relative to the viewport size makes media queries look really good for adjusting font size based on the user&rsquo;s screen size.</p>
<pre><code class="language-css">.card-title {
  font-size: clamp(100%, 1rem + 2vw, 24px);
}
</code></pre>
<p>This works until that same component is moved into a different context, like a sidebar, where the viewport is completely irrelevant. Now, because we tied the responsiveness to the wrong reference point, the scaled typography can get too big or too small.</p>
<p>Container queries come with their very own units &mdash; <code>cqi</code>, <code>cqw</code>, <code>cqb</code>, among others &mdash; and we can take responsive components further by coupling those units with the CSS <code>clamp()</code> function, using it for fluid typography that scales with the component rather than the viewport:</p>
<pre><code class="language-css">
.card-title {
  font-size: clamp(1rem, .5rem + 3cqi, 2rem);
}
</code></pre>
<figure class="break-out">
<p data-height="480" data-theme-id="light" data-slug-hash="rayWGVd" data-user="smashingmag" data-default-tab="result" class="codepen">See the Pen [Fluid Typography [forked]](https://codepen.io/smashingmag/pen/01a0a73f-1a44-750c-8b40-9b0ab774d981) by Vayo.</p><figcaption>See the Pen Fluid Typography [forked] by Vayo.</figcaption></figure>
<p>With this in place, the entire code is self-contained to that element&rsquo;s specific container.</p>
<h2 id="example-flexbox-wrap-detection">Example: Flexbox Wrap Detection</h2>
<p>Interestingly, container queries can, in a way, detect the state of a component&rsquo;s internal layout. It&rsquo;s not bulletproof, but it is also something media queries simply cannot do because they only observe the external browser window and are structurally blind to internal layout events like when, for example, flex items wrap onto a new line. Let&rsquo;s poke at that.</p>
<p>Flexbox is superb at wrapping content (<code>flex-wrap: wrap</code>), allowing flex items to automatically wrap to new lines when the flex container runs out of space to fit them in a single row. But CSS by itself can&rsquo;t tell when that wrap happens. There isn&rsquo;t something like a <code>:wrapped</code> pseudo-class or a media query like <code>@media (flex-wrapped: true)</code> that would get us there.</p>
<p>Media queries only observe the browser window, as they can&rsquo;t see internal changes. That is pretty much what flex wrapping is: a width state change on the item itself.</p>
<p>For example, if you have a horizontal menu that is lined up with flexbox and you want the items to restyle themselves only when wrapped, you&rsquo;d be in JavaScript territory, using <code>ResizeObserver</code> to get that information. However, when we nest container queries inside flex items, we can come up with a workaround to get what we need without JavaScript, thanks to this technique I learned from Kevin Powell. The core idea is to allow a flex item to <code>flex-grow: 1</code> when we query the container&rsquo;s inline size.</p>
<figure class="break-out">
<p data-height="480" data-theme-id="light" data-slug-hash="rayWGVd" data-user="smashingmag" data-default-tab="result" class="codepen">See the Pen [Fluid Typography [forked]](https://codepen.io/smashingmag/pen/01a0a73f-1a44-750c-8b40-9b0ab774d981) by Vayo.</p><figcaption>See the Pen Fluid Typography [forked] by Vayo.</figcaption></figure>
<figure><img decoding="async" loading="lazy" width="800" height="674" src="https://thenokiablog.com/wp-content/uploads/2026/09/stop-treating-css-container-queries-like-traditional-media-queries.jpg" alt="Normal and wrapped states of two responsive items."><figcaption class="op-vertical-bottom">(Large preview)</figcaption></figure>
<p>The logic works like this:</p>
<ol>
<li>When there&rsquo;s enough room, both items (<code>.flex-item</code>) sit side-by-side, each exactly half the parent container&rsquo;s width.</li>
<li>When there is limited space, the second item wraps to the next line.</li>
<li>Because <code>flex-grow</code> is active on each item, the wrapped items stretch to fill most of the parent&rsquo;s width.</li>
<li>If the item is a container itself, it detects the sudden width expansion and fires.</li>
</ol>
<pre><code class="language-css">/* The flex parent */
.flex-layout {
  display: flex;
  flex-wrap: wrap;
}

/* Register a flex item as a container */
.flex-item {
  container-type: inline-size;
  flex: 1 1 390px; /* Grow to fill space, wrap at 390px */
}

/* Default Card Styles (narrow / side-by-side) */
.card {
  display: flex;
  flex-direction: column;
  background: #f4f4f4;
}

/* Once there's enough room for a full row */
@container (min-width: 600px) {
  .card {
    flex-direction: row;
    align-items: center;
    background: #e2f0d9;
  }
}
</code></pre>
<p>This works. As the parent size shrinks and the cards wrap to two lines, the card item expands, the container query fires, and applies the necessary styles.</p>
<figure class="break-out">
<p data-height="480" data-theme-id="light" data-slug-hash="ByWQwLz" data-user="smashingmag" data-default-tab="result" class="codepen">See the Pen [Flex Wrap Detection Using Container Queries [forked]](https://codepen.io/smashingmag/pen/01a0a752-150a-754e-a77a-07fe1b046658) by Vayo.</p><figcaption>See the Pen Flex Wrap Detection Using Container Queries [forked] by Vayo.</figcaption></figure>
<h2 id="container-queries-do-have-side-effects">Container Queries Do Have Side Effects</h2>
<p>Container queries, like all things, come with some side effects or caveats you will want to watch for before reaching for them.</p>
<p>Container queries need something similar to a parent-child relationship to function as expected. Let&rsquo;s say we have this markup:</p>
<pre><code class="language-html">&lt;div class="card"&gt;
  &lt;div class="card-content"&gt;...&lt;/div&gt;
&lt;/div&gt;
</code></pre>
<p>We can&rsquo;t actually query the <code>.card</code> component to adjust the <code>.card-content</code>, like this:</p>
<pre><code class="language-css">/* DOES NOT WORK */
.card {
  container-name: card;
  container-type: inline-size;
}

@container card (min-width: 400px) {
  .card {
    display: flex;
  }
}
</code></pre>
<p>This doesn&rsquo;t work because a container cannot query itself. In that last example, we&rsquo;re querying a <code>card</code> container and then attempting to adjust that container&rsquo;s display based on its size. It&rsquo;s an infinite loop.</p>
<p>Instead, we need an additional wrapper that makes the <code>.card</code> a descendant of the container:</p>
<pre><code class="language-html">&lt;div class="cards"&gt;
  &lt;div class="card"&gt;
    &lt;div class="card-content"&gt;...&lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;
</code></pre>
<p>From there, we can query the <code>.cards</code> container and adjust the <code>.card</code> layout accordingly:</p>
<pre><code class="language-css">.cards {
  container-name: cards;
  container-type: inline-size;
}

@container cards (min-width: 400px) {
  .card {
    display: flex;
  }
}
</code></pre>
<p>In media queries, this doesn&rsquo;t matter as <code>@media</code> does not care which element you style inside the block; its only concern is the viewport, which is always available. So you can just slap a condition on any element and call it a day.</p>
<h3 id="2-querying-a-container-s-size-could-collapse-your-layout">2. Querying A Container&rsquo;s <code>size</code> Could Collapse Your Layout</h3>
<p>This happens when querying the container&rsquo;s <code>size</code> (i.e., its block, or vertical, size) instead of its <code>inline-size</code>:</p>
<pre><code class="language-css">/* Collapses to 0px even if it has content inside */
.hero-banner {
  container-type: size;
}
</code></pre>
<p>Why? Because the browser calculates the container&rsquo;s dimensions without looking at its children. If we don&rsquo;t give the <code>.hero-banner</code> an explicit <code>height</code> (or <code>min-height</code> or <code>aspect-ratio</code>), the browser sets a height of <code>0px</code>.</p>
<p>For that reason, it&rsquo;s often better to query a container by its <code>inline-size</code> instead. That is, unless you genuinely need to query the container&rsquo;s block size.</p>
<p>Media queries aren&rsquo;t affected by this, as they treat height the same way they treat <code>@media (min-height: ...)</code> does, i.e., ask the viewport and move on.</p>
<h3 id="3-queries-cannot-accept-custom-properties">3. Queries Cannot Accept Custom Properties</h3>
<p>Another container query limitation: we can&rsquo;t query against a custom property value:</p>
<pre><code class="language-css">:root {
  --breakpoint-lg: 1600px;
}

/* DOES NOT WORK */
@container (min-width: var(--breakpoint-lg)) {
  /* ... */
}
</code></pre>
<p>This is because custom properties depend on values that cascade down the DOM tree. There&rsquo;s the possibility that a container query that relies on a custom property can change that same custom property. And it can quickly get complicated:</p>
<pre><code class="language-css">:root {
  --breakpoint-lg: 1600px;
}

/* DOES NOT WORK */
@container cards (min-width: var(--breakpoint-lg)) {
  .card {
    --breakpoint-lg: 1000px;
  }
}
</code></pre>
<p>I don&rsquo;t think any project should wholesale use <code>@container</code> instead of <code>@media</code>. Media queries still play an important role in responsive layouts. It&rsquo;s about understanding the separation of concerns.</p>
<p>I tend to reach for container queries when a component is used in more than one layout context. For example, a <code>.card</code> element could live in a full-width grid or a narrower sidebar. If that&rsquo;s the case, then we&rsquo;ll want the component&rsquo;s content to determine when it adjusts rather than a media query that looks at the outer viewport.</p>
<p>Similarly, I reach for media queries when a component solely exists at the page level. This would be something like a main navigation that always sits at the top of the page. It is directly influenced by the viewport&rsquo;s size, meaning that the viewport is a reliable reference for when the navigation needs to adjust. Again, it&rsquo;s all about &ldquo;macro&rdquo; layout versus &ldquo;micro&rdquo; layouts.</p>
<p>Here&rsquo;s a diagram for how I reason about which type of query to use:</p>
<figure class="break-out article__image"><img decoding="async" loading="lazy" width="800" height="632" src="https://thenokiablog.com/wp-content/uploads/2026/09/stop-treating-css-container-queries-like-traditional-media-queries-1.jpg" alt="Flowchart for choosing between media and container queries"><figcaption class="op-vertical-bottom">(Large preview)</figcaption></figure>
<h2 id="conclusion">Conclusion</h2>
<p>At the end of the day, the core reason why container queries look incredibly similar to media queries is simply familiarity. They&rsquo;re not exactly &ldquo;new&rdquo;, but they are way less understood and adopted than media queries. But media queries have plenty of their own limitations; otherwise, we wouldn&rsquo;t need container queries to fill those gaps.</p>
<p>What we have is a more effective feature for detecting when a specific component&rsquo;s context changes and a means for adjusting styles based on its content, as it should be when that component can exist in multiple contexts.</p>
<div class="signature"><img decoding="async" src="https://thenokiablog.com/wp-content/uploads/2026/09/stop-treating-css-container-queries-like-traditional-media-queries-1.png" alt="Smashing Editorial" width="35" height="46" loading="lazy"><br>
<span>(gg, yk)</span></div>
<p>The post <a href="https://thenokiablog.com/stop-treating-css-container-queries-like-traditional-media-queries/">Stop Treating CSS Container Queries Like Traditional Media Queries</a> appeared first on <a href="https://thenokiablog.com">The NOKIA Blog</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>6 Best WordPress Plugins for Improving Website Accessibility in 2026</title>
		<link>https://thenokiablog.com/6-best-wordpress-plugins-for-improving-website-accessibility-in-2026/</link>
		
		<dc:creator><![CDATA[Mister Nokia]]></dc:creator>
		<pubDate>Tue, 25 Aug 2026 17:09:45 +0000</pubDate>
				<category><![CDATA[Design]]></category>
		<category><![CDATA[Color]]></category>
		<category><![CDATA[CSS]]></category>
		<category><![CDATA[Fonts]]></category>
		<category><![CDATA[HTML5]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[Web Accessibility]]></category>
		<category><![CDATA[WordPress]]></category>
		<category><![CDATA[WordPress Plugins]]></category>
		<guid isPermaLink="false">https://thenokiablog.com/6-best-wordpress-plugins-for-improving-website-accessibility-in-2026/</guid>

					<description><![CDATA[<p>Accessibility should be a top priority when building a website. However, things can degrade over time....</p>
<p>The post <a href="https://thenokiablog.com/6-best-wordpress-plugins-for-improving-website-accessibility-in-2026/">6 Best WordPress Plugins for Improving Website Accessibility in 2026</a> appeared first on <a href="https://thenokiablog.com">The NOKIA Blog</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>Accessibility should be a top priority when building a website. However, things can degrade over time. Clients adding content could forget basics such as <code>ALT</code> attributes and logical heading order. Best practices often fall through the cracks as a site ages. Thus, every website requires vigilance.</p>
<p>Part of the challenge is that it&rsquo;s not always easy to identify accessibility issues. It often requires digging deeper to find and fix them. This is where the right WordPress plugin can be useful.</p>
<p>Some plugins allow users to adjust visual elements like font size and color contrast. Even better, some scan your content and alert you to problems. You might be surprised to find out how many issues can pop up in your theme and other installed plugins. The results may lead you to consider different options.</p>
<p>With that in mind, let&rsquo;s look at some of the best WordPress accessibility plugins on the market. They won&rsquo;t fix every issue, but they can help you learn and improve your site.</p>
<div class="internal-linking-related-contents"><a href="https://thenokiablog.com/rose-gold-design/" class="template-2"><span class="cta">Read more</span><span class="postTitle">Rose gold design</span></a></div><p>Accessibility Checker isn&rsquo;t a band-aid for your site&rsquo;s shortcomings. However, it will add missing skip links, focus outlines, and form labels upon request. It also scans your site&rsquo;s content and creates a laundry list of potential issues. It catches common problems, such as ambiguous link text and missing <code>ALT</code> attributes.</p>
<p>Like all automated tools, you may find some false-positive results. As such, a manual inspection is required to verify them. But the plugin offers tips for fixing any issues it finds. The free version scans your site&rsquo;s pages and posts, while the pro version includes custom post types and other handy features.</p>
<p><img fetchpriority="high" decoding="async" src="https://thenokiablog.com/wp-content/uploads/2026/08/6-best-wordpress-plugins-for-improving-website-accessibility-in-2026.jpg" alt="Accessibility Checker WordPress Plugin" width="900" height="400"></p>
<p>Use WP Accessibility to fix common issues, such as missing language/text direction attributes, missing <code>ARIA</code> labels for navigation, and viewports that don&rsquo;t scale. It will also enforce ALT attributes in the editor and identify media with missing descriptive text.</p>
<div class="internal-linking-related-contents"><a href="https://thenokiablog.com/passion-work/" class="template-2"><span class="cta">Read more</span><span class="postTitle">Passion &amp; work</span></a></div><p>These are optional and can be activated as needed. The other big feature here is a front-end toolbar that controls page zooming and color contrast. This gives users more control over how they view your site.</p>
<p><img decoding="async" src="https://thenokiablog.com/wp-content/uploads/2026/08/6-best-wordpress-plugins-for-improving-website-accessibility-in-2026-1.jpg" alt="WP Accessibility WordPress Plugin" width="900" height="400"></p>
<p>We&rsquo;re starting to see AI used for a variety of purposes, including accessibility improvements. Alt Text AI automatically analyzes uploaded images and generates an <code>ALT</code> description.</p>
<p>The feature can save you a lot of time and effort &ndash; just be sure to double-check to ensure the description is accurate. Note that the plugin requires purchasing AI &ldquo;credits&rdquo; to work. There&rsquo;s a free trial for trying the plugin before you buy.</p>
<div class="internal-linking-related-contents"><a href="https://thenokiablog.com/the-joy-of-art/" class="template-2"><span class="cta">Read more</span><span class="postTitle">The joy of art</span></a></div><p><img decoding="async" src="https://thenokiablog.com/wp-content/uploads/2026/08/6-best-wordpress-plugins-for-improving-website-accessibility-in-2026-2.jpg" alt="Alt Text AI WordPress Plugin" width="900" height="400"></p>
<p>Are you looking to provide visitors with accessibility options? This plugin adds a widget with a variety of optional feature modules. Users can change the font size and contrast. But there are also tools for switching to a dyslexia-friendly font, highlighting titles, and pausing animations.</p>
<p>You&rsquo;ll also find a feature that helps you create an accessibility statement. Experiment to see which features will benefit your visitors.</p>
<p><img decoding="async" loading="lazy" src="https://thenokiablog.com/wp-content/uploads/2026/08/6-best-wordpress-plugins-for-improving-website-accessibility-in-2026-3.jpg" alt="AccessiYes Accessibility Widget WordPress Plugin" width="900" height="400"></p>
<p>Accessibility is about more than fixing issues &ndash; it&rsquo;s also about adding accessible features. SimpleTOC creates a WCAG 2.2-conforming table of contents for your content. It&rsquo;s a great way to help users navigate longer pages and blog posts. By default, the plugin adds zero CSS or JavaScript to your site.</p>
<p>Instead, it relies on HTML details and summary tags, which work natively in your browser. When it comes to accessibility, simple is always better.</p>
<p><img decoding="async" loading="lazy" src="https://thenokiablog.com/wp-content/uploads/2026/08/6-best-wordpress-plugins-for-improving-website-accessibility-in-2026-4.jpg" alt="SimpleTOC WordPress Plugin" width="900" height="400"></p>
<p>Media embeds, particularly from third-party services, can be difficult to use. Some are bloated with JavaScript and other code that inhibit accessibility. Able Player offers an accessibility-first solution and implements an HTML5 media player via a configurable shortcode.</p>
<p>It works with locally uploaded files and videos from YouTube and Vimeo. The shortcode offers several options impacting the style and functionality of the player.</p>
<h2><span id="Leave_No_User_Behind">Leave No User Behind</span></h2>
<p>Accessibility is not a &ldquo;nice-to-have&rdquo; feature &ndash; it&rsquo;s an obligation. It&rsquo;s also an ongoing process that requires your attention. Thankfully, there are tools to help you find potential issues and fix the most common ones. The WordPress plugins above will help you in your quest. Bonus: Some will add accessible elements that benefit everyone.</p>
<p>No singular plugin can make your site fully accessible. But they can make your job easier. We hope you find a solution to do just that.</p>
<h2><span id="Website_Accessibility_Plugins_at_a_Glance"> Website Accessibility Plugins at a Glance</span></h2>
<div>
<table class="table-wrapped">
<thead>
<tr>
<th><strong>Plugin</strong></th>
<th><strong>Description</strong></th>
<th><strong>Primary Use</strong></th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Accessibility Checker</strong></td>
<td>Scans content for accessibility issues and can add missing skip links, focus outlines, and form labels, with guidance for fixing problems.</td>
<td>Accessibility auditing</td>
</tr>
<tr>
<td><strong>WP Accessibility</strong></td>
<td>Fixes common accessibility issues and adds front-end controls for page zooming and color contrast.</td>
<td>Accessibility improvements</td>
</tr>
<tr>
<td><strong>Alt Text AI</strong></td>
<td>Automatically analyzes uploaded images and generates ALT descriptions, with the option to review the generated text for accuracy.</td>
<td>Image ALT text</td>
</tr>
<tr>
<td><strong>AccessiYes Accessibility Widget</strong></td>
<td>Adds visitor controls for font size, contrast, dyslexia-friendly fonts, highlighted titles, paused animations, and other accessibility options.</td>
<td>Visitor accessibility controls</td>
</tr>
<tr>
<td><strong>SimpleTOC</strong></td>
<td>Creates a WCAG 2.2-conforming table of contents using native HTML without adding CSS or JavaScript by default.</td>
<td>Accessible content navigation</td>
</tr>
<tr>
<td><strong>Able Player</strong></td>
<td>Adds an accessibility-focused HTML5 media player for local files, YouTube, and Vimeo using a configurable shortcode.</td>
<td>Accessible media playback</td>
</tr>
</tbody>
</table>
<hr>
<div class="explore-more">
<h2><span id="More_Essential_Free_WordPress_Plugins">More Essential Free WordPress Plugins</span></h2>
<p>here. He recently started a writing service for WordPress products: WP Product Writeup.  He also has an opinion on just about every subject. You can follow his rants on Bluesky @karks.com.</p>
<p>
                        Read more articles by Eric Karkovack
                    </p>

<hr>
</div></div><p>The post <a href="https://thenokiablog.com/6-best-wordpress-plugins-for-improving-website-accessibility-in-2026/">6 Best WordPress Plugins for Improving Website Accessibility in 2026</a> appeared first on <a href="https://thenokiablog.com">The NOKIA Blog</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>How Baseline Can Help You Ship Less JavaScript</title>
		<link>https://thenokiablog.com/how-baseline-can-help-you-ship-less-javascript/</link>
		
		<dc:creator><![CDATA[Mister Nokia]]></dc:creator>
		<pubDate>Fri, 07 Aug 2026 13:00:00 +0000</pubDate>
				<category><![CDATA[Design]]></category>
		<category><![CDATA[Analytics]]></category>
		<category><![CDATA[CSS]]></category>
		<category><![CDATA[Dashboard]]></category>
		<category><![CDATA[data]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[News]]></category>
		<category><![CDATA[Scrolling]]></category>
		<category><![CDATA[Security]]></category>
		<guid isPermaLink="false">https://thenokiablog.com/how-baseline-can-help-you-ship-less-javascript/</guid>

					<description><![CDATA[<p>The gap between &#8220;you need a library for this&#8221; and &#8220;the browser does this&#8221; keeps closing....</p>
<p>The post <a href="https://thenokiablog.com/how-baseline-can-help-you-ship-less-javascript/">How Baseline Can Help You Ship Less JavaScript</a> appeared first on <a href="https://thenokiablog.com">The NOKIA Blog</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p><section aria-label="Quick summary" class="article__summary"><span id="article__start" class="summary__heading" aria-hidden="true"></span>The gap between &ldquo;you need a library for this&rdquo; and &ldquo;the browser does this&rdquo; keeps closing. A practical guide to auditing your dependencies and finding what the web platform can now handle for you.</section>
</p>
<p>Most of us install a dependency once and never look at it again. It does its job, the tests pass, and we move on. But the web platform keeps moving too, and a surprising number of the libraries sitting in your <code>package.json</code> today are now built into the browser.</p>
<p>In a typical mid-sized JavaScript app, you can often find somewhere between 60KB and 90KB (minified and gzipped) of dependencies that the platform can now handle on its own. Date and number formatting, HTTP requests, modals, tooltips, deep cloning, grouping arrays: these were all real gaps a few years ago. A lot of them aren&rsquo;t gaps anymore.</p>
<p>The reason those libraries stick around isn&rsquo;t laziness. It&rsquo;s that most teams don&rsquo;t re-audit their dependencies on a Baseline cadence, or are simply not aware of how fast browsers are shipping these days. You check <code>npm audit</code> for security, but <em>is this library still doing something the browser can&rsquo;t?</em> is a question that rarely gets asked. So the libraries stay.</p>
<div class="internal-linking-related-contents"><a href="https://thenokiablog.com/rose-gold-design/" class="template-2"><span class="cta">Read more</span><span class="postTitle">Rose gold design</span></a></div><p>In this article, we&rsquo;ll run that audit together. Instead of going through dependencies one by one, we&rsquo;ll work in clusters, because the wins tend to come in groups. We&rsquo;ll do the bundle math, build a small decision framework you can reuse, and stay honest about the cases where the platform still falls short. By the end, you&rsquo;ll have a repeatable process you can run on your own <code>package.json</code>.</p>
<figure class="break-out article__image"><img decoding="async" loading="lazy" width="800" height="340" src="https://thenokiablog.com/wp-content/uploads/2026/08/how-baseline-can-help-you-ship-less-javascript.png" alt="Git diff showing removed npm dependencies highlighted in red from a package.json file, illustrating how Baseline helps reduce JavaScript bundle size."><figcaption class="op-vertical-bottom">(Large preview)</figcaption></figure>
<h2 id="what-baseline-actually-means">What &ldquo;Baseline&rdquo; Actually Means</h2>
<p>Before we start deleting things, let&rsquo;s quickly recap what Baseline is. Feel free to skip this section if you&rsquo;re already familiar.</p>
<p>Baseline is a project from the WebDX Community Group that tells you, in plain terms, how safe a web feature is to use across the major browsers (Chrome, Edge, Firefox, and Safari). A feature can be in one of three states:</p>
<ul>
<li><strong>Limited availability</strong><br>The feature hasn&rsquo;t shipped in all the major engines yet. Not safe to rely on without a fallback.</li>
<li><strong>Baseline Newly available</strong><br>The feature has just landed in all the major engines. It works for users on up-to-date browsers, but older devices in the wild may not have it yet.</li>
<li><strong>Baseline Widely available</strong><br>The feature has been in all the major engines for 30 months. At this point, you can reach for it without much thought.</li>
</ul>
<p>That 30-month gap between &ldquo;Newly&rdquo; and &ldquo;Widely&rdquo; matters a lot for this audit. A feature that&rsquo;s Widely available is something you can usually drop a library for today. A feature that&rsquo;s only Newly available is something you can drop a library for <em>if</em> you check your audience first, or if you&rsquo;re comfortable with a small feature check. We&rsquo;ll treat those two cases differently throughout.</p>
<div class="internal-linking-related-contents"><a href="https://thenokiablog.com/passion-work/" class="template-2"><span class="cta">Read more</span><span class="postTitle">Passion &amp; work</span></a></div><p>You can look any feature up on webstatus.dev, on MDN (every reference page shows a Baseline badge near the top), or programmatically with the <code>web-features</code> npm package. We&rsquo;ll use all three later when we run the audit on a real project.</p>
<div data-audience="non-subscriber" data-remove="true" class="feature-panel-container"><img decoding="async" loading="lazy" class="feature-panel-image-img" src="https://thenokiablog.com/wp-content/uploads/2026/08/how-baseline-can-help-you-ship-less-javascript-1.png" alt="Feature Panel" width="481" height="698"></div>


<h2 id="a-decision-framework-before-you-delete-anything">A Decision Framework Before You Delete Anything</h2>
<p>It&rsquo;s tempting to read <em>&ldquo;the browser does this now&rdquo;</em> and start ripping libraries out. Let&rsquo;s not do that. A swap that looks free on paper can quietly break things for a chunk of your users, or cost you a feature you were relying on without realizing it.</p>
<p>So before dropping any library, ask three questions. We&rsquo;ll reuse these in every cluster below.</p>
<p><strong>1. Is the replacement Baseline-safe for</strong> <strong><em>my</em></strong> <strong>audience?</strong></p>
<div class="internal-linking-related-contents"><a href="https://thenokiablog.com/the-joy-of-art/" class="template-2"><span class="cta">Read more</span><span class="postTitle">The joy of art</span></a></div><p>Not <em>&ldquo;is it Baseline&rdquo;</em> in the abstract, but <em>&ldquo;is it safe for the people who actually use my app.&rdquo;</em> If the native feature is Widely available, this is usually a yes. If it&rsquo;s only Newly available, check your analytics or your <code>browserslist</code> config and see what share of your users would miss out. A B2B dashboard where everyone&rsquo;s on the latest browser is a very different situation from a public-facing site with a long tail of old Android devices.</p>
<p><strong>2. What does the swap actually cost?</strong></p>
<p>Dropping a library isn&rsquo;t always free. Sometimes the native feature isn&rsquo;t supported widely enough yet, so you&rsquo;d reach for a polyfill. If that polyfill is heavier than the library you&rsquo;re removing, you&rsquo;ve made your bundle <em>bigger</em>, unless you load it conditionally. We&rsquo;ll see exactly this with Temporal later.</p>
<p><strong>3. Does the platform feature cover my real use case?</strong></p>
<p>Libraries often do more than the platform feature they resemble. <code>axios</code> isn&rsquo;t just <code>fetch</code> with automatic JSON parsing; it has interceptors, request cancellation, and retries. If you&rsquo;re using those, a straight swap to <code>fetch</code> will leave you reimplementing them. Check what you actually use before assuming it&rsquo;s a drop-in replacement.</p>
<p>Keep these three in mind. Every cluster below is really just these questions applied to a different corner of your dependencies.</p>
<h2 id="cluster-1-internationalization-the-biggest-drop-today-win">Cluster 1: Internationalization (The Biggest Drop Today Win)</h2>
<p>This is the cluster where you&rsquo;ll usually find the most KBs sitting on top of features that are already Widely available. The browser ships a whole family of formatting tools under the <code>Intl</code> namespace, and a lot of small, popular libraries became unnecessary.</p>
<p>Here are the usual suspects and what replaces them:</p>
<ul>
<li><code>timeago.js</code> (1 KB gz) &rarr; <code>Intl.RelativeTimeFormat</code></li>
<li><code>pluralize</code> (2.3 KB gz) &rarr; <code>Intl.PluralRules</code></li>
<li><code>numeral</code> (3.9 KB gz) &rarr; <code>Intl.NumberFormat</code></li>
<li><code>humanize-duration</code> (6.6 KB gz) &rarr; <code>Intl.DurationFormat</code></li>
<li>list-joining helpers &rarr; <code>Intl.ListFormat</code></li>
</ul>
<p>Let&rsquo;s walk through some of them.</p>
<h3 id="relative-time">Relative Time</h3>
<p><code>timeago.js</code> exists to turn a timestamp into <em>&ldquo;3 hours ago&rdquo;</em>. <code>Intl.RelativeTimeFormat</code> does the same thing, and it&rsquo;s Baseline Widely available.</p>
<pre><code class="language-javascript">const rtf = new Intl.RelativeTimeFormat("en", { numeric: "auto" });

rtf.format(-1, "day"); // "yesterday"
rtf.format(3, "hour"); // "in 3 hours"
rtf.format(-2, "week"); // "2 weeks ago"
</code></pre>
<p>The <code>numeric: "auto"</code> option is the nice touch here: it gives you &ldquo;yesterday&rdquo; instead of &ldquo;1 day ago&rdquo; where the language has a word for it. You pass a number and a unit, and you get a localized string back.</p>
<p>You may be wondering about the one thing <code>timeago.js</code> does that this snippet doesn&rsquo;t: it picks the unit for you. Given a date, <code>timeago.js</code> decides whether to say &ldquo;seconds&rdquo; or &ldquo;days.&rdquo; <code>Intl.RelativeTimeFormat</code> expects you to do that part. It&rsquo;s a few lines of arithmetic (work out the difference, find the largest unit that fits), and once you&rsquo;ve written that helper, you don&rsquo;t need the library anymore.</p>
<h3 id="numbers-currency-and-lists">Numbers, Currency, And Lists</h3>
<p><code>Intl.NumberFormat</code> covers most of what number-formatting libraries do: thousands separators, currency, percentages, and compact notation.</p>
<div class="break-out">
<pre><code class="language-javascript">new Intl.NumberFormat("en-US").format(1234567.89);
// "1,234,567.89"

new Intl.NumberFormat("en-US", { style: "currency", currency: "USD" }).format(
  1234.5,
);
// "$1,234.50"

new Intl.NumberFormat("en", { notation: "compact" }).format(1200000);
// "1.2M"
</code></pre>
</div>
<p>And <code>Intl.ListFormat</code>, Widely available, handles the &ldquo;join an array into a sentence&rdquo; problem, including the Oxford comma, which is the kind of thing people write fiddly helper functions for:</p>
<div class="break-out">
<pre><code class="language-javascript">const lf = new Intl.ListFormat("en", { style: "long", type: "conjunction" });

lf.format(["Alice", "Bob", "Carol"]);
// "Alice, Bob, and Carol"
</code></pre>
</div>
<h3 id="the-one-caveat-durations">The One Caveat: Durations</h3>
<p><code>humanize-duration</code> turns a number of milliseconds into <em>&ldquo;1 hour, 30 minutes&rdquo;</em>. The platform equivalent is <code>Intl.DurationFormat</code>:</p>
<pre><code class="language-javascript">const df = new Intl.DurationFormat("en", { style: "long" });

df.format({ hours: 1, minutes: 30 });
// "1 hour, 30 minutes"
</code></pre>
<p>One thing to keep in mind is that <code>Intl.DurationFormat</code> is Baseline <em>Newly</em> available at the time of writing, not Widely available. It landed in all the major engines in March 2025, and it&rsquo;s on track to become Widely available in 2027. So this one fails question 1 for broad-audience apps unless you check your traffic first or add a fallback. For an internal tool on modern browsers, it&rsquo;s fine today. For a public site with old devices, give it another year or guard it with a feature check.</p>
<h3 id="the-math-on-this-cluster">The Math On This Cluster</h3>
<p>If your app uses the full set (<code>humanize-duration</code>, <code>timeago.js</code>, <code>pluralize</code>, <code>numeral</code>), that&rsquo;s roughly <strong>14 KB gzipped</strong> of dependencies, most of it replaceable right now with Widely available APIs. The internationalization cluster is usually the easiest win in the whole audit.</p>
<h2 id="cluster-2-http-clients">Cluster 2: HTTP Clients</h2>
<p>This cluster is more nuanced, so it&rsquo;s a good one to slow down on.</p>
<p>The browser HTTP libraries people reach for are <code>axios</code> (17 KB gz) and <code>superagent</code> (19 KB gz). For most requests, <code>fetch</code> plus <code>AbortController</code> covers what you need, and both are Widely available.</p>
<p>A basic GET looks like this:</p>
<pre><code class="language-javascript">// axios
const { data } = await axios.get("/api/users");

// fetch
const res = await fetch("/api/users");
const data = await res.json();
</code></pre>
<p>The one extra line (<code>res.json()</code>) is <code>fetch</code> being explicit where <code>axios</code> was implicit. That&rsquo;s the pattern across this whole cluster: <code>fetch</code> does less for you by default, and you decide whether you want the things it leaves out.</p>
<h3 id="timeouts">Timeouts</h3>
<p><code>axios</code> has a <code>timeout</code> option. <code>fetch</code> has <code>AbortSignal.timeout()</code>:</p>
<pre><code class="language-javascript">const res = await fetch("/api/users", {
  signal: AbortSignal.timeout(5000), // abort after 5 seconds
});
</code></pre>
<h3 id="where-fetch-doesn-t-replace-axios">Where <code>fetch</code> Doesn&rsquo;t Replace <code>axios</code></h3>
<p>This is where question 3 does most of the work, so let&rsquo;s be specific about the gaps:</p>
<ul>
<li><strong><code>fetch</code> doesn&rsquo;t reject on HTTP errors.</strong><br>A <code>404</code> or <code>500</code> is a resolved promise, not a rejection. You have to check <code>res.ok</code> yourself. <code>axios</code> rejects on any non-2xx status.</li>
<li><strong>No interceptors.</strong><br>If you rely on <code>axios</code> interceptors to attach auth tokens or handle 401s in one place, <code>fetch</code> has no equivalent. You&rsquo;d wrap <code>fetch</code> in your own function or class to get the same behavior.</li>
<li><strong>No automatic retries.</strong><br><code>axios</code> (with a plugin) can retry failed requests. With <code>fetch</code>, that&rsquo;s your code to write.</li>
<li><strong>No upload progress.</strong><br><code>fetch</code> still can&rsquo;t report upload progress in a first-class way. If you have a file uploader with a progress bar, that&rsquo;s a real reason to keep a library.</li>
</ul>
<p>I personally heavily rely on <em>interceptors</em> in my interactive online courses, such as Learn JavaScript, and I have solved that for years using a custom <code>class</code> on top of <code>fetch</code>. I&rsquo;ve shipped this to millions of users and have seen lots of success with it.</p>
<p>None of these are hard to rebuild, and most apps only use one or two of them. But this is exactly the kind of cluster where you shouldn&rsquo;t do a blind find-and-replace. Look at how you actually use your HTTP client first. If it&rsquo;s plain GETs and POSTs, dropping <code>axios</code> for a thin <code>fetch</code> wrapper saves you about <strong>17 KB gzipped</strong>.</p>
<h2 id="cluster-3-ui-primitives">Cluster 3: UI Primitives</h2>
<p>This cluster has some of the most satisfying swaps, because the platform features don&rsquo;t just match the libraries, they&rsquo;re often more accessible than what teams ship by hand.</p>
<p>The libraries here are modal dialogs (something like <code>a11y-dialog</code>, 1.8 KB gz), tooltip and popover libraries (<code>tippy.js</code>, 14 KB gz, which bundles Popper for positioning), <code>focus-trap</code> (6.6 KB gz), and <code>body-scroll-lock</code> (1.3 KB gz). They get replaced by three platform features: the <code>&lt;dialog&gt;</code> element, the Popover API, and CSS anchor positioning.</p>
<h3 id="the-dialog-element">The <code>&lt;dialog&gt;</code> Element</h3>
<p>A huge amount of modal-related code exists to solve accessibility problems: trapping focus inside the modal, closing on <code>Escape</code>, restoring focus to the previous element when the dialog is closed, and rendering above everything else. The <code>&lt;dialog&gt;</code> element, Widely available, does all of that for you.</p>
<div class="break-out">
<pre><code class="language-javascript">&lt;dialog id="confirm"&gt;
  &lt;form method="dialog"&gt;
    &lt;p&gt;Delete this file?&lt;/p&gt;
    &lt;button value="cancel"&gt;Cancel&lt;/button&gt;
    &lt;button value="delete"&gt;Delete&lt;/button&gt;
  &lt;/form&gt;
&lt;/dialog&gt;

const dialog = document.querySelector("#confirm");

dialog.showModal(); // focus moves in, background goes inert, Escape closes it

dialog.addEventListener("close", () =&gt; {
  console.log(dialog.returnValue); // "cancel" or "delete"
});
</code></pre>
</div>
<p>Calling <code>showModal()</code> does the work that <code>focus-trap</code> was installed for: focus moves into the dialog, the rest of the page becomes inert so you can&rsquo;t tab out of it, <code>Escape</code> closes it, and focus returns to the element that opened it. The dialog renders in the browser&rsquo;s <em>Top layer</em>, so you don&rsquo;t fight <code>z-index</code>. You also get a <code>::backdrop</code> pseudo-element to style the overlay.</p>
<p>That single element can replace your modal library <em>and</em> <code>focus-trap</code>. The one piece it doesn&rsquo;t handle on its own is locking the background from scrolling, which is what <code>body-scroll-lock</code> was for. That&rsquo;s now one line of CSS:</p>
<pre><code class="language-css">body:has(dialog:modal) {
  overflow: hidden;
}
</code></pre>
<p>If you&rsquo;re wondering why we&rsquo;re using <code>dialog:modal</code> instead of <code>dialog[open]</code>, it&rsquo;s because the <code>open</code> attribute is set as soon as you call <code>show()</code>, but the dialog isn&rsquo;t actually <em>modal</em> so you don&rsquo;t want to lock scrolling yet. The <code>:modal</code> pseudo-class is only true when the dialog is actually <em>modal</em>, which is the case when you call <code>showModal()</code>.</p>
<p>So three libraries collapse into one element and one CSS rule.</p>
<h3 id="popover-api-and-anchor-positioning">Popover API And Anchor Positioning</h3>
<p>For things that aren&rsquo;t full modals (dropdown menus, tooltips, the small floating panels that <code>tippy.js</code> handles), the Popover API gives you light-dismiss behavior, top-layer rendering, and <code>Escape</code>-to-close with no JavaScript at all:</p>
<pre><code class="language-html">&lt;button popovertarget="menu" id="options"&gt;Options&lt;/button&gt;

&lt;div id="menu" popover&gt;
  &lt;!-- menu content --&gt;
&lt;/div&gt;
</code></pre>
<p>Clicking the button toggles the popover. Clicking outside it closes it. It&rsquo;s Baseline Newly available (since January 2025).</p>
<p>The other half of what a tooltip library does is <em>positioning</em>: keeping the floating element pinned to its trigger and flipping it when it would overflow the viewport. That&rsquo;s what Popper (bundled inside <code>tippy.js</code>) handles, and it&rsquo;s now a CSS feature called anchor positioning. Here it pins the same <code>#menu</code> popover directly under its trigger button:</p>
<pre><code class="language-css">#options {
  anchor-name: --trigger;
}

.tooltip {
  position-anchor: --trigger;
  position-area: top;
  margin: 0;
}
</code></pre>
<p>Anchor positioning is the newest feature in this article. It became Baseline Newly available in January 2026, when Firefox 147 shipped it (Chrome had it since version 125, and Safari since version 26). Because it&rsquo;s this fresh, it&rsquo;s squarely a question-1 feature: great for modern audiences, but check your traffic, and note that some of the more advanced parts (like position-try fallbacks) have uneven support across versions. Keep a sensible fallback for older browsers.</p>
<p>Between <code>&lt;dialog&gt;</code>, the Popover API, and anchor positioning, the UI primitives cluster (tooltip library, modal library, <code>focus-trap</code>, <code>body-scroll-lock</code>) adds up to roughly <strong>24 KB gzipped</strong>, and you come out the other side with better accessibility defaults than most hand-rolled solutions.</p>
<h2 id="cluster-4-lodash-utilities">Cluster 4: Lodash Utilities</h2>
<p>Lodash is rarely imported whole anymore, but its individual functions show up everywhere, either as the full <code>lodash</code> package (25 KB gz) or as standalone installs like <code>lodash.clonedeep</code> and <code>lodash.groupby</code>. Several of the most common ones now have direct platform equivalents.</p>
<h3 id="grouping">Grouping</h3>
<p><code>lodash.groupby</code> reorganizes an array into an object keyed by some property. <code>Object.groupBy</code> does exactly that:</p>
<pre><code class="language-javascript">const products = [
  { name: "Apple", category: "fruit" },
  { name: "Carrot", category: "vegetable" },
  { name: "Banana", category: "fruit" },
];

const grouped = Object.groupBy(products, (product) =&gt; product.category);
// {
//   fruit: [{ name: "Apple", ... }, { name: "Banana", ... }],
//   vegetable: [{ name: "Carrot", ... }],
// }
</code></pre>
<p>There&rsquo;s also <code>Map.groupBy</code> for when you want a <code>Map</code> instead of a plain object (handy if your keys aren&rsquo;t strings). Both are Baseline Newly available, since March 2024, and on track to become Widely available in late 2026.</p>
<h3 id="deep-cloning">Deep Cloning</h3>
<p><code>lodash.clonedeep</code> makes a deep copy of an object. <code>structuredClone</code> is the platform version, and it&rsquo;s Widely available:</p>
<pre><code class="language-javascript">const original = { user: { name: "Sam", roles: ["admin"] } };

const copy = structuredClone(original);
copy.user.roles.push("editor");

original.user.roles; // ["admin"] (unchanged)
</code></pre>
<p><code>structuredClone</code> handles the tricky cases that trip up <code>JSON.parse(JSON.stringify(...))</code>: it clones <code>Date</code>, <code>Map</code>, <code>Set</code>, <code>ArrayBuffer</code>, and circular references correctly. The limit to know about (question 3 again) is that it can&rsquo;t clone functions, DOM nodes, or class instances; it throws on functions and drops the prototype on class instances. For plain data, which is what most people deep-clone, it&rsquo;s a clean replacement.</p>
<h3 id="set-operations">Set Operations</h3>
<p>If you&rsquo;ve ever pulled in a Lodash helper for <code>union</code>, <code>intersection</code>, or <code>difference</code>, the <code>Set</code> object now has these built in. They&rsquo;re Baseline Newly available, since June 2024:</p>
<pre><code class="language-javascript">const admins = new Set(["sam", "alex", "jo"]);
const editors = new Set(["alex", "kim"]);

admins.intersection(editors); // Set { "alex" }
admins.union(editors); // Set { "sam", "alex", "jo", "kim" }
admins.difference(editors); // Set { "sam", "jo" }
</code></pre>
<p>The full set of methods is <code>union</code>, <code>intersection</code>, <code>difference</code>, <code>symmetricDifference</code>, <code>isSubsetOf</code>, <code>isSupersetOf</code>, and <code>isDisjointFrom</code>.</p>
<h3 id="what-s-worth-keeping">What&rsquo;s Worth Keeping</h3>
<p>Not all of Lodash has moved into the platform. <code>debounce</code> and <code>throttle</code> still have no native equivalent, and they&rsquo;re genuinely useful, so cherry-picking <code>lodash.debounce</code> is reasonable. The point of this cluster isn&rsquo;t &ldquo;delete Lodash,&rdquo; it&rsquo;s &ldquo;stop shipping the parts the browser already has.&rdquo; Dropping <code>lodash.clonedeep</code> and <code>lodash.groupby</code> alone is about <strong>8 KB gzipped</strong>, and if you were importing the full <code>lodash</code> for a handful of functions, replacing the platform-covered ones can let you drop it entirely.</p>
<h2 id="cluster-5-temporal-a-case-study-in-not-dropping-a-library-yet">Cluster 5: Temporal, A Case Study In Not Dropping A Library Yet</h2>
<p>Every cluster so far has ended in &ldquo;go ahead, drop it.&rdquo; This one is the opposite, and that&rsquo;s why it&rsquo;s worth including: it shows the framework telling you to wait.</p>
<p><code>Temporal</code> is the long-awaited replacement for JavaScript&rsquo;s <code>Date</code>, and it&rsquo;s a genuinely better API: immutable objects, sane time zone handling, and no more month indexes starting at zero. It reached TC39 Stage 4 in March 2026 and is part of the ES2026 specification. Firefox shipped it in version 139 (in 2025), and Chrome shipped it in version 144 (January 2026). Safari hasn&rsquo;t shipped it in a stable release yet; it&rsquo;s in Safari Technology Preview, with stable support expected later in 2026.</p>
<p>If Temporal is news to you, check out the Temporal Cheatsheet for a quick overview of the API and a comparison to <code>Date</code>.</p>
<p>However, <code>Temporal</code> is <em>not</em> Baseline. It&rsquo;s still in <em>limited availability</em>, because Safari users don&rsquo;t have it. To use it across all browsers today, you need a polyfill, and this is where the math turns against you.</p>
<p>The official <code>@js-temporal/polyfill</code> is about <strong>44 KB gzipped</strong>. There&rsquo;s a smaller polyfill that internally does not depend on BigInt and it weighs <strong>19 KB gzipped</strong>. A lightweight date library like <code>dayjs</code> is about <strong>3 KB gzipped</strong>. So if you swap <code>dayjs</code> for <code>Temporal</code> plus its polyfill right now, you&rsquo;re not saving 3 KB, you&rsquo;re <em>adding</em> roughly 41 KB to your bundle, unless you are able to load the polyfill conditionally.</p>
<p>Run it through the framework:</p>
<ul>
<li><strong>Question 1 (audience):</strong> Temporal isn&rsquo;t Baseline. For a broad audience, that&rsquo;s a lot of people.</li>
<li><strong>Question 2 (cost):</strong> the polyfill is more than ten times the size of the library you&rsquo;d remove. The swap makes your bundle bigger.</li>
<li><strong>Question 3 (feature gap):</strong> Temporal actually wins here; it does more than <code>dayjs</code>. But that doesn&rsquo;t matter while questions 1 and 2 are failing.</li>
</ul>
<p>The verdict is generally clear: keep <code>dayjs</code> (or <code>date-fns</code>) for now. The moment to revisit is when Safari ships Temporal in a stable release and it reaches Baseline. At that point you can use Temporal natively and conditionally load the polyfill for users on older browsers. This is a feature to write down and check again in a few months, not one to act on today.</p>
<h2 id="how-to-run-this-audit-on-your-own-package-json">How To Run This Audit On Your Own <code>package.json</code></h2>
<p>The clusters above are a starting map, but your dependencies are your own. Here&rsquo;s a repeatable process you can run this quarter.</p>
<h3 id="step-1-list-your-production-dependencies">Step 1: List Your Production Dependencies</h3>
<p>Start by listing what actually ships to users:</p>
<pre><code class="language-bash">npm ls --omit=dev --depth=0
</code></pre>
<h3 id="step-2-measure-what-each-one-costs">Step 2: Measure What Each One Costs</h3>
<p>For a quick per-package number, Bundlephobia gives you the minified and gzipped size of any npm package. For the real picture (what each dependency costs <em>in your actual bundle</em>, after tree-shaking and deduplication), run a bundle analyzer against your build. <code>npx source-map-explorer</code> works on most bundles, and <code>npx vite-bundle-visualizer</code> works for Vite projects.</p>
<figure class="break-out article__image"><img decoding="async" loading="lazy" width="800" height="447" src="https://thenokiablog.com/wp-content/uploads/2026/08/how-baseline-can-help-you-ship-less-javascript-2.png" alt="Bundlephobia website showing that axios weighs 44kb minified and 16.6kb minified &amp; gzipped. Such package takes 331ms to download on slow 3G and 19ms on emerging 4G."><figcaption class="op-vertical-bottom">(Large preview)</figcaption></figure>
<h3 id="step-3-check-the-baseline-status-of-each-replacement">Step 3: Check The Baseline Status Of Each Replacement</h3>
<p>For each candidate, find the platform feature that would replace it and check its Baseline status. The quickest way is webstatus.dev or the Baseline badge on the feature&rsquo;s MDN page.</p>
<h3 id="step-4-run-the-three-questions">Step 4: Run The Three Questions</h3>
<p>For each library with a platform replacement, go back to the framework: Is it Baseline-safe for your audience? What does the swap cost? Does the feature cover how you actually use the library? Most of your decisions will fall out of question 1 (check the feature&rsquo;s status against your <code>browserslist</code>) and question 3 (check your own usage).</p>
<h3 id="step-5-swap-behind-progressive-enhancement-where-needed">Step 5: Swap Behind Progressive Enhancement Where Needed</h3>
<p>For Widely available features, swap and move on. For Newly available ones, either confirm your audience is on modern browsers or guard the new code with a quick feature check and keep a fallback:</p>
<pre><code class="language-javascript">if (typeof Intl.DurationFormat === "function") {
  // use the platform feature
} else {
  // fall back to the library, or a simpler format
}
</code></pre>
<p>That way you ship less code to the users who can run it, without breaking the ones who can&rsquo;t.</p>
<h2 id="wrapping-up">Wrapping Up</h2>
<p>Add the clusters up, and the picture is concrete. The internationalization cluster is around 14 KB gzipped, HTTP is around 17 KB, the UI primitives are around 24 KB, and the Lodash utilities are 8 KB or more depending on how much of the library you were shipping. For a typical mid-sized app, that&rsquo;s somewhere between 60 KB and 90 KB gzipped of dependencies you can hand back to the platform, and more if you were shipping the full <code>lodash</code> or several of these libraries at once. (The uncompressed numbers are two to three times larger, which is what you&rsquo;ll see in a bundle analyzer before gzip.)</p>
<p>I&rsquo;ve chosen relatively lean packages for most of these features, but some individual packages could still be heavy. Your dialog package, for instance, could alone weigh as much as 50KB gzipped depending on what you&rsquo;re using.</p>
<p>A few features are worth keeping an eye on over the next year, because they&rsquo;ll open up further swaps:</p>
<ul>
<li><strong>Temporal going native.</strong><br>Once Safari ships it in a stable release and it reaches Baseline, you can drop both your date library <em>and</em> the polyfill, turning today&rsquo;s regression into a real win.</li>
<li><strong>CSS anchor positioning maturing.</strong><br>It became Baseline Newly available in January 2026. As it ages toward Widely available, dropping tooltip and popover positioning libraries gets safer for broad audiences.</li>
<li><strong><code>Object.groupBy</code> and friends crossing into Widely available.</strong><br>The 2024 batch (array grouping, Set methods) is on track to become Widely available in late 2026, which moves them from &ldquo;check your audience&rdquo; to &ldquo;just use it.&rdquo;</li>
</ul>
<p>None of this is a one-time cleanup. The platform ships new features constantly, and the gap between <em>&ldquo;you need a library for this&rdquo;</em> and <em>&ldquo;the browser does this&rdquo;</em> keeps closing. The habit worth building is small: once a quarter, run the audit. List your dependencies, check what&rsquo;s now Baseline, and hand back what you can.</p>
<p>Pick one cluster from this article, open your <code>package.json</code>, and see how much of it the browser already does for you.</p>
<div class="signature"><img decoding="async" src="https://thenokiablog.com/wp-content/uploads/2026/08/how-baseline-can-help-you-ship-less-javascript-3.png" alt="Smashing Editorial" width="35" height="46" loading="lazy"><br>
<span>(yk)</span></div>
<p>The post <a href="https://thenokiablog.com/how-baseline-can-help-you-ship-less-javascript/">How Baseline Can Help You Ship Less JavaScript</a> appeared first on <a href="https://thenokiablog.com">The NOKIA Blog</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Runtime instances: persistent compute for production AI agents on Amazon Bedrock AgentCore</title>
		<link>https://thenokiablog.com/runtime-instances-persistent-compute-for-production-ai-agents-on-amazon-bedrock-agentcore/</link>
		
		<dc:creator><![CDATA[Mister Nokia]]></dc:creator>
		<pubDate>Thu, 06 Aug 2026 22:58:00 +0000</pubDate>
				<category><![CDATA[Nokia Mobile Phone]]></category>
		<category><![CDATA[Amazon Bedrock]]></category>
		<category><![CDATA[Amazon Bedrock AgentCore]]></category>
		<category><![CDATA[AWS CLI]]></category>
		<category><![CDATA[AWS Management Console]]></category>
		<category><![CDATA[Compute]]></category>
		<category><![CDATA[data]]></category>
		<category><![CDATA[Identity]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[JavaScript Snippets]]></category>
		<category><![CDATA[Launch]]></category>
		<category><![CDATA[News]]></category>
		<category><![CDATA[Regions]]></category>
		<category><![CDATA[Security]]></category>
		<category><![CDATA[Storage]]></category>
		<category><![CDATA[Strands Agents]]></category>
		<guid isPermaLink="false">https://thenokiablog.com/runtime-instances-persistent-compute-for-production-ai-agents-on-amazon-bedrock-agentcore/</guid>

					<description><![CDATA[<p>When you move AI agents from prototype to production, the infrastructure challenges multiply. Your agents need...</p>
<p>The post <a href="https://thenokiablog.com/runtime-instances-persistent-compute-for-production-ai-agents-on-amazon-bedrock-agentcore/">Runtime instances: persistent compute for production AI agents on Amazon Bedrock AgentCore</a> appeared first on <a href="https://thenokiablog.com">The NOKIA Blog</a>.</p>
]]></description>
										<content:encoded><![CDATA[<table id="amazon-polly-audio-table">
<tbody>
<tr>
<td id="amazon-polly-audio-tab">
<div id="amazon-polly-by-tab">
            <img loading="lazy" decoding="async" src="https://thenokiablog.com/wp-content/uploads/2026/08/runtime-instances-persistent-compute-for-production-ai-agents-on-amazon-bedrock-agentcore.png" alt="Voiced by Polly" width="554" height="56">
           </div>
</td>
</tr>
</tbody>
</table>
<p>When you move AI agents from prototype to production, the infrastructure challenges multiply. Your agents need to persist state across multi-step workflows that run for hours or days. They need to coordinate with other agents, share context, and sometimes access GPUs for specialized tasks. Amazon Bedrock AgentCore runtime microVMs provide a fully managed environment for invocations that can run for up to 8 hours and support stateful workflows through managed session storage. Some workloads also benefit from dedicated, larger-capacity environments &mdash; for example, when agents need to run continuously for multiple days, access GPUs or the underlying OS, or run multiple collaborating agents on the same host.</p>
<p>Today, I&rsquo;m happy to announce runtime instances, a new complementary compute option in Amazon Bedrock AgentCore Runtime that gives your agents persistent, managed infrastructure purpose-built for complex agent workloads.</p>
<p><span><strong>What you get</strong></span><br>Runtime instances provides AWS-managed EC2 infrastructure where you deploy multiple agents in a single runtime, each with their own dependencies and artifact types. Your agents can collaborate on the same host within shared sessions that persist for up to 14 days. The service supports GPU acceleration for compute-intensive tasks, session stop/restart to save costs during idle periods, and containerized deployments for teams that want to ship independently. For knowledge that needs to survive beyond a session, runtime instances pairs naturally with Amazon Elastic Block Store (Amazon EBS) and AgentCore Memory, which gives your agents long-term recall across sessions and environments.</p>
<p>Before today, if you wanted to keep your agents running for days or they needed GPU access, or multi-agent coordination, you had to build and manage that infrastructure yourself. You provisioned EC2 instances, configured networking, set up session management, handled scaling, and stitched together monitoring. Runtime instances handles all of that for you while integrating with the same AgentCore APIs, identity controls, and observability you already use with AgentCore Runtime microVMs.</p>
<div class="internal-linking-related-contents"><a href="https://thenokiablog.com/html-element-select-envato-tuts/" class="template-2"><span class="cta">Read more</span><span class="postTitle">HTML Element: select | Envato Tuts+</span></a></div><p>A few things that should make agent developers smile: your agents can call each other as tools within a shared session, iterating autonomously until the job is done. You bring any framework (CrewAI, LangGraph, LlamaIndex, Strands) and any model. Packaging is minimal, a <code>@app.entrypoint</code> decorator and a zip file or container image. And if your workflow spans days, hibernate Monday night and resume Wednesday morning with everything intact.</p>
<p>Runtime microVMs and runtime instances are complementary compute options that you can use independently or together through the same AgentCore runtime APIs. A lightweight orchestrator agent on runtime microVM can coordinate and dispatch work to specialized worker agents running on instances. The orchestrator handles API calls, task routing, and result aggregation using runtime microVM&rsquo;s fast scaling, while workers on Instances perform compute-intensive tasks like code compilation, security scanning, or GUI automation that require persistent state and direct OS access.</p>
<p><span><strong>Let me show you how it works<br></strong></span>I built two agents for this demo: a code writer agent that generates Python code from natural language descriptions, and a code reviewer agent that analyzes the generated code for bugs, security issues, and style improvements. Both agents share the same file system, so the reviewer can read whatever the writer produces without any data transfer or API calls between them.</p>
<p>Here is the code writer (simplified, no error handling):</p>
<pre><code class="lang-python">writer = Agent(
    model="us.anthropic.claude-sonnet-4-5-20250929-v1:0",
    system_prompt=(
        "You are a senior Python engineer. "
        "Given a task, return ONLY a single Python code block &mdash; no prose."
    ),
)

@app.entrypoint
def handler(event, context):
    task = event.get("task") or event.get("prompt")
    session_id = getattr(context, "session_id", None) or event.get("session_id")
    session_dir = SHARED_DIR / session_id
    session_dir.mkdir(parents=True, exist_ok=True)

    code = str(writer(task))
    (session_dir / "code.py").write_text(code)

    return {"agent": "writer", "wrote": str(session_dir / "code.py"), "code": code}</code></pre>
<div class="internal-linking-related-contents"><a href="https://thenokiablog.com/skipping-the-whm-getting-started-wizard-cpanel-blog/" class="template-2"><span class="cta">Read more</span><span class="postTitle">Skipping the WHM Getting Started Wizard | cPanel Blog</span></a></div><p>Here is the code reviewer agent (simplified, no error handling):</p>
<pre><code class="lang-python">reviewer = Agent(
    model="us.anthropic.claude-sonnet-4-5-20250929-v1:0",
    system_prompt=(
        "You are a strict Python code reviewer. "
        "Given code, return 3 bullet points: bugs, style, suggestions."
    ),
)

@app.entrypoint
def handler(event, context):
    session_id = getattr(context, "session_id", None) or event.get("session_id")
    code_path = SHARED_DIR / session_id / "code.py"
    code = code_path.read_text()
    review = str(reviewer(f"Review this code:nn{code}"))

    return {"agent": "reviewer", "read": str(code_path), "review": review}
</code></pre>
<p>Each agent is a Python application using Strands Agents with an <code>@app.entrypoint</code> decorator and a model of its choice. I package each one as a zip file. For this demo, I use the AWS Management Console. You can also use the AgentCore CLI, the AWS Command Line Interface (AWS CLI) or infrastructure as code.</p>
<p><strong>Step 1: Create a capacity provider.</strong></p>
<p>A capacity provider defines the EC2 infrastructure your agents run on. In the AgentCore console, I select <strong>Runtime</strong> in the left navigation, then select the <strong>Capacity providers</strong> tab and <strong>Create capacity provider</strong>.</p>
<div class="internal-linking-related-contents"><a href="https://thenokiablog.com/gone-phishing-cpanel-blog/" class="template-2"><span class="cta">Read more</span><span class="postTitle">Gone Phishing | cPanel Blog</span></a></div><p><img loading="lazy" decoding="async" class="aligncenter size-large wp-image-104781" src="https://thenokiablog.com/wp-content/uploads/2026/08/runtime-instances-persistent-compute-for-production-ai-agents-on-amazon-bedrock-agentcore-1.png" alt="ACI Create Capcity Provider 1" width="1024" height="769"></p>
<p>I give it a <strong>Name</strong>, select Linux (64-bit ARM) as the <strong>Operating system</strong>, and choose <code>c7g.2xlarge</code> as the <strong>Allowed instance types</strong>. This gives me 8 vCPUs and 16 GiB of memory, enough for both agents to run comfortably side by side.</p>
<p>Further down, I configure the <strong>VPC</strong>, <strong>subnets</strong>, and <strong>security groups</strong> for network access. Under <strong>Storage configuration</strong>, I keep the default gp3 volume. Under <strong>Service access</strong>, I select <strong>Create a new service role</strong> and let the console create the infrastructure role that manages EC2 instances on my behalf.</p>
<p>I select <strong>Create capacity</strong> provider and wait a few seconds. The status moves to <strong>Active</strong>.</p>
<p><img decoding="async" loading="lazy" class="aligncenter size-large wp-image-104783" src="https://thenokiablog.com/wp-content/uploads/2026/08/runtime-instances-persistent-compute-for-production-ai-agents-on-amazon-bedrock-agentcore-2.png" alt="ACI Create Capacity Provider 2" width="1024" height="834"></p>
<p><img decoding="async" loading="lazy" class="aligncenter size-full wp-image-104784" src="https://thenokiablog.com/wp-content/uploads/2026/08/runtime-instances-persistent-compute-for-production-ai-agents-on-amazon-bedrock-agentcore-3.png" alt="ACI Create Capacity Provider 3" width="934" height="712"></p>
<p>Note the capacity provider configuration summary: operating system, instance type, subnets, security group, instance profile, and infrastructure role. Once created, only the description can be edited, so verify your settings before you proceed.</p>
<p><img decoding="async" loading="lazy" class="aligncenter size-large wp-image-104782" src="https://thenokiablog.com/wp-content/uploads/2026/08/runtime-instances-persistent-compute-for-production-ai-agents-on-amazon-bedrock-agentcore-4.png" alt="ACI Create Capcity Provider 2" width="1024" height="860"></p>
<p><strong>Step 2: Create a runtime and deploy the first agent.</strong></p>
<p>Back on the <strong>Runtime</strong> page, I select <strong>Create runtime</strong>. I give it a <strong>Name</strong>, select Instances as the <strong>Compute type</strong>, and choose the <strong>Capacity provider</strong> I created in the previous step.</p>
<p><img decoding="async" loading="lazy" class="aligncenter size-large wp-image-104785" src="https://thenokiablog.com/wp-content/uploads/2026/08/runtime-instances-persistent-compute-for-production-ai-agents-on-amazon-bedrock-agentcore-5.png" alt="ACI Create Runtime 1" width="1024" height="444"></p>
<p>Under <strong>Agent source</strong>, I select <strong>S3 Source</strong>, then <strong>Upload to S3</strong>. I choose my agent zip file (<code>ACIDemoWriter.zip</code>), set the <strong>Language runtime</strong> to <code>Python 3.13,</code> and specify <code>agent.py</code> as the <strong>Agent entry point</strong>. This is the file that contains my <code>@app.entrypoint</code> decorated function. Under <strong>Permissions</strong>, I select <strong>Create default role</strong> to let the console provision the IAM role my agent needs.</p>
<p><img decoding="async" loading="lazy" class="aligncenter size-large wp-image-104786" src="https://thenokiablog.com/wp-content/uploads/2026/08/runtime-instances-persistent-compute-for-production-ai-agents-on-amazon-bedrock-agentcore-6.png" alt="ACI Create Runtime 2" width="1024" height="986"></p>
<p>I select <strong>Create runtime</strong> and wait for the status to become <strong>Ready</strong>.</p>
<p>I repeat the same process for my code reviewer agent. I create a second runtime, select the same capacity provider, upload my reviewer agent zip file, and wait for it to become <strong>Ready</strong>. Both agents now share the same underlying EC2 infrastructure.</p>
<p><img decoding="async" loading="lazy" class="aligncenter size-large wp-image-104926" src="https://thenokiablog.com/wp-content/uploads/2026/08/runtime-instances-persistent-compute-for-production-ai-agents-on-amazon-bedrock-agentcore-7.png" alt="AgentCore Runtime Instances - Agent Ready" width="1024" height="646">The console shows me a <strong>View invocation code</strong> section with ready-to-use Python, TypeScript, and JavaScript snippets to invoke my agent programmatically. But for this demo, I use the built-in test feature. I select <strong>Test</strong> on the writer agent&rsquo;s page.</p>
<p><img decoding="async" loading="lazy" class="aligncenter size-large wp-image-104927" src="https://thenokiablog.com/wp-content/uploads/2026/08/runtime-instances-persistent-compute-for-production-ai-agents-on-amazon-bedrock-agentcore-8.png" alt="AgentCore Runtime Instances - Show invocation code" width="1024" height="679"><strong>Step 3: Invoke agents and observe collaboration.</strong></p>
<p>The <strong>Runtime playground</strong> opens. At the top, I see three fields: <strong>Runtime agent</strong>, <strong>Endpoint</strong>, and <strong>Session ID</strong>. The console generates a session ID automatically. I take note of it because I will reuse it with the reviewer agent.</p>
<p>In the <strong>Input</strong> field, I type a JSON payload asking the writer agent to generate code:</p>
<pre><code class="lang-json">{"prompt": "write a fibonacci suite"}</code></pre>
<p>I select <strong>Run</strong>. After a few seconds, the <strong>Output</strong> panel shows the agent&rsquo;s response. The writer agent generated a Python module with two implementations of a Fibonacci sequence (a list-based function and a generator) and wrote it to <code>/tmp/agentcore-session/ca5ec24d-07f5-4eeb-add1-5ba416bf9eb2/code.py</code>. Notice the session ID in the file path. That directory is the shared file system for this session.</p>
<p><img decoding="async" loading="lazy" class="aligncenter size-large wp-image-104928" src="https://thenokiablog.com/wp-content/uploads/2026/08/runtime-instances-persistent-compute-for-production-ai-agents-on-amazon-bedrock-agentcore-9.png" alt="AgentCore Runtime Instances - Invoke code writer agent" width="1024" height="722"></p>
<p><strong>Step 4: Invoke the reviewer agent in the same session.</strong></p>
<p>Now I switch the <strong>Runtime agent</strong> dropdown to <strong>ACIDemoReviewer</strong>. The important part: I paste the same session ID (<code>ca5ec24d-07f5-4eeb-add1-5ba416bf9eb2)</code> in the <strong>Session ID</strong> field. This is what connects the two agents.</p>
<p>I type a simple prompt:</p>
<pre><code class="lang-json">{"prompt": "review the code"}</code></pre>
<p>I select <strong>Run</strong>. The reviewer agent reads the file the writer produced from the shared session directory and returns a detailed code review. It finds no critical bugs but suggests adding type hints, input validation, and simplifying the edge case handling.</p>
<p><img decoding="async" loading="lazy" class="aligncenter size-large wp-image-104929" src="https://thenokiablog.com/wp-content/uploads/2026/08/runtime-instances-persistent-compute-for-production-ai-agents-on-amazon-bedrock-agentcore-10.png" alt="AgentCore Runtime Instances - Invoke code reviewer agent" width="1024" height="740">The two agents never exchanged messages or called each other&rsquo;s APIs. They collaborated through the shared file system that runtime instances provide within a session. You can extend this pattern to any number of agents: a test agent that runs the code, a documentation agent that generates README files, a security agent that scans for vulnerabilities, all sharing the same working directory.</p>
<p><span><strong>Key details<br></strong></span>Here are a few things to know as you get started:</p>
<ul>
<li><strong>Supported OS</strong>: Linux (ARM64 and x86_64) at launch.</li>
<li><strong>Session persistence</strong>: Sessions persist for up to 14 days.</li>
<li><strong>Runtimes</strong>: Python 3.11-14 with native code support. Container images also supported.</li>
<li><strong>GPU</strong>: Support for GPU-accelerated instance types.</li>
<li><strong>Integration</strong>: Uses the same AgentCore APIs, identity, observability, and policy controls as AgentCore Runtime.</li>
<li><strong>Pricing</strong>: Standard EC2 pricing plus a management fee for AgentCore orchestration.</li>
<li><strong>Regions</strong>: US East (Ohio, N. Virginia), US West (Oregon), Asia Pacific (Mumbai, Singapore, Sydney, Tokyo), and Europe (Frankfurt, Ireland)</li>
</ul>
<p>To get started, visit the runtime instance in Amazon Bedrock AgentCore documentation<strong>&nbsp;</strong>and create your first capacity provider.</p>
<p>&mdash; seb <!-- '"` --></p>
<p>The post <a href="https://thenokiablog.com/runtime-instances-persistent-compute-for-production-ai-agents-on-amazon-bedrock-agentcore/">Runtime instances: persistent compute for production AI agents on Amazon Bedrock AgentCore</a> appeared first on <a href="https://thenokiablog.com">The NOKIA Blog</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>10 Best Free Responsive Slider &#038; Carousel WordPress Plugins</title>
		<link>https://thenokiablog.com/10-best-free-responsive-slider-carousel-wordpress-plugins/</link>
		
		<dc:creator><![CDATA[Mister Nokia]]></dc:creator>
		<pubDate>Wed, 05 Aug 2026 12:53:10 +0000</pubDate>
				<category><![CDATA[Design]]></category>
		<category><![CDATA[Animation]]></category>
		<category><![CDATA[data]]></category>
		<category><![CDATA[Featured]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[Responsive Web Design]]></category>
		<category><![CDATA[SEO]]></category>
		<category><![CDATA[Sliders]]></category>
		<category><![CDATA[Slides]]></category>
		<category><![CDATA[Slideshow]]></category>
		<category><![CDATA[Web Design]]></category>
		<category><![CDATA[WooCommerce]]></category>
		<category><![CDATA[WordPress]]></category>
		<category><![CDATA[WordPress Plugins]]></category>
		<guid isPermaLink="false">https://thenokiablog.com/10-best-free-responsive-slider-carousel-wordpress-plugins/</guid>

					<description><![CDATA[<p>Sliders and carousels occupy an interesting spot in web design. We&#8217;re often advised against using them...</p>
<p>The post <a href="https://thenokiablog.com/10-best-free-responsive-slider-carousel-wordpress-plugins/">10 Best Free Responsive Slider &amp; Carousel WordPress Plugins</a> appeared first on <a href="https://thenokiablog.com">The NOKIA Blog</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>Sliders and carousels occupy an interesting spot in web design. We&rsquo;re often advised against using them because of performance and accessibility concerns. Plus, they may force users to wait for important information. Yet, their popularity hasn&rsquo;t waned. Why?</p>
<p>Perhaps it&rsquo;s because these features are still incredibly convenient. Smart usage creates a pleasing aesthetic and can save space. And better coding practices lessen those previous concerns. This doesn&rsquo;t mean a slider or carousel is <em>always</em> appropriate. However, there are still plenty of use cases when they make sense.</p>
<p>Even better, there are so many options, particularly for WordPress websites. You&rsquo;ll find plugins that offer everything from basic image movement to full-on dynamic content presentations. Odds are, there&rsquo;s a slider or carousel plugin that will match your needs.</p>
<p>With that in mind, we&rsquo;ve rounded up a collection of the best free responsive slider and carousel plugins for WordPress. Let&rsquo;s go!</p>
<hr>
<div class="internal-linking-related-contents"><a href="https://thenokiablog.com/rose-gold-design/" class="template-2"><span class="cta">Read more</span><span class="postTitle">Rose gold design</span></a></div><p>portfolios or to spotlight sponsors. GS Logo Slider was designed specifically for this niche, giving you a quick way to create a logo presentation.</p>
<p>You&rsquo;ll find multiple layout options, drag-and-drop ordering, tooltips, hover controls, and the ability to display images in grayscale. You can display your slider via a custom shortcode or by using the included Gutenberg block.</p>
<p><img loading="lazy" decoding="async" src="https://thenokiablog.com/wp-content/uploads/2026/08/10-best-free-responsive-slider-carousel-wordpress-plugins.jpg" alt="GS Logo Slider WordPress Plugin" width="900" height="400"></p>
<p>MetaSlider offers a simple workflow for creating responsive slideshows. Drag your images onto the canvas and set captions, links, and SEO data. There are also settings to help you optimize sliders for mobile devices. Also included are multiple themes, transition/animation effects, and accessibility options.</p>
<div class="internal-linking-related-contents"><a href="https://thenokiablog.com/passion-work/" class="template-2"><span class="cta">Read more</span><span class="postTitle">Passion &amp; work</span></a></div><p><img loading="lazy" decoding="async" src="https://thenokiablog.com/wp-content/uploads/2026/08/10-best-free-responsive-slider-carousel-wordpress-plugins-1.jpg" alt="MetaSlider WordPress Plugin" width="900" height="400"></p>
<p>Create stunning sliders in no time with Smart Slider 3&rsquo;s slide library feature. The plugin offers premade slides ready for your custom content. The included slider templates will also come in handy for integrating popular styles with minimal effort.</p>
<p>Want to showcase your content? There&rsquo;s also a dynamic slide feature that will fetch your site&rsquo;s latest posts. It&rsquo;s a good choice for those who want great looks without the extra work.</p>
<p><img loading="lazy" decoding="async" src="https://thenokiablog.com/wp-content/uploads/2026/08/10-best-free-responsive-slider-carousel-wordpress-plugins-2.jpg" alt="Smart Slider 3 WordPress Plugin" width="900" height="400"></p>
<div class="internal-linking-related-contents"><a href="https://thenokiablog.com/the-joy-of-art/" class="template-2"><span class="cta">Read more</span><span class="postTitle">The joy of art</span></a></div><p>There&rsquo;s a lot to like about fetching reviews from third-party websites like Google. New reviews are added automatically, and there&rsquo;s no manual labor involved. WP Google Review Slider will download your company&rsquo;s reviews and serve them locally &ndash; great for performance.</p>
<p>The plugin integrates with the Google Places API to grab new reviews daily. There&rsquo;s also an option to display reviews in a static grid format &ndash; no slider necessary.</p>
<p><img decoding="async" loading="lazy" src="https://thenokiablog.com/wp-content/uploads/2026/08/10-best-free-responsive-slider-carousel-wordpress-plugins-3.jpg" alt="WP Google Review Slider WordPress Plugin" width="900" height="400"></p>
<p>The beauty of Carousel Slider Block is that it&rsquo;s a block-first solution. It supports all Gutenberg blocks, meaning you can add any type of content imaginable into a carousel.</p>
<p>This design also means you can preview your creations directly in the Block Editor. You&rsquo;ll also find plenty of controls to personalize your presentation.</p>
<p><img decoding="async" loading="lazy" src="https://thenokiablog.com/wp-content/uploads/2026/08/10-best-free-responsive-slider-carousel-wordpress-plugins-4.jpg" alt="Carousel Slider Block WordPress Plugin" width="900" height="400"></p>
<p>Accessibility shouldn&rsquo;t be an afterthought when building a slider or carousel. Accessible Carousel outputs keyboard-navigable and screen reader-friendly presentations. It also features a graceful fallback when JavaScript isn&rsquo;t enabled.</p>
<p>The plugin includes a hero carousel, card scroller, and premade block patterns. You can also turn off autoplay to put users in control.</p>
<p><img decoding="async" loading="lazy" src="https://thenokiablog.com/wp-content/uploads/2026/08/10-best-free-responsive-slider-carousel-wordpress-plugins-5.jpg" alt="Accessible Carousel WordPress Plugin" width="900" height="400"></p>
<p>Depicter helps you build interactive sliders and beyond. The included template library will get your project off to a fast start, while an array of visual effects will grab user attention. You also get tools to build carousels, notification bars, and popups. The plugin is a jack-of-all-trades for interactivity.</p>
<p><img decoding="async" loading="lazy" src="https://thenokiablog.com/wp-content/uploads/2026/08/10-best-free-responsive-slider-carousel-wordpress-plugins-6.jpg" alt="Depicter WordPress Plugin" width="900" height="400"></p>
<p>Here&rsquo;s a plugin that lets you create compelling sliders without weighing you down. Super Block Slider uses a 5KB JavaScript file to power your slideshows &ndash; great for even slow connections. Sliders are responsive and allow you to set different backgrounds for mobile devices.</p>
<p>There are also settings for custom overlays, parallax effects, animations, and navigation styles.</p>
<p><img decoding="async" loading="lazy" src="https://thenokiablog.com/wp-content/uploads/2026/08/10-best-free-responsive-slider-carousel-wordpress-plugins-7.jpg" alt="Super Block Slider WordPress Plugin" width="900" height="400"></p>
<p>Slideshows can be a great way to show off your WooCommerce products. Woo Product Slider offers a quick setup via templates and the ability to display new and featured products. No worries if your store has hidden products, as you can choose to add them to your slider.</p>
<p>There are also options for displaying product details and an add-to-cart button.</p>
<p><img decoding="async" loading="lazy" src="https://thenokiablog.com/wp-content/uploads/2026/08/10-best-free-responsive-slider-carousel-wordpress-plugins-8.jpg" alt="Woo Product Slider WordPress Plugin" width="900" height="400"></p>
<p>Sticking with e-commerce, Product Gallery Slider for WooCommerce adds interactivity to your product pages. It improves on the standard Woo product gallery with a responsive slider that can handle all your product images.</p>
<p>It&rsquo;s perfect for displaying product variations, such as colors or styles. Other features include a lightbox, caption, and image zoom toggles.</p>
<p><img decoding="async" loading="lazy" src="https://thenokiablog.com/wp-content/uploads/2026/08/10-best-free-responsive-slider-carousel-wordpress-plugins-9.jpg" alt="Product Gallery Slider for WooCommerce WordPress Plugin" width="900" height="400"></p>
<h2><span id="WordPress_Slider_Plugins_at_a_Glance">WordPress Slider Plugins at a Glance</span></h2>
<div>
<table class="table-wrapped">
<thead>
<tr>
<th><strong>Plugin</strong></th>
<th><strong>Description</strong></th>
<th><strong>Primary Use</strong></th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>GS Logo Slider</strong></td>
<td>Creates responsive logo presentations with multiple layouts, drag-and-drop ordering, tooltips, hover controls, and grayscale images.</td>
<td>Logo and sponsor sliders.</td>
</tr>
<tr>
<td><strong>MetaSlider Plugin</strong></td>
<td>Creates responsive image slideshows with captions, links, SEO data, themes, transition effects, and accessibility options.</td>
<td>Responsive image slideshows.</td>
</tr>
<tr>
<td><strong>Smart Slider 3</strong></td>
<td>Provides premade slides and templates along with dynamic slides that can automatically display your site&rsquo;s latest posts.</td>
<td>Content and image sliders.</td>
</tr>
<tr>
<td><strong>WP Google Review Slider</strong></td>
<td>Downloads Google reviews for local display, automatically fetches new reviews, and includes a static grid option.</td>
<td>Google review sliders.</td>
</tr>
<tr>
<td><strong>Carousel Slider Block</strong></td>
<td>Creates carousels using Gutenberg blocks, allowing virtually any type of content to be added and previewed in the Block Editor.</td>
<td>Block-based content carousels.</td>
</tr>
<tr>
<td><strong>Accessible Carousel</strong></td>
<td>Creates keyboard-navigable and screen reader-friendly carousels with hero, card scroller, block pattern, and autoplay options.</td>
<td>Accessible sliders and carousels.</td>
</tr>
<tr>
<td><strong>Depicter</strong></td>
<td>Builds interactive sliders and carousels using templates and visual effects, with additional tools for notification bars and popups.</td>
<td>Interactive sliders and carousels.</td>
</tr>
<tr>
<td><strong>Super Block Slider</strong></td>
<td>Creates lightweight responsive sliders with mobile backgrounds, overlays, parallax effects, animations, and customizable navigation.</td>
<td>Lightweight responsive sliders.</td>
</tr>
<tr>
<td><strong>Woo Product Slider</strong></td>
<td>Displays WooCommerce products in sliders with templates, new and featured product options, product details, and add-to-cart buttons.</td>
<td>WooCommerce product sliders.</td>
</tr>
<tr>
<td><strong>Product Gallery Slider for WooCommerce</strong></td>
<td>Replaces the standard WooCommerce product gallery with a responsive slider featuring lightbox, captions, and image zoom controls.</td>
<td>WooCommerce product galleries.</td>
</tr>
</tbody>
</table>
</div>
<h2><span id="Slide_On_In">Slide On In</span></h2>
<p>This collection of free responsive WordPress slider plugins covers just about every use case. Whether you&rsquo;re looking to showcase your site&rsquo;s content, social media uploads, or enhance your e-commerce shop, you&rsquo;ll find a tool that can help you achieve your goals.</p>
<div class="darp-related-posts">
<h2>Related Articles</h2>
</div>
<div class="topics">
<h2>Related Topics</h2>
</div>
<p>                                here. He recently started a writing service for WordPress products: WP Product Writeup.  He also has an opinion on just about every subject. You can follow his rants on Bluesky @karks.com.</p>
<p>
                        Read more articles by Eric Karkovack
                    </p>

<hr>
<p>The post <a href="https://thenokiablog.com/10-best-free-responsive-slider-carousel-wordpress-plugins/">10 Best Free Responsive Slider &amp; Carousel WordPress Plugins</a> appeared first on <a href="https://thenokiablog.com">The NOKIA Blog</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Weaponizing And Defending The React Flight Protocol: Deserialization Sinks In RSCs</title>
		<link>https://thenokiablog.com/weaponizing-and-defending-the-react-flight-protocol-deserialization-sinks-in-rscs/</link>
		
		<dc:creator><![CDATA[Mister Nokia]]></dc:creator>
		<pubDate>Tue, 21 Jul 2026 10:00:00 +0000</pubDate>
				<category><![CDATA[Design]]></category>
		<category><![CDATA[.NET]]></category>
		<category><![CDATA[Business]]></category>
		<category><![CDATA[data]]></category>
		<category><![CDATA[Database]]></category>
		<category><![CDATA[Developer]]></category>
		<category><![CDATA[Fonts]]></category>
		<category><![CDATA[Hosting]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[Security]]></category>
		<guid isPermaLink="false">https://thenokiablog.com/weaponizing-and-defending-the-react-flight-protocol-deserialization-sinks-in-rscs/</guid>

					<description><![CDATA[<p>While React Server Components rely on the custom Flight protocol to stream interactive UIs, this same...</p>
<p>The post <a href="https://thenokiablog.com/weaponizing-and-defending-the-react-flight-protocol-deserialization-sinks-in-rscs/">Weaponizing And Defending The React Flight Protocol: Deserialization Sinks In RSCs</a> appeared first on <a href="https://thenokiablog.com">The NOKIA Blog</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p><section aria-label="Quick summary" class="article__summary"><span id="article__start" class="summary__heading" aria-hidden="true"></span>While React Server Components rely on the custom Flight protocol to stream interactive UIs, this same mechanism introduces powerful deserialization sinks that attackers can exploit. Durgesh Pawar breaks down the mechanics behind the CVSS 10.0 &ldquo;React2Shell&rdquo; vulnerability to show how protocol manipulation can lead to remote code execution. It also covers a practical, ranked set of defenses, from strict schema validation to CSRF hardening, for securing React applications against these structural risks.</section>
</p>
<p>React Server Components don&rsquo;t send HTML to your browser. They don&rsquo;t send JSON either. When a server component renders, what actually travels over the wire is a custom streaming protocol called <strong>Flight</strong>. It&rsquo;s a line-delimited format with its own type system, its own reference resolution, and its own rules for reconstructing executable behavior on the client.</p>
<p>Most React developers have never opened the Network tab and actually looked at a Flight payload. It looks like a mix of JSON fragments, dollar-sign-prefixed references, and module pointers that the React runtime silently reassembles into a live component tree. The framework handles it, so nobody questions it.</p>
<p>I&rsquo;m not sure most teams have thought carefully about what that trust actually implies.</p>
<div class="internal-linking-related-contents"><a href="https://thenokiablog.com/rose-gold-design/" class="template-2"><span class="cta">Read more</span><span class="postTitle">Rose gold design</span></a></div><p>I started pulling apart the Flight protocol after CVE-2025-55182 dropped in December 2025. The security community called it <strong>React2Shell</strong>, and for good reason. It was a CVSS 10.0, unauthenticated remote code execution vulnerability sitting in the Flight deserialization layer. One crafted HTTP request to a Server Function endpoint, and an attacker had shell access. No credentials needed.</p>
<p>The federal Cybersecurity &amp; Infrastructure Agency (CISA) added it to the Known Exploited Vulnerabilities catalog. Sysdig tied in-the-wild exploitation to North Korean state-sponsored actors deploying file-less implants through the Ethereum blockchain. That&rsquo;s the kind of CVE that gets your attention.</p>
<p>After spending time in the source (mostly <code>getOutlinedModel</code> and <code>getChunk</code>, which is where the resolution logic that matters actually lives), I realized React2Shell wasn&rsquo;t a one-off parsing bug. It was a symptom. Flight reconstructs executable references, lazy-loaded components, server RPC endpoints, and async state from a stream of text. That&rsquo;s a <strong>deserialization system</strong>.</p>
<p>The attack surface extends well beyond a single missing hasOwnProperty check. This article covers how Flight works on the wire, where the deserialization sinks are, what attackers have already weaponized, and what&rsquo;s still exposed.</p>
<div class="internal-linking-related-contents"><a href="https://thenokiablog.com/passion-work/" class="template-2"><span class="cta">Read more</span><span class="postTitle">Passion &amp; work</span></a></div><p>This leads to a <strong>ranked, practical set of defenses</strong> for your own Server Components: schema validation on every Server Action, the server-only package, cross-site request forgery (CSRF) hardening beyond framework defaults, and an assessment of what the Taint API and Web Application Firewalls (WAFs) provide.</p>
<div data-audience="non-subscriber" data-remove="true" class="feature-panel-container"><img decoding="async" loading="lazy" class="feature-panel-image-img" src="https://thenokiablog.com/wp-content/uploads/2026/07/weaponizing-and-defending-the-react-flight-protocol-deserialization-sinks-in-rscs.jpg" alt="Feature Panel" width="690" height="790"></div>


<h3 id="table-of-contents">Table of Contents</h3>
<h2 id="flight-on-the-wire">Flight On The Wire</h2>
<p>Open your browser&rsquo;s Network tab on any Next.js App Router page and look for requests returning <code>Content-Type: text/x-component</code>. That&rsquo;s Flight. It&rsquo;s not a single JSON blob. It&rsquo;s a streaming, line-delimited format where each line is a self-contained &ldquo;row&rdquo; that the client-side React runtime processes as it arrives over the connection.</p>
<p>Here&rsquo;s what a simple Flight payload looks like in practice:</p>
<div class="break-out">
<pre><code class="language-javascript">1:I["./src/components/ClientComponent.js",["chunks/main.js"],"default"]
2:J["$","article",null,{"children":"$1"}]
0:D{"name":"RootLayout","env":"Server"}
</code></pre>
</div>
<p>Row 1 is an import directive. It tells the client to load <code>ClientComponent.js</code> from the bundler&rsquo;s chunk map. Row 2 is a JSON tree that constructs an <code>&lt;article&gt;</code> HTML element, and the <code>"$1"</code> inside <code>children</code> is a reference back to chunk 1 (the imported component). Row 0 defines the server execution context, marking this as a <code>RootLayout</code> running in the <code>Server</code> environment. Even in this tiny example, you can see the mix of structural data, module references, and cross-chunk pointers that makes Flight different from plain JSON.</p>
<h3 id="the-row-format">The Row Format</h3>
<div class="internal-linking-related-contents"><a href="https://thenokiablog.com/the-joy-of-art/" class="template-2"><span class="cta">Read more</span><span class="postTitle">The joy of art</span></a></div><p>Every row follows the same syntax: <code>&lt;ROW_ID&gt;:&lt;ROW_TAG&gt;&lt;PAYLOAD&gt;n</code>. The row ID is a numeric identifier that other rows can reference. The tag is a single character (or short string) that tells the parser what kind of data follows. The payload is the actual content.</p>
<p>Here are the row tags I found while reading through the source:</p>
<table class="tablesaw break-out">
<thead>
<tr>
<th>Tag</th>
<th>Name</th>
<th>What it does</th>
</tr>
</thead>
<tbody>
<tr>
<td>J</td>
<td>JSON Tree</td>
<td>Serialized virtual DOM nodes, component props, and HTML elements.</td>
</tr>
<tr>
<td>M</td>
<td>Module</td>
<td>Metadata for a specific Client Component module or chunk.</td>
</tr>
<tr>
<td>I</td>
<td>Import</td>
<td>Tells the client to load a module from the bundler&rsquo;s chunk map.</td>
</tr>
<tr>
<td>HL</td>
<td>Hint/Preload</td>
<td>Instructs the browser to preload resources such as stylesheets or fonts.</td>
</tr>
<tr>
<td>D</td>
<td>Data</td>
<td>Server-rendered element context and environment info.</td>
</tr>
<tr>
<td>E</td>
<td>Error</td>
<td>Serialized server-side exceptions and error boundaries.</td>
</tr>
</tbody>
</table>
<p>So far, this might look like a benign structured data format with some custom tags, but the real complexity and attack surface live in the <strong>prefix system</strong>.</p>
<h3 id="the-prefix-system">The <code>$</code> Prefix System</h3>
<p>This is where I started paying closer attention.</p>
<p>When the client-side parser encounters a string value starting with <code>$</code>, it doesn&rsquo;t treat it as literal text. It intercepts the string, checks the prefix, and routes it through a type-specific resolution path. The <code>parseModelString</code> function in <code>ReactFlightClient.js</code> is where this happens. It&rsquo;s essentially a big switch statement on the character after <code>$</code>.</p>
<table class="tablesaw break-out">
<thead>
<tr>
<th>Prefix</th>
<th>Type</th>
<th>What the parser does with it</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>$</code></td>
<td>Model Reference</td>
<td>Resolves to another chunk in the stream (e.g., <code>$2</code> points to row 2).</td>
</tr>
<tr>
<td><code>$:</code></td>
<td>Property Access</td>
<td>Traverses into a resolved chunk&rsquo;s properties (e.g., <code>$1:user:name</code>).</td>
</tr>
<tr>
<td><code>$S</code></td>
<td>Symbol</td>
<td>Creates a native JavaScript <code>Symbol</code>.</td>
</tr>
<tr>
<td><code>$F</code></td>
<td>Server Reference</td>
<td>Represents a callable Server Action (an RPC endpoint on the server).</td>
</tr>
<tr>
<td><code>$L</code></td>
<td>Lazy Component</td>
<td>Defers component loading until it&rsquo;s needed in the render tree</td>
</tr>
<tr>
<td><code>$@</code></td>
<td>Promise/Raw Chunk</td>
<td>Returns the internal Chunk wrapper object itself (often acting as a Thenable/Promise), not its resolved value.</td>
</tr>
<tr>
<td><code>$B</code></td>
<td>Blob/Binary</td>
<td>Triggers the blob deserialization handler for binary data.</td>
</tr>
</tbody>
</table>
<p>Every other prefix resolves a chunk and gives you the parsed result. <code>$@</code> hands you the raw internal <code>Chunk</code> object instead, the wrapper React uses to track resolution state, pending callbacks, and internal metadata (which is why it&rsquo;s used for Promises and why exploits use it to get a mutable handle). Exposing framework plumbing through the protocol looks like a design mistake to me, though I&rsquo;d be interested to hear the rationale if there is one.</p>
<p>And <code>$:</code> (property access) is the other critical prefix. It lets the protocol specify a path like <code>$1:user:name</code>, which tells the parser to resolve chunk 1, then access <code>.user</code>, then access <code>.name</code> on the result. That&rsquo;s arbitrary property traversal driven by data in the stream. If you&rsquo;ve spent any time auditing JavaScript for prototype pollution, that pattern should feel familiar.</p>
<h3 id="this-is-not-just-a-data-format">This Is Not Just A Data Format</h3>
<p>Flight is not JSON with extra steps. JSON gives you data. Flight gives you <strong>behavior</strong>. It reconstructs module references that trigger client-side code loading, creates server action endpoints the client can invoke as RPC calls, sets up Promise chains that the React runtime will <code>await</code>, and builds lazy-loaded component boundaries that execute on demand.</p>
<p>Whether React developers think of it that way or not, the mechanics look very similar to deserialization systems that have historically caused problems. The stream doesn&rsquo;t just describe what the UI looks like. It instructs the client runtime on what code to load, what functions to call, and what to trust.</p>
<p>If you want to read the implementation yourself, fair warning: the chunk resolution path is miserable to follow. State transitions bounce between helper functions, and the naming obscures what the code is actually doing. I gave up on static reading and just set breakpoints. The key files are react-client/src/ReactFlightClient.js for the client-side parser (look for <code>parseModelString</code>, <code>getChunk</code>, <code>reviveModel</code>, and <code>getOutlinedModel</code>) and react-server/src/ReactFlightServer.js for the serialization side. The reply handler for Server Actions lives in react-server/src/ReactFlightReplyServer.js.</p>
<h2 id="why-flight-is-a-deserialization-sink">Why Flight Is A Deserialization Sink</h2>
<p>The deserialization pattern is familiar: Java&rsquo;s <code>ObjectInputStream</code> gave us ysoserial, Python&rsquo;s <code>pickle</code> executes code on <code>load()</code>, PHP&rsquo;s <code>unserialize</code> chains <code>__wakeup</code> and <code>__destruct</code> methods, and .NET&rsquo;s <code>BinaryFormatter</code> was deprecated entirely.</p>
<blockquote><p>The pattern: deserialize attacker-controlled input &rarr; invoke behavior during reconstruction &rarr; lose control of execution.</p></blockquote>
<p>So JavaScript should be immune to this, right? <code>JSON.parse()</code> only produces plain data objects. No constructors fire. No magic methods run. You get back exactly what the JSON string describes, nothing more.</p>
<p>That&rsquo;s true for raw <code>JSON.parse()</code>. But it stops being true the moment a framework wraps custom deserialization logic around it. And that&rsquo;s exactly what Flight does.</p>
<h3 id="prototype-pollution">Prototype Pollution</h3>
<p>JavaScript uses prototype-based inheritance. Every object has a <code>__proto__</code> link to its prototype, and property lookups walk up this chain. If an attacker injects <code>__proto__</code> or <code>constructor.prototype</code> as a key during reconstruction, they modify the shared base prototypes that all objects inherit from. Downstream code reads attacker-controlled values without knowing.</p>
<p>Flight&rsquo;s <code>$:</code> prefix performs property traversal on deserialized objects. The <code>getOutlinedModel</code> function walks colon-separated paths like <code>$1:user:name</code> by iterating through each segment and accessing it on the parent object. If those path segments include <code>__proto__</code> or <code>constructor</code>, the traversal walks straight up the prototype chain. That&rsquo;s not a theoretical risk. It&rsquo;s exactly how React2Shell worked.</p>
<h3 id="duck-typing-and-thenables">Duck Typing and Thenables</h3>
<p>The V8 engine (and the JavaScript spec) treats any object with a <code>.then</code> property as a <strong>Thenable</strong>. When you <code>await</code> something, the runtime checks for <code>.then</code> and calls it if it exists. No class check. No internal slot verification. If <code>.then</code> is callable, it gets invoked.</p>
<p>Flight resolves chunks asynchronously. If an attacker constructs an object with a manipulated <code>.then</code> property and gets it into the chunk resolution pipeline, the runtime calls the attacker&rsquo;s function during normal await behavior. The language semantics do the work.</p>
<p>I initially focused on <code>$F</code> because forging Server Action references seemed like the obvious attack surface. After tracing the resolution path, <code>$:</code> property traversal looked much more interesting. I also spent a few hours examining chunk status transitions (pending, blocked, resolved, errored) to see if you could force a chunk into an unexpected state, though that approach didn&rsquo;t yield any results.</p>
<h3 id="the-core-problem">The Core Problem</h3>
<p>These two risks converge in Flight because the protocol doesn&rsquo;t just deserialize data. It deserializes <em>behavior</em>. The <code>$</code> prefix system dictates which execution path the parser takes: <code>$F</code> creates a callable server endpoint, <code>$L</code> sets up lazy code loading, <code>$B</code> triggers a blob handler, <code>$@</code> exposes internal framework state. The parser&rsquo;s control flow is driven entirely by what&rsquo;s in the stream.</p>
<p>If an attacker can influence the stream&rsquo;s content, they control which functions the parser calls, which objects it constructs, and which internal state it exposes.</p>
<h2 id="the-mechanics-of-react2shell">The Mechanics Of React2Shell</h2>
<p>This is the CVE that proved the theory. CVE-2025-55182, nicknamed React2Shell, is a CVSS 10.0 unauthenticated remote code execution vulnerability in the Flight deserialization layer. One HTTP request, no login required, full shell access.</p>
<p>I want to walk through the entire gadget chain because understanding it reveals how much power the Flight protocol hands to an attacker who can control the stream.</p>
<h3 id="the-root-cause">The Root Cause</h3>
<p>The vulnerability sits in <code>getOutlinedModel</code>, a function responsible for resolving deep property paths from the <code>$:</code> reference system. The instance used in the exploit chain lives in the server-side reply handling code (<code>ReactFlightReplyServer.js</code>). When the parser encounters a reference like <code>$1:user:name</code>, it splits on the colons and walks the path segment by segment. Here&rsquo;s the vulnerable loop:</p>
<pre><code class="language-javascript">for (key = 1; key &lt; reference.length; key++)
    parentObject = parentObject[reference[key]];
</code></pre>
<p>Two lines. No <code>hasOwnProperty</code> check. No validation that the property exists on the object itself rather than somewhere up the prototype chain. Just <code>parentObject[reference[key]]</code> and move on.</p>
<p>So an attacker supplies <code>$1:__proto__:constructor:constructor</code>, and the loop traverses from a plain JSON object up through <code>Object.prototype</code> to the <code>Object</code> constructor to the <code>Function</code> constructor. <code>Function</code> in JavaScript behaves like <code>eval()</code>. <code>Function("arbitrary code")()</code> executes.</p>
<p>No allowlist on property names. No check for <code>__proto__</code>. I searched <code>reviveModel</code> and the chunk initialization path for any filtering. Nothing.</p>
<h3 id="the-gadget-chain">The Gadget Chain</h3>
<p>Getting from <em>&ldquo;I can reach the Function constructor&rdquo;</em> to <em>&ldquo;I have RCE&rdquo;</em> requires chaining several Flight protocol features together. The Resecurity write-up covers the full chain in detail; here&rsquo;s the high-level sequence:</p>
<ul>
<li><strong>Step 1: Prototype walk to Function.</strong><br>The <code>$:</code> path <code>__proto__:constructor:constructor</code> walks from any plain object to <code>Object.prototype</code>, then to the <code>Object</code> constructor, then to <code>Function</code> &mdash; JavaScript&rsquo;s built-in <code>eval()</code> equivalent.</li>
<li><strong>Step 2: Raw chunk self-reference.</strong><br><code>$@0</code> returns the raw internal <code>Chunk</code> wrapper instead of its resolved value, giving the attacker a mutable handle on React&rsquo;s internal state machine.</li>
<li><strong>Step 3: Thenable hijack.</strong><br>The attacker sets the chunk&rsquo;s <code>.then</code> to <code>Chunk.prototype.then</code>, so React&rsquo;s resolution pipeline treats the manipulated chunk as a legitimate Promise-like object and awaits it.</li>
<li><strong>Step 4: Context confusion.</strong><br>During the second deserialization pass, the payload overwrites <code>_response._formData.get</code> to point to the hijacked <code>Function</code> constructor and places the attacker&rsquo;s shell command into <code>_response._prefix</code>.</li>
<li><strong>Step 5: Trigger via blob handler.</strong><br><code>$B0</code> invokes the blob handler, which internally calls <code>response._formData.get(response._prefix + blobId)</code> &mdash; now equivalent to <code>Function("attacker_shell_command")()</code>. That&rsquo;s arbitrary code execution with whatever privileges the Node.js process has.</li>
</ul>
<p>Each step uses a legitimate Flight protocol feature in a way the designers didn&rsquo;t anticipate. There&rsquo;s no single &ldquo;broken&rdquo; feature. The vulnerability emerges from how these features compose when an attacker controls the input.</p>
<h3 id="impact">Impact</h3>
<p>The numbers on this one are stark:</p>
<ul>
<li>CVSS 10.0. The maximum possible score.</li>
<li>Unauthenticated and pre-auth. No credentials needed &mdash; and the deserialization happens before any application-level auth checks run, so even endpoints behind login walls are exposed.</li>
<li>Single HTTP request. One POST to a Server Function endpoint.</li>
<li>Affected React 19.0.0, 19.1.0, 19.1.1, and 19.2.0, across <code>react-server-dom-webpack</code>, <code>react-server-dom-parcel</code>, and <code>react-server-dom-turbopack</code>.</li>
<li>CISA added it to the Known Exploited Vulnerabilities catalog within days.</li>
</ul>
<h3 id="what-happened-in-the-wild">What Happened In The Wild</h3>
<p>Exploitation was immediate. Sysdig published research linking <strong>EtherRAT</strong> deployments to North Korean state-sponsored actors who weaponized the vulnerability within hours of disclosure. EtherRAT is a file-less implant that uses the Ethereum blockchain for command-and-control communication &mdash; a technique researchers call &ldquo;EtherHiding&rdquo; &mdash; making takedown nearly impossible because you can&rsquo;t seize a blockchain.</p>
<p>Separately, Palo Alto&rsquo;s Unit 42 documented a backdoor called <strong>KSwapDoor</strong> that masquerades as <code>[kswapd1]</code> on infected Linux systems, blending into process lists alongside the legitimate <code>kswapd0</code> kernel swap daemon; their analysis confirms KSwapDoor uses RC4 encryption to protect its internal strings and configuration data, while C2 communications run over AES-256-CFB with Diffie-Hellman key exchange across a P2P mesh network. The speed and sophistication of these campaigns &mdash; state-sponsored actors deploying novel implants through a single unauthenticated HTTP request &mdash; underscores why a CVSS 10.0 in a deserialization layer demands immediate patching, not triage.</p>
<h2 id="the-fix">The Fix</h2>
<p>The React team&rsquo;s patch is clean and targeted. The core change caches the genuine <code>hasOwnProperty</code> method at module load time:</p>
<pre><code class="language-javascript">var hasOwnProperty = Object.prototype.hasOwnProperty;
</code></pre>
<p>Then every property check in the deserialization path uses <code>.call()</code> to invoke the cached reference:</p>
<pre><code class="language-javascript">hasOwnProperty.call(value, i);
</code></pre>
<p>Even if an attacker shadows <code>hasOwnProperty</code> on a malicious object, the check uses the original prototype method. The prototype chain traversal that powered the gadget chain is blocked. This fix shipped in React 19.0.1, 19.1.2, and 19.2.1.</p>
<p>The fix is correct. But reading through the patches, I noticed the React team hardened ownership checks while leaving the property traversal model intact. The <code>$:</code> prefix still walks colon-separated paths; it just validates each step now. I think exposing arbitrary property traversal through a network protocol was a design mistake, and the patch treats the symptom. If future bugs emerge, they&rsquo;ll likely come from this same area.</p>
<p>The framework patch closes the known gadget chain, but it doesn&rsquo;t change the fundamental dynamic: the Flight protocol still reconstructs behavior &mdash; executable references, module imports, RPC endpoints, async state &mdash; from a stream of text. That reconstruction happens before your application code runs, before your validation logic fires, before your auth middleware even sees the request. Relying solely on the framework to protect your Server Components means trusting that every edge case in a complex deserialization parser has been found and fixed. The defenses that follow are the practical steps you can take to limit the blast radius on your own.</p>
<h2 id="defenses-ranked-by-impact">Defenses, Ranked By Impact</h2>
<p>Some of these close real attack paths. Others mostly make you feel safer than you are. I&rsquo;ve ranked these from most-to-least impactful based on what I&rsquo;ve seen in the vulnerability research. If you only have time for one change, start at the top.</p>
<h3 id="1-input-validation-on-server-actions-zod-valibot">1. Input Validation On Server Actions (Zod, Valibot)</h3>
<p>This is the single most impactful thing you can do at the application level. The Flight deserializer processes raw, unvalidated network input before your code takes control. Strict schema validation is your primary defense against whatever the protocol reconstructs.</p>
<p>Put a schema validation call at the very top of every Server Action, before any business logic runs &mdash; and I mean before <em>anything</em>, including logging. If you log an argument before validating it, and that argument triggers the stringification bug from CVE-2025-55183, you&rsquo;ve leaked source code before your validation even had a chance to run.</p>
<p>Zod and Valibot both work well for this. Validate types, shapes, string lengths, numeric bounds, and enumerated values. Reject anything that doesn&rsquo;t match. Use <code>.safeParse()</code>, not <code>.parse()</code> &mdash; the throwing variant can surface internal error details in the response if you&rsquo;re not careful with your error boundaries.</p>
<pre><code class="language-typescript">"use server"
import { z } from "zod"

const UpdateProfileSchema = z.object({
  name: z.string().min(1).max(100),
  email: z.string().email(),
  role: z.enum(["user", "editor"]),
})

export async function updateProfile(formData: FormData) {
  const parsed = UpdateProfileSchema.safeParse({
    name: formData.get("name"),
    email: formData.get("email"),
    role: formData.get("role"),
  })
  if (!parsed.success) return { error: "Invalid input" }
  // proceed with parsed.data, this is now the only shape
  // your business logic ever sees
}
</code></pre>
<p><strong>One important nuance</strong>: If your Server Action accepts a plain object argument (not <code>FormData</code>), validate the whole argument &mdash; don&rsquo;t destructure first and validate fields individually. Destructuring before validation means you&rsquo;re already accessing properties on the unvalidated input, which is exactly the kind of operation the Flight deserializer can exploit.</p>
<pre><code class="language-typescript">"use server"
import { z } from "zod"

const CommentSchema = z.object({
  postId: z.string().uuid(),
  body: z.string().min(1).max(5000),
})

// Good: validate the raw argument first
export async function addComment(data: unknown) {
  const parsed = CommentSchema.safeParse(data)
  if (!parsed.success) return { error: "Invalid input" }
  await db.comments.create(parsed.data)
}

// Bad: destructuring before validation
export async function addCommentUnsafe(
  { postId, body }: { postId: string; body: string }
) {
  // by the time this runs, you've already accessed properties
  // on the deserialized input
  const parsed = CommentSchema.safeParse({ postId, body })
  // ...
}
</code></pre>
<p>If your Server Action doesn&rsquo;t start with a schema parse, it&rsquo;s a vulnerability waiting to happen. I&rsquo;d argue this should be a lint rule &mdash; and if you&rsquo;re running <code>eslint-plugin-react</code>, consider writing a custom rule that flags any <code>"use server"</code> export without a validation call in its first statement.</p>
<h3 id="2-the-server-only-package">2. The <code>server-only</code> Package</h3>
<p>The <code>server-only</code> package is straightforward and effective.</p>
<p>Import <code>server-only</code> at the top of any file that contains database credentials, raw API calls, internal business logic, or anything else that should never cross the server-client boundary. If a Client Component tries to import that file (directly or transitively), the build fails with a clear error.</p>
<pre><code class="language-typescript">import "server-only"
import { db } from "./database"

export async function getUser(id: string) {
  return db.query("SELECT * FROM users WHERE id = $1", [id])
}
</code></pre>
<p>The failure mode to watch for is barrel files. If you re-export a server-only function through an <code>index.ts</code> that also exports client-safe utilities, any Client Component importing from that barrel will pull in the <code>server-only</code> module transitively and break the build &mdash; or worse, if the barrel doesn&rsquo;t include the <code>server-only</code> import itself, it may silently let server code through. Keep server-only modules in separate files with their own import paths.</p>
<pre><code class="language-typescript">// Don't do this: barrel re-export mixes boundaries
// src/utils/index.ts
export { getUser } from "./users"     // has "server-only"
export { formatDate } from "./dates"  // client-safe

// Do this: separate import paths
// Client Component imports from "src/utils/dates" directly
// Server Component imports from "src/utils/users" directly
</code></pre>
<p>It also won&rsquo;t protect you from data leaking through <em>return values</em>. If a Server Component calls <code>getUser()</code> and passes the full user object (including <code>passwordHash</code> or <code>internalRole</code>) as props to a Client Component, that data rides the Flight stream to the browser. The <code>server-only</code> guard prevents the <em>code</em> from crossing the boundary, not the <em>data</em> the code returns. You must explicitly filter your return shapes.</p>
<h3 id="3-csrf-protections">3. CSRF Protections</h3>
<p>After CVE-2026-27978, relying solely on Next.js&rsquo;s built-in <code>Origin</code> vs. <code>Host</code> header check isn&rsquo;t enough. The <code>Origin: null</code> bypass showed that framework-level CSRF protection has edge cases.</p>
<p>For state-changing Server Actions (anything that writes data, deletes records, or modifies permissions), layer your own protections on top of the framework&rsquo;s defaults.</p>
<p><strong>Cookie configuration.</strong><br>Set <code>SameSite=Strict</code> or <code>SameSite=Lax</code> on session cookies. If you&rsquo;re using <code>next-auth</code> or a custom session library, verify this is set explicitly &mdash; don&rsquo;t rely on browser defaults, which vary.</p>
<pre><code class="language-typescript">// next.config.js or your auth configuration
cookies: {
  sessionToken: {
    name: "__session",
    options: {
      httpOnly: true,
      sameSite: "strict",
      secure: process.env.NODE_ENV === "production",
      path: "/",
    },
  },
}
</code></pre>
<p><strong>Explicit CSRF tokens.</strong><br>For high-value operations (password changes, role assignments, payment actions), generate a per-session CSRF token on the server, embed it in a hidden form field or custom header, and validate it in the Server Action before proceeding.</p>
<pre><code class="language-typescript">"use server"
import { cookies } from "next/headers"
import { validateCsrfToken } from "@/lib/csrf"

export async function deleteAccount(formData: FormData) {
  const token = formData.get("csrf_token") as string
  const sessionToken = (await cookies()).get("csrf_secret")?.value
  if (!validateCsrfToken(token, sessionToken)) {
    return { error: "Invalid request" }
  }
  // proceed with deletion
}
</code></pre>
<p><strong>The <code>allowedOrigins</code> gotcha.</strong><br>Never, under any circumstances, add <code>'null'</code> to <code>experimental.serverActions.allowedOrigins</code> in your Next.js config (even if the officially advisory is more nuanced, saying <em>&ldquo;unless intentionally required and additionally protected&rdquo;</em>). That string literal matches <code>Origin: null</code> &mdash; the exact header that sandboxed iframes send &mdash; and it reopens the CVE-2026-27978 bypass. If you&rsquo;re seeing CSRF failures from legitimate requests, the fix is to configure your reverse proxy to set the correct <code>Origin</code> and <code>Host</code> headers, not to weaken the validation.</p>
<pre><code class="language-javascript">// Never do this
module.exports = {
  experimental: {
    serverActions: {
      allowedOrigins: ["null"],  // reopens CSRF bypass
    },
  },
}
</code></pre>
<h3 id="4-the-hasownproperty-patch">4. The <code>hasOwnProperty</code> Patch</h3>
<p>I covered this in detail in the React2Shell section. The fix is correct, and it completely neutralizes the known gadget chain. It shipped fast, which I respect.</p>
<p>The action item here is to verify you&rsquo;re actually running a patched version. The RCE fix landed in React 19.0.1, 19.1.2, and 19.2.1. Check your lockfile:</p>
<pre><code class="language-bash"># npm
npm ls react react-dom react-server-dom-webpack

# pnpm
pnpm ls react react-dom react-server-dom-webpack

# yarn
yarn why react-server-dom-webpack
</code></pre>
<p>If you see 19.0.0, 19.1.0&acirc;&euro;&ldquo;19.1.1, or 19.2.0, you&rsquo;re vulnerable to the RCE. Update immediately. And don&rsquo;t stop there: the DoS fixes (CVE-2025-55184, CVE-2025-67779, CVE-2026-23864) require 19.0.4+, 19.1.5+, or 19.2.4+. If you updated after React2Shell and then stopped paying attention, you may still be running a version vulnerable to the DoS variants.</p>
<p>It&rsquo;s a reactive patch, not a structural redesign.</p>
<p><strong>Note</strong>: <em>More on that in Where This Goes Next.</em></p>
<h3 id="5-the-taint-api">5. The Taint API</h3>
<p>React&rsquo;s <code>taintObjectReference</code> and <code>taintUniqueValue</code> functions register objects or strings with the runtime. If tainted data tries to pass through the Flight serializer, it throws an error. The idea is to prevent sensitive data &mdash; user records, API keys, tokens &mdash; from accidentally leaking into the client.</p>
<p>Here&rsquo;s how it looks in practice:</p>
<div class="break-out">
<pre><code class="language-typescript">import {
  experimental_taintObjectReference as taintObjectReference
} from "react"
import "server-only"

export async function getUserRecord(id: string) {
  const user = await db.users.findUnique({ where: { id } })
  taintObjectReference(
    "Do not pass the full user object to Client Components. " +
    "Select only the fields you need.",
    user
  )
  return user
}
</code></pre>
</div>
<p>If a Server Component passes the tainted <code>user</code> object as props to a Client Component, React throws it at serialization time with your custom error message. That&rsquo;s genuinely useful as a development-time guardrail.</p>
<p>The catch &mdash; and it&rsquo;s a significant one &mdash; is that taint tracks <em>object references</em>, not data content. Any derivation breaks the tracking:</p>
<pre><code class="language-typescript">const user = await getUserRecord(id)

// taint is lost. Spread creates a new object.
&lt;ClientProfile user={{ ...user }} /&gt;

// taint is lost. Individual properties aren't tracked.
&lt;ClientProfile token={user.apiToken} /&gt;

// taint is lost. Serialization round-trip creates new refs.
&lt;ClientProfile user={JSON.parse(JSON.stringify(user))} /&gt;

// taint fires. Same object reference.
&lt;ClientProfile user={user} /&gt;
</code></pre>
<p><code>taintUniqueValue</code> works on specific strings (like API keys), but it&rsquo;s also reference-based. If the same key value appears in a different variable, the taint doesn&rsquo;t follow.</p>
<p>Think of taint as a development guardrail, not a security boundary. It catches honest mistakes: a developer accidentally passing a full user object to the client. It won&rsquo;t stop an attacker who can influence what gets serialized, and it won&rsquo;t survive routine data transformations that your own code performs. It&rsquo;s a useful defense-in-depth layer, but shouldn&rsquo;t be your primary boundary.</p>
<h3 id="6-wafs">6. WAFs</h3>
<p>Web Application Firewalls can add a detection layer for known attack patterns. They can inspect POST requests carrying the <code>Next-Action</code> header, block payloads containing <code>constructor:constructor</code> or <code>__proto__</code> chains, and flag error responses containing <code>E{"digest"</code> patterns that indicate the server is leaking internal error details.</p>
<p>If you&rsquo;re running a WAF, here are specific patterns worth adding:</p>
<pre><code class="language-bash"># Block prototype pollution attempts in request bodies
Rule: body contains "__proto__" OR "constructor:constructor"
Action: BLOCK
Scope: POST requests with header "Next-Action"

# Flag potential Flight error leakage in responses
Rule: response body matches /E{"digest":"[^"]+"/
Action: LOG + ALERT
Scope: responses with Content-Type "text/x-component"

# Block excessively large Server Action payloads
Rule: Content-Length &gt; 1MB for POST with "Next-Action" header
Action: BLOCK (mitigates CVE-2026-23864 zipbomb vector)
</code></pre>
<p>But attackers know about WAF inspection buffers, and they&rsquo;re usually around 128KB. Prepend 130KB of padding before the malicious payload, and the WAF inspects the padding, finds nothing, and lets the request through. Chunked Transfer-Encoding tricks accomplish the same thing.</p>
<p>The failure mode is treating WAF coverage as a security boundary rather than a noise-reduction layer. WAFs catch automated scanners and low-effort attacks, and that has real value. But a motivated attacker will bypass them with padding or encoding tricks. The defenses that actually stop sophisticated attacks are the ones earlier in this list: validating input before it reaches your business logic, keeping sensitive code off the wire, and staying on patched versions.</p>
<h2 id="what-came-after-react2shell">What Came After React2Shell</h2>
<p>React2Shell wasn&rsquo;t the end of it. The security audits that followed the December 2025 disclosure shook out a series of related vulnerabilities in the same deserialization surface. None of them are as severe as the original RCE, but they&rsquo;re worth tracking because some of them required multiple rounds of patching.</p>
<table class="tablesaw break-out">
<thead>
<tr>
<th>CVE</th>
<th>CVSS</th>
<th>Type</th>
<th>Description</th>
<th>Fixed In</th>
</tr>
</thead>
<tbody>
<tr>
<td>CVE-2025-55184</td>
<td>7.5</td>
<td>DoS</td>
<td>Infinite recursion of nested Promises in Server Function deserialization. Hangs the Node.js event loop.</td>
<td>19.0.2, 19.1.3, 19.2.2</td>
</tr>
<tr>
<td>CVE-2025-67779</td>
<td>7.5</td>
<td>DoS</td>
<td>Incomplete fix for CVE-2025-55184. Same loop via edge cases the first patch missed.</td>
<td>19.0.4, 19.1.5, 19.2.4</td>
</tr>
<tr>
<td>CVE-2026-23864</td>
<td>7.5</td>
<td>DoS/OOM</td>
<td>Unbounded request body buffering and zipbomb-style decompression. Memory exhaustion. Disclosed Jan 2026.</td>
<td>19.0.4+, 19.1.5+, 19.2.4+</td>
</tr>
<tr>
<td>CVE-2025-55183</td>
<td>5.3</td>
<td>Info Disclosure</td>
<td>Crafted requests reflect Server Function source code when the function stringifies an argument.</td>
<td>19.0.1, 19.1.2, 19.2.1</td>
</tr>
<tr>
<td>CVE-2026-27978</td>
<td>5.3</td>
<td>CSRF Bypass</td>
<td>Next.js treated <code>Origin: null</code> (sandboxed iframes) as &ldquo;missing&rdquo; instead of &ldquo;cross-origin.&rdquo;</td>
<td>Next.js 16.1.7</td>
</tr>
</tbody>
</table>
<p>The DoS pair (CVE-2025-55184 and CVE-2025-67779) is a textbook example of why deserialization parsers are hard to patch correctly. The first fix shipped, researchers found edge cases it missed, and a second round was needed. CVE-2026-23864 added a third DoS vector through unbounded memory allocation rather than CPU exhaustion. (See the defenses section above for specific version checks.)</p>
<p>CVE-2025-55183 is the sneaky one. It&rsquo;s a source code exposure bug that triggers when a Server Function calls <code>JSON.stringify</code> (or any implicit stringification) on one of its arguments. Developers do this constantly for logging, debugging, or error reporting.</p>
<p>The attacker sends a crafted argument that, when stringified, causes the deserialization parser to reflect the function&rsquo;s own source code back in the response. Business logic, database queries, and any hardcoded secrets sitting in Server Action files become readable by anyone who can send an HTTP request.</p>
<p>CVE-2026-27978 is a different class of bug entirely. It&rsquo;s a CSRF bypass in Next.js&rsquo;s Server Action handling. Next.js validates that the <code>Origin</code> header matches the <code>Host</code> header to prevent cross-site request forgery. But when a request comes from a sandboxed <code>&lt;iframe&gt;</code>, the browser sends <code>Origin: null</code>.</p>
<p>The Next.js parser in <code>action-handler.ts</code> treated the string <code>'null'</code> as a missing origin rather than an explicit cross-origin indicator. So an attacker could embed a form inside a sandboxed iframe, submit it, and invoke Server Actions using the victim&rsquo;s authenticated session cookies. Fixed in Next.js 16.1.7.</p>
<h2 id="what-s-still-exposed-a-name-whats-still-exposed-a">What&rsquo;s Still Exposed </h2>
<p>The CVEs above have patches. But some of the risk is structural, baked into how Flight is designed to work.</p>
<h3 id="man-in-the-middle-mitm-on-the-flight-stream">Man-In-The-Middle (MITM) On The Flight Stream</h3>
<p>If an attacker can sit between server and client (CDN compromise, cache poisoning, rogue proxy), modifying the Flight stream in transit looks feasible. The format is plain text with a predictable structure.</p>
<p>Assuming stream control, an attacker could alter <code>$I</code> (Import) rows to redirect component loading to a different module in the webpack chunk map. They could inject <code>$F</code> (Server Reference) tags to embed hidden RPC triggers in the rendered UI. They could modify <code>D</code> (Data) rows to change component props, and if the target component uses <code>dangerouslySetInnerHTML</code>, that&rsquo;s a direct XSS vector.</p>
<p>Flight escapes <code>$</code> prefixes in user-supplied strings to prevent data from being interpreted as protocol instructions. But that only applies to data flowing through the serializer. A MITM attacker writes raw protocol directly into the stream. The escaping doesn&rsquo;t help.</p>
<h3 id="server-action-enumeration">Server Action Enumeration</h3>
<p>Server Action IDs are obfuscated hashes generated at build time. They look random. But <code>server-reference-manifest.json</code> maps every action ID to its source implementation. A public manifest hands an attacker a complete API map. This exposure usually stems from misconfigured hosting, an exposed <code>.next</code> directory, or path traversal.</p>
<p>Known action IDs expose Server Actions to standard IDOR and parameter tampering attacks. An attacker can forge direct requests with manipulated arguments. Developers often trust these inputs blindly because they originate from React&rsquo;s internal machinery. The architectural consequences of that misplaced trust will be the focus of my next piece.</p>
<h3 id="encrypted-closure-tampering">Encrypted Closure Tampering</h3>
<p>When a Server Action captures variables from its surrounding scope (closures), Next.js encrypts them before sending to the client. The key is in <code>NEXT_SERVER_ACTIONS_ENCRYPTION_KEY</code>, AES with a base64-encoded key (16, 24, or 32 bytes). <code>decryptActionBoundArgs</code> handles decryption on each invocation.</p>
<p>By default, this key regenerates every build. But multi-server setups often use a static key. If an attacker gets file read access (path traversal, SSRF), they extract the key, decrypt the closure state, modify it (changing a <code>userId</code>, a <code>role</code>, a query parameter), and re-encrypt. The server accepts the forged closure as legitimate.</p>
<h3 id="supply-chain-activation-via-module-ids">Supply Chain Activation via Module IDs</h3>
<p>I haven&rsquo;t demonstrated this end-to-end, but the theory is straightforward.</p>
<p>Flight references client components by module ID, something like <code>["360","static/chunks/app/page-7f3480.js"]</code>. The bundler assigns these IDs at build time based on the module graph. A compromised npm package sitting in <code>node_modules</code> as a transitive dependency gets bundled into a chunk but never loaded because no component references it. Inert.</p>
<p>But if an attacker injects <code>$I</code> import references into the Flight stream (via MITM, cache poisoning, or server-side injection), the parser should load that dormant module. There may be chunk-level validation I&rsquo;m not seeing. But if the module ID is valid and present in the manifest, I don&rsquo;t see what stops it. The attack doesn&rsquo;t require the package to be imported anywhere in your code. It just needs to exist in the bundle output.</p>
<h2 id="this-has-happened-before">This Has Happened Before</h2>
<p>React Flight isn&rsquo;t the first framework to invent a custom serialization format for server-client communication and then discover it&rsquo;s an attack surface. And it won&rsquo;t be the last.</p>
<p>Google Web Toolkit (GWT) used a custom RPC protocol to sync Java objects between browser and server. BishopFox demonstrated that attackers could manipulate the wire format to achieve arbitrary deserialization; GWT eventually disabled binary serialization entirely. It took years.</p>
<p>Java Server Faces (JSF) and ASP.NET both serialized <strong><code>ViewState</code></strong> to the client as a hidden form field. When cryptographic signing was weak or missing, attackers tampered with the serialized state and achieved remote code execution. Microsoft and Oracle patched it repeatedly. The underlying pattern kept resurfacing.</p>
<p>The pattern is always the same: a framework invents a custom wire format to move rich, stateful, sometimes executable data between server and client. The designers assume the server is the sole producer of that data and the client is a trusted consumer. Then someone demonstrates that the wire format can be manipulated in transit, or that the server can be tricked into deserializing attacker-controlled input. React Flight is the latest entry in this pattern. It is <strong>not an anomaly</strong>.</p>
<h2 id="where-this-goes-next">Where This Goes Next</h2>
<p>The React Flight protocol solves a genuinely hard problem: streaming interactive component trees from server to client in a way that enables progressive hydration, async data loading, and server-driven code splitting. It works. I don&rsquo;t want to lose sight of that.</p>
<p>But it works by serializing executable references, async state, module pointers, and RPC endpoints over a streaming text protocol, and then trusting the structure of that stream on both ends. The React team has patched the known gadgets. The <code>hasOwnProperty</code> fix is correct. The DoS fixes are in place. The source code exposure bug is closed.</p>
<p>I think exposing arbitrary property traversal and executable Thenable reconstruction through a network-facing protocol was a <strong>design mistake</strong>. <code>$:</code>, <code>$@</code>, and <code>$B</code> are powerful internal primitives that were reachable through a parser that didn&rsquo;t validate ownership of the properties it traversed. One check was missing, and the result was CVSS 10.0.</p>
<blockquote class="pull-quote">
<p>As more frameworks adopt server-driven UI patterns, the industry is going to need stronger primitives than &ldquo;the server is trusted&rdquo;: cryptographic validation of serialized payloads, signed component trees, and content integrity checks on the Flight stream itself.</p>
</blockquote>
<p>Hoping the parser handles every edge case hasn&rsquo;t worked historically, and I don&rsquo;t see why it would start working now.</p>
<p>The code is in <code>react-client/src/ReactFlightClient.js</code>. If you ship Server Components, read it. Know what your framework is trusting on your behalf.</p>
<div class="signature"><img decoding="async" src="https://thenokiablog.com/wp-content/uploads/2026/07/weaponizing-and-defending-the-react-flight-protocol-deserialization-sinks-in-rscs.png" alt="Smashing Editorial" width="35" height="46" loading="lazy"><br>
<span>(gg, yk)</span></div>
<p>The post <a href="https://thenokiablog.com/weaponizing-and-defending-the-react-flight-protocol-deserialization-sinks-in-rscs/">Weaponizing And Defending The React Flight Protocol: Deserialization Sinks In RSCs</a> appeared first on <a href="https://thenokiablog.com">The NOKIA Blog</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>When It Makes Sense To “Block” The Main Thread</title>
		<link>https://thenokiablog.com/when-it-makes-sense-to-block-the-main-thread/</link>
		
		<dc:creator><![CDATA[Mister Nokia]]></dc:creator>
		<pubDate>Fri, 17 Jul 2026 08:00:00 +0000</pubDate>
				<category><![CDATA[Design]]></category>
		<category><![CDATA[Compute]]></category>
		<category><![CDATA[CSS]]></category>
		<category><![CDATA[data]]></category>
		<category><![CDATA[Developer]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[Messaging]]></category>
		<guid isPermaLink="false">https://thenokiablog.com/when-it-makes-sense-to-block-the-main-thread/</guid>

					<description><![CDATA[<p>The common rule of thumb is to never &#8220;block&#8221; the browser&#8217;s main thread when running JavaScript...</p>
<p>The post <a href="https://thenokiablog.com/when-it-makes-sense-to-block-the-main-thread/">When It Makes Sense To “Block” The Main Thread</a> appeared first on <a href="https://thenokiablog.com">The NOKIA Blog</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p><section aria-label="Quick summary" class="article__summary"><span id="article__start" class="summary__heading" aria-hidden="true"></span>The common rule of thumb is to never &ldquo;block&rdquo; the browser&rsquo;s main thread when running JavaScript tasks. But is this a hard rule? Victor Ayomipo describes a use case he encountered involving a screenshot extension where he made an exception to the rule and decided that blocking the main thread was absolutely the right thing to do.</section>
</p>
<p>We&rsquo;ve all heard of the sacred rule in modern web development, the rule never to be broken. The rule of <em>&ldquo;Never block the main thread.&rdquo;</em></p>
<p>You almost can&rsquo;t miss it as a web developer; it&rsquo;s in almost every performance guide, and to be fair, it is good advice. We all know the browser&rsquo;s main thread is <strong>single-threaded</strong>, meaning it can only do one thing at a time.</p>
<p>Plus, as we know, the main thread isn&rsquo;t ours alone; we share it with the browser&rsquo;s rendering engine, input handlers, and other critical tasks. As a result, the less time we hold onto the main thread, the more responsive an app feels. That leads us to share tasks with background workers as we&rsquo;ve convinced ourselves there should be a hard line between the UI and any computation, and that line shouldn&rsquo;t be crossed.</p>
<div class="internal-linking-related-contents"><a href="https://thenokiablog.com/rose-gold-design/" class="template-2"><span class="cta">Read more</span><span class="postTitle">Rose gold design</span></a></div><p>And that is what a &ldquo;recommended&rdquo; architecture looks like.</p>
<p>But I dare say that *sometimes*<em>,</em> moving the data to a worker is slower than just letting the main thread do the work.</p>
<p>I found this out a few months ago while building a Chrome extension with screenshotting features called Fastary. I kept finding a latency of about 2 to 3 seconds in all my testing, even after using an Offscreen Document (a background process in Chrome extensions) to handle the canvas operations. A screenshot task should feel instant without lag, after all.</p>
<p>It is quite ironic that by reflex, we move work away from the main thread to avoid freezing the UI, but sometimes the act of moving that work (e.g., serializing, copying, and deserializing) can also freeze the UI. And <em>sometimes</em> the recommended approach of letting the background do the work can be slower than just doing the work on the main thread.</p>
<div class="internal-linking-related-contents"><a href="https://thenokiablog.com/passion-work/" class="template-2"><span class="cta">Read more</span><span class="postTitle">Passion &amp; work</span></a></div><p>Let&rsquo;s talk about that.</p>
<div data-audience="non-subscriber" data-remove="true" class="feature-panel-container"><img decoding="async" loading="lazy" class="feature-panel-image-img" src="https://thenokiablog.com/wp-content/uploads/2026/07/when-it-makes-sense-to-block-the-main-thread.png" alt="Feature Panel" width="481" height="698"></div>


<h2 id="the-architecture-of-browser-context-isolation">The Architecture Of Browser Context Isolation</h2>
<p>To put things in perspective, let&rsquo;s understand why we isolate browser contexts and how they communicate with each other, with emphasis on the <em>communication</em> part.</p>
<p>A browser is more than a single environment. Different environments are running at the same time, each having its own memory space, what it can access, and rules:</p>
<ul>
<li>The <strong>main thread</strong> is what we are most familiar with; this is where JavaScript logic runs, where the DOM lives, where styles get rendered, and where users interact.</li>
<li>The <strong>Web Workers</strong> are separate threads that can also execute JavaScript without DOM access. We mostly use this for heavy data tasks.</li>
<li>The <strong>Service Workers</strong> are network-related proxies in charge of intercepting network requests and can even run when the page is closed.</li>
<li>And then there are <strong>Chrome extension contexts</strong>, where we have background service workers, content scripts, and Offscreen Documents (the relevant ones for this article).</li>
</ul>
<p>Each one of these is isolated from the others. A web worker or background script lives in a different memory space from the main thread. They cannot just reach and read each other&rsquo;s variables or logic, and this is known as the <strong>&ldquo;shared-nothing&rdquo; architecture</strong>.</p>
<div class="internal-linking-related-contents"><a href="https://thenokiablog.com/the-joy-of-art/" class="template-2"><span class="cta">Read more</span><span class="postTitle">The joy of art</span></a></div><p>How do these isolated environments communicate? They explicitly message each other back and forth using APIs, like <code>postMessage()</code>.</p>
<h3 id="the-structured-clone-algorithm">The Structured Clone Algorithm</h3>
<p><code>postMessage()</code> tells the browser to take a piece of data and deliver it to the context that requested it. But to do this, the browser relies on the Structured Clone Algorithm (SCA).</p>
<p>You&rsquo;re probably familiar with <code>JSON.stringify()</code>. SCA is similar, but much stronger and smarter. In its simplest form, SCA is a deep, recursive copy operation, i.e., cloning. It walks through the entire data structure it is given, clones every single value, serializes it into a transportable format, ships those bytes to the target contexts, and then reconstructs the original object on the receiving side.</p>
<p>SCA is fast, or maybe fast-<em>ish</em>&hellip; For a small regular config object like <code>{theme: "dark"}</code>, it is imperceptible; you don&rsquo;t even notice it. The story changes, however, when dealing with heavy data because the SCA is a synchronous blocking <code>O(n)</code> operation, i.e., the cost increases linearly with the size of your data.</p>
<p>Let&rsquo;s put that into perspective. A user clicks a button, and internally, an 8MB image payload is sent to a background worker for processing. When you call <code>postMessage()</code>, the main thread must immediately stop what it is doing to run this serialization and copying process.</p>
<blockquote><p>So, if the time it takes to pack, ship, unpack the data, and go back to the start is longer than the time to just process the data on the main thread, why not do that instead?</p></blockquote>
<h2 id="what-about-transferable-objects">What About Transferable Objects?</h2>
<p>I&rsquo;m sure some of you are already thinking, <em>&ldquo;Why not just use Transferable objects?&rdquo;</em> And that is a valid point. Let&rsquo;s talk about that.</p>
<p>Developers who really pursue ultra-high-performance web apps usually use Transferable objects (e.g., <code>ArrayBuffer</code>, <code>ImageBitmap</code>, or <code>MessagePort</code>) to bypass the Structured Clone Algorithm. This is because when you transfer an object, you&rsquo;re not making a copy (like SCM). Instead, the browser switches ownership of the data from one context to another.</p>
<p>The browser performs a hand-off whereby the sending context loses access to the data instantly, and the receiving context takes full control. It is actually insanely fast. According to Chrome Developers&rsquo; benchmark, transferring a massive 32MB <code>ArrayBuffer</code> can take under 7ms, compared to about 300ms when cloning with SCM. That&rsquo;s a 43x speed boost.</p>
<figure><img decoding="async" loading="lazy" width="800" height="315" src="https://thenokiablog.com/wp-content/uploads/2026/07/when-it-makes-sense-to-block-the-main-thread-1.png" alt="Structured cloning vs. Transferable Objects by Chrome Developers"><figcaption class="op-vertical-bottom">Structured cloning vs. Transferable Objects by Chrome Developers. (Image source: Chrome for Developers) (Large preview)</figcaption></figure>
<p>But like all good things, there are downsides. To name a few:</p>
<ul>
<li><strong>You lose it once you send it.</strong><br>If the UI still needs that data (like to show an image preview), you can&rsquo;t access it anymore.</li>
<li><strong>Not all data is transferable.</strong><br>A plain JS object is not. A Blob is not. Even a Base64 string is not.</li>
<li><strong>API limitations.</strong><br>In the context of browser extensions, Chrome&rsquo;s internal messaging (<code>chrome.runtime.sendMessage</code>) traditionally forces everything through JSON serialization.</li>
</ul>
<p>So, as far as my screenshot extension went, Transferable objects were not an option.</p>
<h2 id="why-we-isolate-contexts-anyway">Why We Isolate Contexts Anyway</h2>
<p>Why do we even bother isolating contexts at all? Why not just leave it all to the main thread?</p>
<p>Offloading long-running CPU tasks to a background thread is absolutely the right thing to do. The browser needs to paint a new frame <strong>every 16.6ms</strong> to keep things fluid; that means any task that takes &gt;50ms is generally considered &ldquo;long&rdquo;. Offloading to the background is absolutely the right thing to do.</p>
<p>The issue, however, is that we&rsquo;ve turned this <em>&ldquo;never block the main thread&rdquo;</em> into an absolute rule, without asking <em>is this task expensive to process or expensive to move?</em></p>
<blockquote class="pull-quote">
<p>I have come to realize now that the rule is less &ldquo;never block the main thread&rdquo; than &ldquo;never block the main thread for too long.&rdquo;</p>
</blockquote>
<h2 id="when-the-right-architecture-is-the-wrong-architecture">When The Right Architecture Is The Wrong Architecture</h2>
<p>My goal with the Fastary extension was to make it feel like a native app, running as smoothly and instantly as you would expect a native app to.</p>
<p>As you already know, I took the recommended approach to use the Offscreen Document to handle DOM work in the background. But to my surprise, that took a different turn.</p>
<p>The Offscreen Document API is a clear winner. You create a hidden, undisplayed document that runs entirely in the background. It has a DOM and supports Canvas. For example, if I want to crop a screenshot, stitch multiple screenshots together, perform heavy image manipulation, or add a watermark, Offscreen Document was made for that.</p>
<p>Turns out that was not the best approach. This was my architecture:</p>
<ol>
<li>The background Service Worker captures a screenshot with <code>chrome.tabs.captureVisibleTab()</code>, which returns a Base64-encoded data URL string.</li>
<li>The background Service Worker uses <code>chrome.runtime.sendMessage()</code> to ship this image payload to the Offscreen Document.</li>
<li>The Offscreen Document receives the image, loads it into an <code>&lt;img&gt;</code> element, then draws it onto a canvas before it applies the user&rsquo;s crop coordinates, encodes the result, and sends the processed image back to the background worker.</li>
</ol>
<p>But when I tested it, the screenshot didn&rsquo;t feel instant. As I said earlier, there was a consistent 2&ndash;3 second lag.</p>
<p>I figured out that when <code>captureVisibleTab()</code> takes a screenshot, it returns a Base64 URL string, and on a standard 1080p screen, that string could be approximately 1MB or more, depending on how detailed the image is. It gets even more interesting on modern Retina displays (e.g., MacBooks) as they tend to automatically double the image&rsquo;s size by default.</p>
<p>Keep in mind that since the image payload could be doubled and extension messaging relies on JSON serialization (as of this writing), we potentially deal with <strong>massive synchronous communication</strong> that costs an entire round trip.</p>
<p>The image string data is JSON-serialized at least twice: once when going into the Offscreen Document and once coming back out with the processed results to the background worker. The actual image processing (cropping) done inside the Offscreen Document was fast, no doubt, but I can&rsquo;t say the same about the transfer overhead.</p>
<h3 id="the-retina-high-dpi-problem">The Retina High-DPI Problem</h3>
<p>As if the latency itself wasn&rsquo;t enough, I noticed a rather subtle bug &mdash; which, now that I think of it, was more of my ignorance. After a screenshot was taken, the crop result was completely off in a way that either weirdly scaled the image or resulted in incorrect coordinates.</p>
<p>It turns out that when a user selects a region to crop, the content script gets the box coordinates using <code>getBoundingClientRect()</code>, which is measured in <strong>CSS pixels</strong>; this is what the DOM uses. But when the screenshot is captured natively in Chrome, the browser doesn&rsquo;t crop it automatically; it instead uses the <strong>physical hardware pixels</strong> to get the full screen capture. And the browser uses <code>devicePixelRatio</code> (DPR) to know how many physical pixels should represent one CSS pixel. Basically, if a user on a Retinal display (DPR = 2) highlights an area of 400&times;300 CSS pixels, the actual captured image area is 800&times;600 physical pixels.</p>
<blockquote><p><strong>Note:</strong> One CSS pixel is equal to 1 physical pixel (DPR of 1) on a standard monitor. On a Mac Retina display or a modern 4K monitor, however, the DPR is usually 2 or 3.</p></blockquote>
<p>For an accurate crop, I needed to apply these two different measurement systems with the right DPR, i.e., scale the crop coordinates by the DPR. But remember, Offscreen Documents have no physical display. Processing any image would have a default DPR equal to 1. To fix this, I would have to capture the exact <code>devicePixelRatio</code> from the active tab, serialize it, pass it alongside the image payload, and manually do the scaling math inside the Offscreen Document. The complexity starts to compound.</p>
<p><em>What if I broke the golden rule and did the work on the main thread instead?</em></p>
<h2 id="working-on-the-main-thread">Working On The Main Thread</h2>
<p>Some developers will argue that UI tasks are the only things that should run on the main thread, but I don&rsquo;t fully agree with that. Personally, I believe that user explicitly-invoked actions that need immediate results can sometimes get a solid pass to run on the main thread, provided the work is incredibly <em>fast</em> (e.g., 1s).</p>
<p>That&rsquo;s what I did: scrap out the Offscreen Document and reengineer the logic. Instead of:</p>
<div class="break-out">
<pre><code class="language-markdown">Background &rarr; [serialize] &rarr; Offscreen Document &rarr; [serialize] &rarr; Background &rarr; Content Script</code></pre>
</div>
<p>&hellip;I decided to run the whole image processing in the active tab:</p>
<ol>
<li>The background Service Worker captures the screen and gets the Base64 string (same as before).</li>
<li>The background sends the payload directly to the content script in the active tab using <code>chrome.scripting.executeScript()</code>.</li>
<li>The content script (running on the main thread) receives the payload, draws it to a canvas, performs the crop using the correct DPR value, and copies the result to the clipboard.</li>
</ol>
<div class="break-out">
<pre><code class="language-javascript">// Background Script
const screenshotUrl = await chrome.tabs.captureVisibleTab(undefined, { format: "png" });

// Inject the processing function into the active tab as a content script
await chrome.scripting.executeScript({
  target: { tabId: activeTab.id },
  func: processAndCopyImage,
  args: [{ base64Image: screenshotUrl, cropData: userSelection }]
});
</code></pre>
</div>
<p>This approach completely clears out multiple context hops and round trips that JSON serialization requires. The only cross-context transfer involves sending the data URL from the background to the content script.</p>
<p>The Retina DPI issue essentially solved itself, as the content script runs directly inside the real, active browser tab because it knows the monitor&rsquo;s real <code>devicePixelRatio</code>.</p>
<p>But there&rsquo;s an elephant in the room that you may have noticed.</p>
<p>Sure, the image is now processed on the main thread, and the background manipulates the canvas in the active tab. I could technically be blocking the main thread. That&rsquo;s where I amended the &ldquo;no blocking the main thread&rdquo; rule to &ldquo;no blocking the main thread <em>for too long</em>.&rdquo; In this specific case, at least, blocking the main thread for a task the user requests for approximately one second is justifiable. It works conversely as well: maybe <strong>don&rsquo;t isolate processes if the data transfer cost is greater than the processing cost</strong>.</p>
<h2 id="conclusion-when-to-isolate-and-when-not-to">Conclusion: When To Isolate And When Not To</h2>
<p>I&rsquo;ve boiled it down to a mental model that depends on whether the task is:</p>
<h3 id="1-compute-heavy-tasks-cpu-bound">1. Compute-Heavy Tasks (CPU-Bound)</h3>
<p>These are tasks where the primary cost is computation and not the size of the data itself. These are tasks where most of the time is spent on doing calculations or heavy transformations, e.g., image compression, audio profiling, physics simulation, etc.</p>
<p>The transfer cost for these tasks is minuscule compared to the actual work.</p>
<h3 id="2-data-heavy-tasks-data-bound">2. Data-Heavy Tasks (Data-Bound)</h3>
<p>These tasks are the exact opposite. These tasks are only expensive because of the size. The processing time is almost insignificant, but the data is expensive to transport, e.g., image cropping, filtering an array, shallow copy, etc.</p>
<p>In my specific case, offloading the task to the background falls mostly into <strong>negative-sum efficiency</strong>. If we are talking about moving megabytes of data to perform a 50ms operation, there is no benefit to offloading it to the background.</p>
<p>Perhaps we can think of it like this:</p>
<pre><code class="language-markdown">Total Time = Serialization Cost
  + Transit
  + Background Processing Time
  + Deserialization Cost
</code></pre>
<p>Looking at this, if the <strong>&ldquo;background processing time&rdquo;</strong> is the most dominant task in your operation, then isolation is the clear winner. But if serialization, plus deserialization, plus transit exceeds that cost, then there&rsquo;s no need to isolate things.</p>
<p>And if you can&rsquo;t figure out if the task is CPU-heavy or data-heavy, it certainly doesn&rsquo;t hurt to measure it, for example, using performance.mark() and performance.measure() around <code>postMessage</code> calls to profile the transfer cost.</p>
<div class="signature"><img decoding="async" src="https://thenokiablog.com/wp-content/uploads/2026/07/when-it-makes-sense-to-block-the-main-thread-5.png" alt="Smashing Editorial" width="35" height="46" loading="lazy"><br>
<span>(gg, yk)</span></div>
<p>The post <a href="https://thenokiablog.com/when-it-makes-sense-to-block-the-main-thread/">When It Makes Sense To “Block” The Main Thread</a> appeared first on <a href="https://thenokiablog.com">The NOKIA Blog</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Meet Kirki: WordPress’s First Visual Builder With An Infinite Canvas</title>
		<link>https://thenokiablog.com/meet-kirki-wordpresss-first-visual-builder-with-an-infinite-canvas/</link>
		
		<dc:creator><![CDATA[Mister Nokia]]></dc:creator>
		<pubDate>Tue, 07 Jul 2026 10:00:00 +0000</pubDate>
				<category><![CDATA[Design]]></category>
		<category><![CDATA[Analytics]]></category>
		<category><![CDATA[Branding]]></category>
		<category><![CDATA[Business]]></category>
		<category><![CDATA[Color]]></category>
		<category><![CDATA[CSS]]></category>
		<category><![CDATA[data]]></category>
		<category><![CDATA[Design Trends]]></category>
		<category><![CDATA[Developer]]></category>
		<category><![CDATA[Figma]]></category>
		<category><![CDATA[Fonts]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[Launch]]></category>
		<category><![CDATA[SEO]]></category>
		<category><![CDATA[UI Components]]></category>
		<category><![CDATA[Web Design]]></category>
		<category><![CDATA[WordPress]]></category>
		<guid isPermaLink="false">https://thenokiablog.com/meet-kirki-wordpresss-first-visual-builder-with-an-infinite-canvas/</guid>

					<description><![CDATA[<p>We have been building websites inside boxes for years on WordPress. Let&#8217;s take a closer look...</p>
<p>The post <a href="https://thenokiablog.com/meet-kirki-wordpresss-first-visual-builder-with-an-infinite-canvas/">Meet Kirki: WordPress’s First Visual Builder With An Infinite Canvas</a> appeared first on <a href="https://thenokiablog.com">The NOKIA Blog</a>.</p>
]]></description>
										<content:encoded><![CDATA[<div class="c-garfield-summary">
<section aria-label="Quick summary" class="article__summary"><span id="article__start" class="summary__heading" aria-hidden="true"></span>We have been building websites inside boxes for years on WordPress. Let&rsquo;s take a closer look at Kirki, the first freeform visual builder with an infinite canvas, and explore how it redefines the experience with cleaner performance, full design freedom, and zero plugin dependency.</section>
</div>
<p>For years, WordPress users have relied on traditional page builders to create websites without writing code. While these builders made web design more accessible, many still come with familiar compromises &mdash; rigid layouts, reliance on multiple third-party plugins, bloated code, and performance trade-offs that can slow down your site.</p>
<p>Kirki takes a different approach. Instead of building on the conventions of older page builders, it reimagines the website creation experience with a freeform infinite canvas, an integrated CMS, and a comprehensive set of built-in features. The result is a streamlined workflow that gives you greater creative freedom while producing cleaner, faster websites.</p>
<p>Whether you&rsquo;re a designer seeking pixel-perfect control, a developer looking for cleaner output, or a business owner who simply wants to build a professional website without unnecessary complexity, Kirki aims to remove many of the limitations that have long been associated with WordPress page builders.</p>
<p>In this review, we&rsquo;ll compare Kirki with traditional WordPress builders across the factors that matter most when choosing a website builder, including:</p>
<ul>
<li>Pricing and overall value,</li>
<li>Impact on website performance,</li>
<li>Ease of use versus design flexibility,</li>
<li>Built-in features and functionality,</li>
<li>Theme compatibility and layout customization.</li>
</ul>
<div class="internal-linking-related-contents"><a href="https://thenokiablog.com/rose-gold-design/" class="template-2"><span class="cta">Read more</span><span class="postTitle">Rose gold design</span></a></div><p>By the end, you&rsquo;ll have a clear understanding of where Kirki stands and whether it&rsquo;s the right choice for your next WordPress project.</p>
<h2 id="a-closer-looks-at-kirki">A Closer Looks At Kirki</h2>
<p>Kirki is a no-code visual website builder for WordPress, designed to bridge the gap where other page builders fall short.</p>
<figure class="break-out article__image"><img decoding="async" loading="lazy" width="800" height="419" src="https://thenokiablog.com/wp-content/uploads/2026/08/meet-kirki-wordpresss-first-visual-builder-with-an-infinite-canvas.jpg" alt="Kirki banner"><figcaption class="op-vertical-bottom">(Large preview)</figcaption></figure>
<p>Unlike other page builders, Kirki is an all-in-one solution that aims to provide everything you need to build websites without any third-party dependencies, shifting from the norm in WordPress!</p>
<p>And the best part? It&rsquo;s all included in your subscription, so you won&rsquo;t be hit with surprise upgrades.</p>
<h3 id="the-most-feature-packed-free-wordpress-builder">The Most Feature-Packed Free WordPress Builder</h3>
<div class="internal-linking-related-contents"><a href="https://thenokiablog.com/passion-work/" class="template-2"><span class="cta">Read more</span><span class="postTitle">Passion &amp; work</span></a></div><p>Before anything else, <strong>Kirki has a free version, and it&rsquo;s genuinely powerful.</strong></p>
<p>You can download Kirki for free directly from WordPress.org and start building right away. The free version is not a watered-down teaser. It&rsquo;s a heavily feature-packed builder that lets you design modern websites on an infinite canvas without spending a cent.</p>
<h3 id="scale-without-limits-with-the-pro-plan">Scale Without Limits With the Pro Plan</h3>
<p>For those who want to unlock the full Kirki experience, the Pro plans are surprisingly affordable for the value they deliver.</p>
<figure class="break-out article__image"><img decoding="async" loading="lazy" width="800" height="512" src="https://thenokiablog.com/wp-content/uploads/2026/08/meet-kirki-wordpresss-first-visual-builder-with-an-infinite-canvas-1.jpg" alt="Screenshot displaying Kirki&rsquo;s pricing plans and features."><figcaption class="op-vertical-bottom">Overview of Kirki&rsquo;s affordable and transparent pricing plans. (Large preview)</figcaption></figure>
<p>The <strong>Starter plan</strong> is just <strong>$59/year</strong> for one site and includes all premium features. Compare that with Elementor Pro&rsquo;s Essential plan, which starts at $60/year and still keeps several essentials behind paywalls. With Kirki, what you see is what you get, everything included from day one.</p>
<div class="internal-linking-related-contents"><a href="https://thenokiablog.com/the-joy-of-art/" class="template-2"><span class="cta">Read more</span><span class="postTitle">The joy of art</span></a></div><p>Kirki also offers a <strong>Lifetime plan</strong> for a one-time payment of <strong>$499</strong>, giving you unlimited use forever. No renewals, no upcharges, no surprises.</p>
<p>While most page builders upsell critical features or require multiple add-ons to function properly, Kirki keeps it simple. One platform, all features, no hidden costs. Dynamic content, pop-up builder, form builder, submission manager, the entire growing template library &mdash; all included from the start across every plan.</p>
<p>Explore Kirki pricing.</p>
<h2 id="website-performance-comparison">Website Performance Comparison</h2>
<p>Performance directly impacts user experience, SEO, and conversion rates. So, to get a clear picture of how different page builders impact performance, we put Kirki and Elementor to the test under identical conditions to see how each builder stacks up.</p>
<p>We installed both on a clean WordPress setup using the default Twenty Twenty-Five theme to ensure a fair comparison. Then, we created identical layouts using comparable design elements and ran Lighthouse performance audits to measure load time, responsiveness, and Core Web Vitals.</p>
<p><strong>Test Conditions:</strong></p>
<ul>
<li>Clean WordPress installation,</li>
<li>Same theme: Twenty Twenty-Five,</li>
<li>Same layout structure and design elements,</li>
<li>Lighthouse is used for performance scoring.</li>
</ul>
<p><strong>Sample Layout:</strong></p>
<figure class="break-out article__image"><img decoding="async" loading="lazy" width="800" height="512" src="https://thenokiablog.com/wp-content/uploads/2026/08/meet-kirki-wordpresss-first-visual-builder-with-an-infinite-canvas-2.jpg" alt="Image of a website layout built for performance testing comparison."><figcaption class="op-vertical-bottom">Sample website layout used for benchmarking performance tests. (Large preview)</figcaption></figure>
<p><strong>Kirki&rsquo;s Performance:</strong></p>
<figure class="break-out article__image"><img decoding="async" loading="lazy" width="800" height="512" src="https://thenokiablog.com/wp-content/uploads/2026/08/meet-kirki-wordpresss-first-visual-builder-with-an-infinite-canvas-3.jpg" alt="Performance test scores showing Kirki&rsquo;s website speed and responsiveness."><figcaption class="op-vertical-bottom">Performance results showing Kirki&rsquo;s fast loading times and metrics. (Large preview)</figcaption></figure>
<p><strong>Elementor&rsquo;s Performance:</strong></p>
<figure class="break-out article__image"><img decoding="async" loading="lazy" width="800" height="512" src="https://thenokiablog.com/wp-content/uploads/2026/08/meet-kirki-wordpresss-first-visual-builder-with-an-infinite-canvas-4.jpg" alt="Performance test scores showing Elementor&rsquo;s website speed and responsiveness."><figcaption class="op-vertical-bottom">Performance results illustrating Elementor&rsquo;s load times and metrics. (Large preview)</figcaption></figure>
<p><strong>Kirki&rsquo;s Code Output:</strong></p>
<figure class="break-out article__image"><img decoding="async" loading="lazy" width="800" height="445" src="https://thenokiablog.com/wp-content/uploads/2026/08/meet-kirki-wordpresss-first-visual-builder-with-an-infinite-canvas.png" alt="Screenshot of Kirki&rsquo;s clean semantic HTML code structure."><figcaption class="op-vertical-bottom">Clean and minimal code output generated by Kirki for optimized websites. (Large preview)</figcaption></figure>
<p><strong>Elementor&rsquo;s Code Output:</strong></p>
<figure class="break-out article__image"><img decoding="async" loading="lazy" width="800" height="512" src="https://thenokiablog.com/wp-content/uploads/2026/08/meet-kirki-wordpresss-first-visual-builder-with-an-infinite-canvas-1.png" alt="Screenshot of Elementor&rsquo;s complex and heavily nested HTML code."><figcaption class="op-vertical-bottom">Nested and bulky code output produced by Elementor, affecting performance. (Large preview)</figcaption></figure>
<p>The difference was immediately clear. Kirki generated a much cleaner DOM with significantly fewer <code>&lt;div&gt;</code>s and no unnecessary wrappers, resulting in faster load times and higher scores across all boards.</p>
<p>Elementor, on the other hand, added heavily nested markup and extra scripts, even on this simple layout, which dragged down its performance.</p>
<p>If clean code, fast loading, and technical efficiency are priorities for you, Kirki clearly comes out ahead.</p>
<h2 id="exploring-the-features">Exploring The Features</h2>
<p>Now that we&rsquo;ve seen how Kirki outperforms the competition and does so at a highly competitive price, let&rsquo;s dive into the features to see what makes it such a powerful all-in-one builder.</p>
<h3 id="freeform-infinite-canvas-for-true-design-freedom">Freeform Infinite Canvas For True Design Freedom</h3>
<p>What makes Kirki different from the existing page builders is its infinite canvas.</p>
<p>With Kirki, you finally get the layout flexibility modern design demands, and no longer need to place elements into rigid structures.</p>
<figure class="break-out article__image"><img decoding="async" loading="lazy" width="800" height="512" src="https://thenokiablog.com/wp-content/uploads/2026/08/meet-kirki-wordpresss-first-visual-builder-with-an-infinite-canvas-5.jpg" alt="Interface screenshot of Kirki&rsquo;s drag-and-drop freeform visual canvas."><figcaption class="op-vertical-bottom">Kirki&rsquo;s flexible freeform canvas enables pixel-perfect design freedom. (Large preview)</figcaption></figure>
<p>Design on an <strong>infinite canvas</strong> where you can pan freely, zoom in and out, place elements exactly where you want, overlap sections, layer backgrounds, and build complex interactions, all visually.</p>
<p>Every element&rsquo;s layout behavior is editable on canvas, giving you pixel-level control without touching code.</p>
<p>The editor supports both light and dark modes for a more comfortable, focused workspace.</p>
<p>If you&rsquo;ve used Figma or Webflow, you&rsquo;ll feel instantly at home. If you haven&rsquo;t, this is the most natural way to design websites you&rsquo;ve ever tried.</p>
<h3 id="concurrent-editing-across-all-responsive-views">Concurrent Editing Across All Responsive Views</h3>
<p>With Kirki&rsquo;s infinite canvas, all your responsive views, Desktop, Tablet, Landscape, and Mobile, are visible side by side simultaneously. You don&rsquo;t switch modes. You work across all of them at once, in real time, on the same canvas.</p>
<p>This means you can spot a layout issue on mobile while designing the desktop version, fix it instantly, and move on without ever breaking your flow. No back and forth.</p>
<figure class="break-out article__image"><img decoding="async" loading="lazy" width="800" height="512" src="https://thenokiablog.com/wp-content/uploads/2026/08/meet-kirki-wordpresss-first-visual-builder-with-an-infinite-canvas-6.jpg" alt="Interface screenshot of Kirki&rsquo;s drag-and-drop freeform visual canvas."><figcaption class="op-vertical-bottom">Concurrent editing across all responsive views on an infinite canvas. (Large preview)</figcaption></figure>
<p>And because Kirki uses a cascading system, changes made at larger breakpoints automatically flow down to smaller ones, so you&rsquo;re never starting from scratch at every screen size. You only step in where you need to, making adjustments where the design requires it and letting the rest handle itself.</p>
<h3 id="instant-figma-to-kirki-handoff">Instant Figma to Kirki Handoff</h3>
<p>Talking about Figma, if you have a design ready in Figma, you can instantly import it into Kirki to create a functional website with no need to rebuild from scratch.</p>
<figure class="break-out article__image"><img decoding="async" loading="lazy" width="800" height="512" src="https://thenokiablog.com/wp-content/uploads/2026/08/meet-kirki-wordpresss-first-visual-builder-with-an-infinite-canvas-7.jpg" alt="Screencast showing Figma design being imported into Kirki builder."><figcaption class="op-vertical-bottom">Seamless import of Figma designs directly into Kirki for fast development. (Large preview)</figcaption></figure>
<p>Your imported design comes in fully responsive by default, adapting to all screen sizes, including any custom breakpoints you define.</p>
<p>And it supports unlimited breakpoints, too. You can define layout behavior exactly how you want it, and styles will cascade intelligently across smaller screens.</p>
<h3 id="no-third-party-plugins-needed-for-dynamic-content">No Third-Party Plugins Needed for Dynamic Content</h3>
<p>In traditional WordPress, handling dynamic content means installing the ACF or other third-party plugins.</p>
<figure class="break-out article__image"><img decoding="async" loading="lazy" width="800" height="512" src="https://thenokiablog.com/wp-content/uploads/2026/08/meet-kirki-wordpresss-first-visual-builder-with-an-infinite-canvas-8.jpg" alt="Image showing Kirki&rsquo;s dynamic content management capabilities."><figcaption class="op-vertical-bottom">Kirki&rsquo;s native dynamic content management without third-party plugins. (Large preview)</figcaption></figure>
<p>But with Kirki, all of that is natively integrated. It comes with a powerful Dynamic Content Manager that lets you:</p>
<ul>
<li>Create custom content types and fields.</li>
<li>Use reference and multi-reference relationships.</li>
<li>Build dynamic templates visually.</li>
<li>Add dynamic SEO to template pages.</li>
<li>Apply advanced filtering to Collection elements.</li>
</ul>
<p>All without writing a single line of code or relying on external plugins.</p>
<h3 id="reusable-styling-with-class-based-editing">Reusable Styling With Class-Based Editing</h3>
<p>Kirki also has an efficient way to manage design at scale without repetitive work.</p>
<figure class="break-out article__image"><img decoding="async" loading="lazy" width="800" height="512" src="https://thenokiablog.com/wp-content/uploads/2026/08/meet-kirki-wordpresss-first-visual-builder-with-an-infinite-canvas-9.jpg" alt="Screenshot of Kirki&rsquo;s CSS class management panel."><figcaption class="op-vertical-bottom">Efficient class-based styling system for reusable and scalable designs. (Large preview)</figcaption></figure>
<p>It uses a class-based styling system that brings structure and scalability to your design process. When you style an element, those styles are automatically saved as reusable CSS classes.</p>
<p>Here&rsquo;s what that means for you:</p>
<ul>
<li>You can <strong>create global classes</strong> for common components like buttons, cards, or headings.</li>
<li><strong>Reuse those styles</strong> across pages and projects with consistency.</li>
<li>Update a class once, and every instance updates instantly.</li>
<li>You can also <strong>create subclasses</strong> to make slight variations, like secondary buttons, while still inheriting styles from the parent.</li>
</ul>
<h3 id="css-variables-for-global-styling">CSS Variables for Global Styling</h3>
<p>Kirki takes styling even further with Global Variables, allowing you to define design tokens like colors, fonts, spacing, and sizing that can be reused across your entire site.</p>
<figure class="break-out article__image"><img decoding="async" loading="lazy" width="800" height="512" src="https://thenokiablog.com/wp-content/uploads/2026/08/meet-kirki-wordpresss-first-visual-builder-with-an-infinite-canvas-10.jpg" alt="Interface displaying CSS variables used for colors, fonts, and spacing in Kirki."><figcaption class="op-vertical-bottom">Global CSS variables powering consistent and easy-to-update site styling. (Large preview)</figcaption></figure>
<p>You can pair these global variables with your class-based structure to:</p>
<ul>
<li>Maintain visual consistency.</li>
<li>Update values globally with a single change.</li>
<li>Easily manage themes like switching between light and dark modes with one click.</li>
</ul>
<p>And while Kirki offers a fully visual experience, it doesn&rsquo;t limit advanced users. You can write custom CSS for any class or element, and even inject JavaScript at the page or element level when needed.</p>
<h3 id="build-complex-interactions-and-animations-visually">Build Complex Interactions and Animations Visually</h3>
<p>When it comes to modern animations and interactive design, Kirki leaves traditional WordPress page builders far behind.</p>
<p>Its fully visual interaction builder lets you create dynamic, immersive experiences.</p>
<figure class="break-out article__image"><img decoding="async" loading="lazy" width="800" height="512" src="https://thenokiablog.com/wp-content/uploads/2026/08/meet-kirki-wordpresss-first-visual-builder-with-an-infinite-canvas-11.jpg" alt="Image of Kirki&rsquo;s timeline-based animation and interaction editor."><figcaption class="op-vertical-bottom">Visual interaction builder allowing advanced animations without coding. (Large preview)</figcaption></figure>
<p>You can build scroll-based animations, hover and click effects, interactive sections that respond across devices, and control visibility, motion, and behavior all within a visual interface.</p>
<p>For advanced users, Kirki includes a <strong>timeline-based editor</strong> where you can:</p>
<ul>
<li>Create multi-step animations.</li>
<li>Fine-tune transitions with precise timing, easing, delays, and sequencing.</li>
</ul>
<p>Even text animations get special attention.</p>
<p>You can animate text by character, word, or full element. Choose custom triggers (scroll, hover, load, etc.) and select from various transition styles or create your own.</p>
<p>Kirki no-code website builder truly helps you move past generic and create unique animations and complex interactions.</p>
<h3 id="seamless-integration-management-with-kirki-apps">Seamless Integration Management with Kirki Apps</h3>
<p>Kirki takes the hassle out of connecting third-party tools with its intuitive <strong>Kirki Apps</strong> system. You can install and manage essential integrations such as analytics, CRMs, email marketing platforms, support widgets, and more, all from within the Kirki editor itself.</p>
<figure class="break-out article__image"><img decoding="async" loading="lazy" width="800" height="512" src="https://thenokiablog.com/wp-content/uploads/2026/08/meet-kirki-wordpresss-first-visual-builder-with-an-infinite-canvas-12.jpg" alt="Kirki Apps interface showing available integrations for analytics, CRM, and more."><figcaption class="op-vertical-bottom">Centralized integrations management with Kirki Apps. (Large preview)</figcaption></figure>
<p>This centralized approach means you never have to leave your workspace. The clean, user-friendly interface guides you through the connection process visually, making setup fast and straightforward even if you&rsquo;re not a technical expert.</p>
<p>Kirki brings the first real multi-user co-editing experience to WordPress. Multiple team members can work on the same page, at the same time, seeing each other&rsquo;s changes unfold live on the canvas.</p>
<figure class="break-out article__image"><img decoding="async" loading="lazy" width="800" height="512" src="https://thenokiablog.com/wp-content/uploads/2026/08/meet-kirki-wordpresss-first-visual-builder-with-an-infinite-canvas-13.jpg" alt="Screenshot of Kirki&rsquo;s real-time collaboration &amp; co-editing."><figcaption class="op-vertical-bottom">First True Multi-user Co-editing Experience in a WordPress Builder. (Large preview)</figcaption></figure>
<p>You see your teammates&rsquo; cursors moving in real time. You watch edits happen as they happen. Every change is color-coded and attributed, so the entire team stays oriented without ever having to ask.</p>
<p>Your team can also leave comments directly on the canvas, pinned exactly where the change needs to happen. For agencies, freelancers working with clients, and in-house teams juggling multiple contributors, this makes a huge of a difference.</p>
<p>Before you publish your site, Kirki helps ensure your site is technically sound with its built-in Page Audit tool.</p>
<figure class="break-out article__image"><img decoding="async" loading="lazy" width="800" height="512" src="https://thenokiablog.com/wp-content/uploads/2026/08/meet-kirki-wordpresss-first-visual-builder-with-an-infinite-canvas-14.jpg" alt="Interface showing Kirki&rsquo;s quality control, including alt text and broken links."><figcaption class="op-vertical-bottom">Page Audit tool ensuring SEO, accessibility, and technical quality before publishing. (Large preview)</figcaption></figure>
<p>It automatically scans your layout for:</p>
<ul>
<li>Missing alt text on images,</li>
<li>Broken links,</li>
<li>Unassigned or duplicate classes,</li>
<li>Accessibility issues,</li>
<li>And more.</li>
</ul>
<p>So you&rsquo;re not just building beautiful pages &mdash; you&rsquo;re shipping fast, accessible, SEO-ready websites with confidence.</p>
<h2 id="theme-layout-options">Theme &amp; Layout Options</h2>
<p>Kirki has a growing library of high-quality templates and modular layout options, so you&rsquo;re never out of options.</p>
<figure class="break-out article__image"><img decoding="async" loading="lazy" width="800" height="512" src="https://thenokiablog.com/wp-content/uploads/2026/08/meet-kirki-wordpresss-first-visual-builder-with-an-infinite-canvas-15.jpg" alt="Preview of Kirki&rsquo;s template library and layout options."><figcaption class="op-vertical-bottom">Extensive library of templates, pre-designed pages, and modular sections. (Large preview)</figcaption></figure>
<h3 id="template-kits-full-website-packs">Template Kits: Full Website Packs</h3>
<p>Kirki&rsquo;s Template Kits include complete multi-page website designs for every industry. Pick a template, update the content, and you&rsquo;re ready to launch.</p>
<p>New template kits are added regularly, so you&rsquo;re always equipped with the latest design trends. And the best part? At no additional cost. You get access to the finest designs without ever paying extra.</p>
<h3 id="pre-designed-pages">Pre-Designed Pages</h3>
<p>Need just a landing page or a pricing page? Kirki also offers standalone pre-designed pages you can drop into your project and customize instantly.</p>
<h3 id="pre-made-sections">Pre-Made Sections</h3>
<p>Prefer to build from scratch but don&rsquo;t want to start with a blank canvas? It also has ready-made sections like hero banners, testimonials, pricing blocks, and FAQs. You can visually assemble your layout in minutes using these.</p>
<h2 id="how-easy-is-kirki-to-use">How Easy Is Kirki To Use?</h2>
<p>Kirki has come a long way in terms of accessibility.</p>
<p>The interface has been refined, the workflow is more intuitive, and a growing library of pre-made blocks, sections, pages, and full templates means you&rsquo;re rarely starting from a blank canvas unless you want to.</p>
<p>That said, if you want something dead simple just to build a basic five-page site fast, there are lighter options out there like Elementor. But they come at the cost of power, performance, design control, and long-term flexibility.</p>
<p>Kirki is built for people who care about what they&rsquo;re building. If you want pixel-level control, clean code output, truly responsive layouts, dynamic content, advanced interactions, and a site that scales without breaking, Kirki delivers all of that, and it&rsquo;s more accessible than ever to get there.</p>
<p>To help you get up to speed quickly, Kirki includes:</p>
<ul>
<li>A refined, intuitive interface that&rsquo;s easier to navigate than ever;</li>
<li>An extensive and growing library of templates, pages, pre-built sections, UI components, and wireframes to kickstart any project instantly;</li>
<li>Guided onboarding to walk you through the essentials;</li>
<li>An AI generator that can scaffold entire pages and layouts in seconds.</li>
</ul>
<p>The bottom line is that Kirki rewards the time you put into learning it. And with everything that&rsquo;s been added recently, that time is shorter than ever.</p>
<h2 id="what-users-are-saying">What Users Are Saying</h2>
<p>For many users, Kirki is more than just a builder. It&rsquo;s the all-in-one tool WordPress has been waiting for. They are calling it the future of WordPress, a truly great alternative to tools like Framer and Webflow.</p>
<figure class="break-out article__image"><img decoding="async" loading="lazy" width="800" height="512" src="https://thenokiablog.com/wp-content/uploads/2026/08/meet-kirki-wordpresss-first-visual-builder-with-an-infinite-canvas-16.jpg" alt="Image showing customer reviews and feedback on Kirki&rsquo;s website builder."><figcaption class="op-vertical-bottom">User testimonials praising Kirki&rsquo;s power, performance, and design freedom. (Large preview)</figcaption></figure>
<h2 id="why-kirki-outshines-traditional-website-builders">Why Kirki Outshines Traditional Website Builders</h2>
<p>Building a professional WordPress website shouldn&rsquo;t mean compromising on speed, flexibility, or performance. Kirki is designed to give designers, developers, and businesses a modern visual building experience that goes beyond the limitations of traditional page builders.</p>
<h3 id="modern-freeform-visual-builder">Modern Freeform Visual Builder</h3>
<p>Design without being restricted by rigid rows or predefined layouts. Kirki&rsquo;s intuitive freeform canvas lets you place, arrange, and customize elements exactly where you want them, giving you complete creative freedom.</p>
<h3 id="real-time-responsive-editing">Real-Time Responsive Editing</h3>
<p>Perfect your website for every screen size with side-by-side responsive editing. Instantly preview and fine-tune your desktop, tablet, and mobile layouts simultaneously, eliminating the guesswork from responsive design.</p>
<h3 id="everything-you-need-in-one-builder">Everything You Need in One Builder</h3>
<p>Forget installing multiple add-ons and third-party extensions. Kirki includes the essential tools, widgets, and features you need in a single, integrated platform, reducing complexity while improving reliability.</p>
<h3 id="performance-optimized-code-output">Performance-Optimized Code Output</h3>
<p>Great websites don&rsquo;t just look good&mdash;they load fast. Kirki generates clean, lightweight, and optimized code that helps improve page speed, user experience, and search engine performance.</p>
<h3 id="seamless-figma-to-wordpress-workflow">Seamless Figma to WordPress Workflow</h3>
<p>Transform your Figma designs into fully functional WordPress pages with minimal effort. Reduce development time and maintain design accuracy from concept to launch.</p>
<p>Create dynamic websites with data-driven content, engaging animations, sophisticated interactions, and global styling controls that keep your branding consistent across every page.</p>
<h3 id="a-powerful-free-version-to-get-your-site-ready">A Powerful Free Version To Get Your Site Ready</h3>
<p>Start building immediately with a feature-rich free version that includes everything you need to create a polished, professional website before deciding to upgrade.</p>
<p>After putting Kirki through its paces, the answer is a clear <strong>yes</strong>. Kirki not only matches traditional WordPress page builders where it counts, but it surpasses them in nearly every critical area.</p>
<p>From its cleaner, faster code output and outstanding performance to its unparalleled design freedom and powerful built-in features, Kirki solves many of the pain points that users have accepted for years.</p>
<p>Its all-in-one approach eliminates the need for multiple plugins, saving time, money, and technical headaches. If you&rsquo;re serious about building high-quality, scalable, and visually stunning websites, Kirki isn&rsquo;t just an alternative; it&rsquo;s the future of WordPress site building.</p>
<p>Ready to experience the difference yourself? Try Kirki today and start building faster, cleaner, and smarter.</p>
<div class="signature"><img decoding="async" src="https://thenokiablog.com/wp-content/uploads/2026/08/meet-kirki-wordpresss-first-visual-builder-with-an-infinite-canvas-2.png" alt="Smashing Editorial" width="35" height="46" loading="lazy"><br>
<span>(yk)</span></div>
<p>The post <a href="https://thenokiablog.com/meet-kirki-wordpresss-first-visual-builder-with-an-infinite-canvas/">Meet Kirki: WordPress’s First Visual Builder With An Infinite Canvas</a> appeared first on <a href="https://thenokiablog.com">The NOKIA Blog</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>6 Best Free Syntax Highlighter WordPress Plugins in 2026</title>
		<link>https://thenokiablog.com/6-best-free-syntax-highlighter-wordpress-plugins-in-2026/</link>
		
		<dc:creator><![CDATA[Mister Nokia]]></dc:creator>
		<pubDate>Sun, 07 Jun 2026 02:51:35 +0000</pubDate>
				<category><![CDATA[Design]]></category>
		<category><![CDATA[Color]]></category>
		<category><![CDATA[Essential WordPress Plugins]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[Syntax Highlighting]]></category>
		<category><![CDATA[WordPress]]></category>
		<category><![CDATA[WordPress Plugins]]></category>
		<guid isPermaLink="false">https://thenokiablog.com/6-best-free-syntax-highlighter-wordpress-plugins-in-2026/</guid>

					<description><![CDATA[<p>Even in the era of blocks, custom code still has a place in the WordPress admin....</p>
<p>The post <a href="https://thenokiablog.com/6-best-free-syntax-highlighter-wordpress-plugins-in-2026/">6 Best Free Syntax Highlighter WordPress Plugins in 2026</a> appeared first on <a href="https://thenokiablog.com">The NOKIA Blog</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>Even in the era of blocks, custom code still has a place in the WordPress admin. For example, we might embed a third-party widget or paste a snippet to share in a tutorial.</p>
<p>WordPress already includes Custom HTML and Code blocks for these purposes. However, their lack of syntax highlighting makes the code harder to read. Thankfully, there are several options for fixing this issue.</p>
<p>Syntax highlighting plugins help by color-coding and formatting your code snippets. Suddenly, you can tell your PHP from your HTML and JavaScript. It looks similar to what you&rsquo;d find in popular editing apps like VS Code.</p>
<p>Want to dress up your code on the front and back end of your site? Here are the best syntax highlighter plugins on the market!</p>
<hr>
<div class="internal-linking-related-contents"><a href="https://thenokiablog.com/rose-gold-design/" class="template-2"><span class="cta">Read more</span><span class="postTitle">Rose gold design</span></a></div><p>custom block to the WordPress editor. Add the block, choose your language, and you&rsquo;ll get an easy-to-read snippet for your readers.</p>
<p>A settings panel lets you adjust the color scheme and typography. It also works with the Classic Editor, adding <code>&lt;pre&gt;&lt;/pre&gt;</code> tags to surround your snippets.</p>
<p><img loading="lazy" decoding="async" src="https://thenokiablog.com/wp-content/uploads/2026/06/6-best-free-syntax-highlighter-wordpress-plugins-in-2026.jpg" alt="Highlighting Code Block WordPress Plugin" width="900" height="291"></p>
<p>If you like the look of the VS Code editor, this is the plugin for you. It uses the same rendering engine as the popular app, while focusing on flexibility and performance.</p>
<div class="internal-linking-related-contents"><a href="https://thenokiablog.com/passion-work/" class="template-2"><span class="cta">Read more</span><span class="postTitle">Passion &amp; work</span></a></div><p>There are 25+ themes to choose from and support for 150+ programming languages. Other niceties include a <code>max-height</code> block setting (you can scroll to reveal additional code), a copy button, and the ability to convert existing Code blocks.</p>
<p><img decoding="async" loading="lazy" src="https://thenokiablog.com/wp-content/uploads/2026/06/6-best-free-syntax-highlighter-wordpress-plugins-in-2026-1.jpg" alt="Code Block Pro WordPress Plugin" width="900" height="290"></p>
<p>PrettyCode claims to be the &ldquo;prettiest syntax highlighting block&rdquo; for WordPress. It fulfills that promise by offering over 50 themes and a slick title display for its custom block.</p>
<p>The plugin uses the popular CodeMirror library to dress up even the most mundane snippets. And there&rsquo;s a look to match just about every site.</p>
<div class="internal-linking-related-contents"><a href="https://thenokiablog.com/the-joy-of-art/" class="template-2"><span class="cta">Read more</span><span class="postTitle">The joy of art</span></a></div><p><img decoding="async" loading="lazy" src="https://thenokiablog.com/wp-content/uploads/2026/06/6-best-free-syntax-highlighter-wordpress-plugins-in-2026-2.jpg" alt="PrettyCode WordPress Plugin" width="900" height="291"></p>
<p>This syntax highlighter plugin offers plenty of formatting options, from plain to vibrant. It also supports more than 90 languages, making it a good choice for a variety of tutorial types.</p>
<p>Powered by Prism.js, you&rsquo;ll find options to enable line numbers, a copy button, and a command line interface (CLI). The plugin also enables highlighting in WordPress post excerpts.</p>
<p><img decoding="async" loading="lazy" src="https://thenokiablog.com/wp-content/uploads/2026/06/6-best-free-syntax-highlighter-wordpress-plugins-in-2026-3.jpg" alt="Prismatic WordPress Plugin" width="900" height="291"></p>
<h2><span id="Syntax_Highlighter_Plugins_at_a_Glance">Syntax Highlighter Plugins at a Glance</span></h2>
<div>
<table class="table-wrapped">
<thead>
<tr>
<th><strong>Plugin</strong></th>
<th><strong>Description</strong></th>
<th><strong>Primary Use</strong></th>
</tr>
</thead>
<tbody>
<tr>
<td>Enlighter</td>
<td>Works with Classic and Block editors, supports multiple languages, includes shortcode and block support, plus a theme customizer.</td>
<td>Flexible multi-language syntax highlighting.</td>
</tr>
<tr>
<td>Syntax-Highlighting Code Block</td>
<td>Adds server-side syntax highlighting to the existing WordPress Code block without loading external scripts.</td>
<td>Performance-focused code highlighting.</td>
</tr>
<tr>
<td>Highlighting Code Block</td>
<td>Adds a custom block for selecting languages, with adjustable color schemes and Classic Editor compatibility.</td>
<td>Custom block-based highlighting.</td>
</tr>
<tr>
<td>Code Block Pro</td>
<td>Uses the VS Code rendering engine, supports 150+ languages, 25+ themes, copy button, and scrollable code blocks.</td>
<td>Advanced VS Code-style highlighting.</td>
</tr>
<tr>
<td>PrettyCode</td>
<td>Uses CodeMirror with 50+ themes, custom block support, and styled title display for snippets.</td>
<td>Theme-rich syntax highlighting.</td>
</tr>
<tr>
<td>Prismatic</td>
<td>Powered by Prism.js with 90+ language support, line numbers, copy button, CLI formatting, and excerpt highlighting.</td>
<td>Prism.js-powered code styling.</td>
</tr>
</tbody>
</table>
</div>
<h2><span id="Make_Your_Code_Snippets_Beautiful_and_Accessible">Make Your Code Snippets Beautiful and Accessible</span></h2>
<p>Using a syntax highlighter to share code snippets adds both form and function. Not only will your code look better, but it will also be more accessible for your audience.</p>
<p>Code is often difficult to read, but it can be improved with smart use of color and typography. The plugins on this list are there to help. They provide a wide range of looks and features. Plus, they can evolve along with your site&rsquo;s design.</p>
<p>Don&rsquo;t settle for the default styles of the WordPress Code block! Not when any of the above plugins will improve your code presentation in just a few clicks.</p>
<hr>
<div class="explore-more">
<h2><span id="More_Essential_Free_WordPress_Plugins">More Essential Free WordPress Plugins</span></h2>
<p>here. He recently started a writing service for WordPress products: WP Product Writeup.  He also has an opinion on just about every subject. You can follow his rants on Bluesky @karks.com.</p>
<p>
                        Read more articles by Eric Karkovack
                    </p>

<hr>
</div><p>The post <a href="https://thenokiablog.com/6-best-free-syntax-highlighter-wordpress-plugins-in-2026/">6 Best Free Syntax Highlighter WordPress Plugins in 2026</a> appeared first on <a href="https://thenokiablog.com">The NOKIA Blog</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>How AI Can Help You Expand Your Agency’s Service Offerings</title>
		<link>https://thenokiablog.com/how-ai-can-help-you-expand-your-agencys-service-offerings/</link>
		
		<dc:creator><![CDATA[Mister Nokia]]></dc:creator>
		<pubDate>Wed, 03 Jun 2026 14:44:53 +0000</pubDate>
				<category><![CDATA[Design]]></category>
		<category><![CDATA[Artificial Intelligence]]></category>
		<category><![CDATA[Artificial Intelligence (AI)]]></category>
		<category><![CDATA[Branding]]></category>
		<category><![CDATA[Business]]></category>
		<category><![CDATA[Developer]]></category>
		<category><![CDATA[Freelance Business]]></category>
		<category><![CDATA[Freelance Workflow]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[Security]]></category>
		<category><![CDATA[SEO]]></category>
		<category><![CDATA[WordPress]]></category>
		<category><![CDATA[WordPress Agency]]></category>
		<category><![CDATA[WordPress AI]]></category>
		<guid isPermaLink="false">https://thenokiablog.com/how-ai-can-help-you-expand-your-agencys-service-offerings/</guid>

					<description><![CDATA[<p>As AI continues to evolve, freelancers and agencies are finding new uses for the technology. It...</p>
<p>The post <a href="https://thenokiablog.com/how-ai-can-help-you-expand-your-agencys-service-offerings/">How AI Can Help You Expand Your Agency’s Service Offerings</a> appeared first on <a href="https://thenokiablog.com">The NOKIA Blog</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>As AI continues to evolve, freelancers and agencies are finding new uses for the technology. It makes sense that many of us start by improving our workflow. Resolving those existing pain points helps us accomplish more with less effort.</p>
<p>However, AI also presents opportunities to broaden our horizons. We can use it to do things we haven&rsquo;t tried before. Even better, we can experiment with ideas that seemed too ambitious a few years earlier. For example, consider projects that had a big learning curve or were time-consuming. Perhaps they don&rsquo;t seem as daunting in the age of artificial intelligence.</p>
<p>The right tool could be the key to offering new and improved services to your clients. It&rsquo;s a vehicle for increasing revenue and strengthening customer relationships. What&rsquo;s not to love?</p>
<p>Let&rsquo;s look at how AI can help you expand your agency&rsquo;s service offerings. We&rsquo;ll cover potential service ideas and considerations before presenting them to clients. You might even find the perfect growth opportunity for your business.</p>
<h2><span id="Find_Tasks_AI_Can_Do_Faster_and_Better">Find Tasks AI Can Do Faster and Better</span></h2>
<div class="internal-linking-related-contents"><a href="https://thenokiablog.com/rose-gold-design/" class="template-2"><span class="cta">Read more</span><span class="postTitle">Rose gold design</span></a></div><p>Time is often the biggest barrier to creating new services. We&rsquo;re busy enough without having to learn something new or focus on tedious work. More than anything, these are the tasks AI can help us tackle.</p>
<p>If you often find yourself knee-deep in code, you can use AI to lift the burden and increase productivity. Here are a few ideas to get you started.</p>
<h3><span id="Performance_and_Security_Audits">Performance and Security Audits</span></h3>
<p>Is your client&rsquo;s website running at peak performance? Is it chock full of vulnerable code? Some plugins can help you find general answers. However, a full audit is often the best way to diagnose and fix issues.</p>
<p>The problem is that it takes significant effort to go through each line of code. You also need to know what to look for during the inspection process. Not everyone has the depth of knowledge in PHP, JavaScript, and other languages found in a WordPress website.</p>
<div class="internal-linking-related-contents"><a href="https://thenokiablog.com/passion-work/" class="template-2"><span class="cta">Read more</span><span class="postTitle">Passion &amp; work</span></a></div><p>AI tools can save you a ton of time. Even a relatively lengthy code audit can be performed in the background (freeing you to enjoy a cup of coffee or two). Plus, some models are adept at finding security issues and performance bottlenecks.</p>
<p>The big caveat here is that AI can point out issues, but you may not be able to fix every one of them. For example, avoid making changes to WordPress core or third-party plugins. Those changes would be overwritten during a software update. You could, however, alert a plugin author of what you found.</p>
<p>On the other hand, the results can lead you to find better options or use AI to build a custom solution. Regardless, it&rsquo;s a service you can charge for.</p>
<p><img loading="lazy" decoding="async" src="https://thenokiablog.com/wp-content/uploads/2026/06/how-ai-can-help-you-expand-your-agencys-service-offerings.webp" alt="AI tools can scan your site's code for security and performance issues" width="900" height="600"></p>
<h3><span id="Custom_Plugin_Development">Custom Plugin Development</span></h3>
<div class="internal-linking-related-contents"><a href="https://thenokiablog.com/the-joy-of-art/" class="template-2"><span class="cta">Read more</span><span class="postTitle">The joy of art</span></a></div><p>There are two truths in agency life: No two projects are the same, and no WordPress plugin can cover every niche. That&rsquo;s what makes custom development an appealing service.</p>
<p>Here again, we face limits on our time and coding abilities. But even an expert developer may not have enough room in their schedule for a complex plugin. Then there&rsquo;s the future commitment to maintenance. It&rsquo;s a lot to put on your virtual plate.</p>
<p>AI models make perfect development companions. They can use WordPress documentation to follow best practices and generate plugins with relative ease. Some more advanced apps can also create a logical file structure for you.</p>
<p>This opens multiple possibilities for your agency. You can create one-off plugins to cover client needs. You might also level up by creating more general-purpose plugins for use across multiple sites. A plugin with enough appeal could be sold in the marketplace.</p>
<p>In every case, there&rsquo;s an opportunity to boost your revenue. Add custom plugin development to your project and maintenance fees.</p>
<p><img loading="lazy" decoding="async" src="https://thenokiablog.com/wp-content/uploads/2026/06/how-ai-can-help-you-expand-your-agencys-service-offerings-1.webp" alt="You can use AI to expand your custom development capabilities" width="900" height="600"></p>
<h3><span id="Content_Starters_and_Analysis">Content Starters and Analysis</span></h3>
<p>It seems like content is always a sticking point when working with clients. We&rsquo;re either waiting for clients to deliver it or we&rsquo;re tasked with cleaning it up. Then there is also the ever-evolving challenge of SEO.</p>
<p>We still don&rsquo;t recommend using AI to generate production-ready content. It&rsquo;s simply too generic and won&rsquo;t reflect branding without human intervention. However, it can help in several areas.</p>
<p>For one, you can use it to create &ldquo;starter&rdquo; content. This is text clients can review and edit to match their needs. The final results might be completely different &ndash; but that&rsquo;s not the point. Seeing content on their screen might be the impetus they need to keep the project moving forward. That saves you time and a few headaches.</p>
<p>AI can also analyze content for accessibility and SEO best practices. Use cases include simplifying language, determining keyword density, improving titles, and generating meta summaries. It&rsquo;s also handy for researching competitors.</p>
<p>Now, these common tasks can be completed in a fraction of the time. Adding them to your service list provides more value to clients. In addition, the SEO analysis could be a source of recurring revenue.</p>
<p><img loading="lazy" decoding="async" src="https://thenokiablog.com/wp-content/uploads/2026/06/how-ai-can-help-you-expand-your-agencys-service-offerings-2.webp" alt="AI can help you optimize client websites for search engines" width="900" height="600"></p>
<h2><span id="AI_Could_Be_Your_Agencys_Growth_Engine">AI Could Be Your Agency&rsquo;s Growth Engine</span></h2>
<p>Perhaps AI&rsquo;s value to your business is all about perspective. Sure, you could use it to make less-good versions of what you&rsquo;re already doing. Generating slop and calling it a day isn&rsquo;t great for your long-term viability. We won&rsquo;t argue that some people have chosen this path.</p>
<p>However, AI is also capable of helping you be the best version of yourself. It just requires a more thoughtful approach. Use these tools to tackle the busy work while keeping a close eye on the results. Most importantly, be the decision maker in what you build. In other words, don&rsquo;t accept AI&rsquo;s initial output as the final answer.</p>
<p>Treating this technology as a partner is a path to growing your agency. The extra time and mental bandwidth you gain will allow you to identify new revenue opportunities. The ideas above are just the tip of the iceberg.</p>
<p><strong>Ask yourself:</strong> <em>What can AI do better and faster? How can it strengthen what I do best? How can I use it to improve client services?</em></p>
<p>The answers might lead you to something great!</p>
<div class="topics">
<h2>Related Topics</h2>
</div>
<p>                here. He recently started a writing service for WordPress products: WP Product Writeup.  He also has an opinion on just about every subject. You can follow his rants on Bluesky @karks.com.</p>
<p>
                        Read more articles by Eric Karkovack
                    </p>

<hr>
<p>The post <a href="https://thenokiablog.com/how-ai-can-help-you-expand-your-agencys-service-offerings/">How AI Can Help You Expand Your Agency’s Service Offerings</a> appeared first on <a href="https://thenokiablog.com">The NOKIA Blog</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>8 CSS &#038; JavaScript Snippets for Adding Scroll Effects to Your Website</title>
		<link>https://thenokiablog.com/8-css-javascript-snippets-for-adding-scroll-effects-to-your-website/</link>
		
		<dc:creator><![CDATA[Mister Nokia]]></dc:creator>
		<pubDate>Tue, 02 Jun 2026 07:36:22 +0000</pubDate>
				<category><![CDATA[Design]]></category>
		<category><![CDATA[Animation]]></category>
		<category><![CDATA[CSS]]></category>
		<category><![CDATA[CSS Effect Snippets]]></category>
		<category><![CDATA[CSS Snippets]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[JavaScript Snippets]]></category>
		<category><![CDATA[Long Scrolling]]></category>
		<category><![CDATA[Parallax Scrolling]]></category>
		<category><![CDATA[Scrolling]]></category>
		<category><![CDATA[UI Components]]></category>
		<category><![CDATA[UI Design]]></category>
		<category><![CDATA[WordPress]]></category>
		<guid isPermaLink="false">https://thenokiablog.com/8-css-javascript-snippets-for-adding-scroll-effects-to-your-website/</guid>

					<description><![CDATA[<p>Scrolling is among the most boring necessities of digital design. Or is it? It&#8217;s easy for...</p>
<p>The post <a href="https://thenokiablog.com/8-css-javascript-snippets-for-adding-scroll-effects-to-your-website/">8 CSS &amp; JavaScript Snippets for Adding Scroll Effects to Your Website</a> appeared first on <a href="https://thenokiablog.com">The NOKIA Blog</a>.</p>
]]></description>
										<content:encoded><![CDATA[<div><img decoding="async" src="https://thenokiablog.com/wp-content/uploads/2026/06/8-css-javascript-snippets-for-adding-scroll-effects-to-your-website.jpg" class="ff-og-image-inserted"></div>
<p>Scrolling is among the most boring necessities of digital design. Or is it? It&rsquo;s easy for both designers and users to ignore. Yet, we spend much of our days moving up, down, and all around. There&rsquo;s something to be said for going the extra mile.</p>
<p>Thankfully, there is an opportunity to do more. Scroll effects can add an element of fun to the mix. They&rsquo;re also great for adding context and drawing attention to important content. Some effects even create a sense of immersion.</p>
<p>Web developers have found all manner of creative use cases. We scoured the CodePen archives to find eight prime examples of how CSS and JavaScript level up the user experience. We were quite impressed with the results.</p>
<p>Below, you&rsquo;ll find everything from basic elements to jaw-dropping visual presentations. You&rsquo;re sure to find inspiration for your next project. Let&rsquo;s get started!</p>
<h2><span id="Premium_ScrollTrigger_Panels">Premium ScrollTrigger Panels</span></h2>
<div class="internal-linking-related-contents"><a href="https://thenokiablog.com/rose-gold-design/" class="template-2"><span class="cta">Read more</span><span class="postTitle">Rose gold design</span></a></div><p class="title-details">Created by Ashish Ranjan</p>
<p>Here&rsquo;s a unique way to scroll through the sections of a webpage. This pen creates a &ldquo;stacking&rdquo; effect, where each new section is layered on top of the last. The previous section zooms and fades out, allowing you to read the current content distraction-free.</p>
<p>Impressively, scrolling up reverses the effect. The technique uses GSAP scroll trigger and makes a long page seem shorter.</p>
<p class="codepen" data-height="450" data-pen-title="PinStack Showcase - Premium ScrollTrigger Panels" data-preview="true" data-default-tab="result" data-slug-hash="JoKXJYQ" data-user="ash1198">See the Pen PinStack Showcase &ndash; Premium ScrollTrigger Panels by Ashish Ranjan</p>
<h2><span id="Scrolling_Text_Rotating_3D_Gallery">Scrolling Text &amp; Rotating 3D Gallery</span></h2>
<div class="internal-linking-related-contents"><a href="https://thenokiablog.com/passion-work/" class="template-2"><span class="cta">Read more</span><span class="postTitle">Passion &amp; work</span></a></div><p class="title-details">Created by Luis Alberto Martinez Riancho</p>
<p>This 3-D gallery turns the classic carousel on its head. It combines scrolling text with a rotating cube that syncs in perfect harmony. Notice the shading and textures used to create a photorealistic feel.</p>
<p>The button-based navigation lets you avoid scrolling altogether, if that&rsquo;s your thing.</p>
<p class="codepen" data-height="450" data-pen-title="Six Faces / Walking The Cow" data-preview="true" data-default-tab="result" data-slug-hash="ZYpyoRV" data-user="luis-lessrain">See the Pen Six Faces / Walking The Cow by Luis Alberto Martinez Riancho</p>
<h2><span id="Stars_Scroll_Effect">Stars Scroll Effect</span></h2>
<div class="internal-linking-related-contents"><a href="https://thenokiablog.com/the-joy-of-art/" class="template-2"><span class="cta">Read more</span><span class="postTitle">The joy of art</span></a></div><p class="title-details">Created by Aleksa Rakocevic</p>
<p>Scroll effects don&rsquo;t have to be bold. There are times when subtlety is called for and appreciated by visitors. This starry background features a parallax motif with a hint of sparkles.</p>
<p>The result is a fun, yet classy, trip down the page. Consider it for blogs or other long-form content.</p>
<p class="codepen" data-height="450" data-pen-title="Stars scroll effect" data-preview="true" data-default-tab="result" data-slug-hash="QwEEWam" data-user="aleksa-rakocevic">See the Pen Stars scroll effect by Aleksa Rakocevic</p>
<h2><span id="Smooth_Scroll_Behavior">Smooth Scroll Behavior</span></h2>
<p class="title-details">Created by Margarita</p>
<p>Staying with the subtle aesthetic, the on-scroll effects of this pen are a great way to dress up an article. Text gently appears as you make your way down the page.</p>
<p>HTML scroll-behavior is used to ensure a smooth ride and add some flair to static content. This one is appropriate for virtually everyone.</p>
<p class="codepen" data-height="450" data-pen-title=" It Was Never A Competition &mdash; Full Story" data-preview="true" data-default-tab="result" data-slug-hash="OPRxZmV" data-user="Margarita-the-solid">See the Pen It Was Never A Competition &mdash; Full Story by Margarita</p>
<h2><span id="Infinite_Loop_Spiral">Infinite Loop Spiral</span></h2>
<p class="title-details">Created by Toc</p>
<p>Are you looking for a completely twisted take on a photo gallery? This spiral masterpiece will certainly grab attention. Scroll down, and this 3-D ribbon-shaped gallery spins through each image.</p>
<p>Use your mouse or finger to change perspectives. The effect is silky-smooth and will enhance your portfolio projects.</p>
<p class="codepen" data-height="450" data-pen-title="Infinite loop spiral" data-preview="true" data-default-tab="result" data-slug-hash="MYjZpOJ" data-user="ol-ivier">See the Pen Infinite loop spiral by Toc</p>
<h2><span id="CSS_Scroll-Timeline_Word_Highlight">CSS Scroll-Timeline Word Highlight</span></h2>
<p class="title-details">Created by Daniel Haim</p>
<p>Here&rsquo;s a way to list your services and/or accomplishments with style. The text on the left stays put as you scroll, while the right side jumps to the next word or phrase. </p>
<p>It uses the scroll-snap-align and scroll-snap-type CSS properties to keep the presentation tidy. JavaScript sets properties based on user action. The result is an attractive method for making an impact with typography.</p>
<p class="codepen" data-height="450" data-pen-title="CSS Scroll-Timeline Word Highlight" data-preview="true" data-default-tab="result" data-slug-hash="azmBEPL" data-user="danielhaim">See the Pen CSS Scroll-Timeline Word Highlight by Daniel Haim</p>
<h2><span id="Springy_Section_Edges">Springy Section Edges</span></h2>
<p class="title-details">Created by Tom Miller</p>
<p>Scrolling from one page section to another doesn&rsquo;t have to be boring. A small effect, like this &ldquo;springy&rdquo; animation, adds context and entertainment value to the process. Reach the bottom of a section, and you&rsquo;re welcomed with a bounce.</p>
<p>Here&rsquo;s an idea: maybe add some confetti when you reach the end of the page. After all, loyal visitors should be celebrated!</p>
<p class="codepen" data-height="450" data-pen-title="Springy Section Edges" data-preview="true" data-default-tab="result" data-slug-hash="WboQLVe" data-user="creativeocean">See the Pen Springy Section Edges by Tom Miller</p>
<h2><span id="Reverse_Scrolling_Columns">Reverse Scrolling Columns</span></h2>
<p class="title-details">Created by Ryan Yu</p>
<p>This photo gallery&rsquo;s multi-column scrolling is reminiscent of a slot machine. The columns are synced, but the middle section moves in the reverse direction. It&rsquo;s fun and immersive without overwhelming users.</p>
<p>Once again, GSAP scroll trigger serves as the secret sauce to the effect.</p>
<p class="codepen" data-height="450" data-pen-title="Reverse scrolling columns" data-preview="true" data-default-tab="result" data-slug-hash="XJdvXym" data-user="iamryanyu">See the Pen Reverse scrolling columns by Ryan Yu</p>
<h2><span id="Use_Scroll_Effects_To_Enhance_User_Experience">Use Scroll Effects To Enhance User Experience</span></h2>
<p>Scrolling doesn&rsquo;t have to be an afterthought in your design projects. The examples above demonstrate what&rsquo;s possible with a little code and imagination.</p>
<p>In some cases, these effects can be the star of the show. Consider the photo galleries in our roundup. They become more interesting and interactive when you go beyond the default grid layout. You get high-end form and functionality.</p>
<p>On the other hand, subtlety also has a place when adding scroll effects to more traditional pages. A hint of elegance can transform static content into something more memorable.</p>
<p>The decision is yours to make. Consider your audience and accessibility needs, then choose the effect that makes the most sense.</p>
<p>Want to see even more awesome scroll effects? Check out our CodePen collection!</p>
<h2><span id="More_CSS_Effects_Snippets">More CSS Effects Snippets</span></h2>
<p>here. He recently started a writing service for WordPress products: WP Product Writeup.  He also has an opinion on just about every subject. You can follow his rants on Bluesky @karks.com.</p>
<p>
                        Read more articles by Eric Karkovack
                    </p>

<hr>
<p>The post <a href="https://thenokiablog.com/8-css-javascript-snippets-for-adding-scroll-effects-to-your-website/">8 CSS &amp; JavaScript Snippets for Adding Scroll Effects to Your Website</a> appeared first on <a href="https://thenokiablog.com">The NOKIA Blog</a>.</p>
]]></content:encoded>
					
		
		
			</item>
	</channel>
</rss>
