<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" xmlns:googleplay="http://www.google.com/schemas/play-podcasts/1.0"><channel><title><![CDATA[Undolog]]></title><description><![CDATA[Research & Development]]></description><link>https://www.undolog.com</link><image><url>https://substackcdn.com/image/fetch/$s_!cbmr!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7d290455-5248-41be-be2d-005d04e5a8ff_802x802.png</url><title>Undolog</title><link>https://www.undolog.com</link></image><generator>Substack</generator><lastBuildDate>Wed, 22 Apr 2026 01:33:58 GMT</lastBuildDate><atom:link href="https://www.undolog.com/feed" rel="self" type="application/rss+xml"/><copyright><![CDATA[Giovambattista Fazioli]]></copyright><language><![CDATA[en]]></language><webMaster><![CDATA[undolog@substack.com]]></webMaster><itunes:owner><itunes:email><![CDATA[undolog@substack.com]]></itunes:email><itunes:name><![CDATA[Giovambattista Fazioli]]></itunes:name></itunes:owner><itunes:author><![CDATA[Giovambattista Fazioli]]></itunes:author><googleplay:owner><![CDATA[undolog@substack.com]]></googleplay:owner><googleplay:email><![CDATA[undolog@substack.com]]></googleplay:email><googleplay:author><![CDATA[Giovambattista Fazioli]]></googleplay:author><itunes:block><![CDATA[Yes]]></itunes:block><item><title><![CDATA[FinderGit v0.1.0 — Stop Tabbing Between Finder and Your Git Client]]></title><description><![CDATA[A public beta of a native macOS app that combines Finder-style file browsing with live Git intelligence &#8212; one window, every repo, branch and diff at a glance.]]></description><link>https://www.undolog.com/p/findergit-v010-stop-tabbing-between</link><guid isPermaLink="false">https://www.undolog.com/p/findergit-v010-stop-tabbing-between</guid><dc:creator><![CDATA[Giovambattista Fazioli]]></dc:creator><pubDate>Fri, 17 Apr 2026 08:31:02 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!YVXB!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fba83f93d-f8c4-424d-8a5a-ec2b8e55febf_2752x1536.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!YVXB!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fba83f93d-f8c4-424d-8a5a-ec2b8e55febf_2752x1536.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!YVXB!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fba83f93d-f8c4-424d-8a5a-ec2b8e55febf_2752x1536.png 424w, https://substackcdn.com/image/fetch/$s_!YVXB!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fba83f93d-f8c4-424d-8a5a-ec2b8e55febf_2752x1536.png 848w, https://substackcdn.com/image/fetch/$s_!YVXB!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fba83f93d-f8c4-424d-8a5a-ec2b8e55febf_2752x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!YVXB!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fba83f93d-f8c4-424d-8a5a-ec2b8e55febf_2752x1536.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!YVXB!,w_2400,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fba83f93d-f8c4-424d-8a5a-ec2b8e55febf_2752x1536.png" width="1200" height="670.054945054945" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ba83f93d-f8c4-424d-8a5a-ec2b8e55febf_2752x1536.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:false,&quot;imageSize&quot;:&quot;large&quot;,&quot;height&quot;:813,&quot;width&quot;:1456,&quot;resizeWidth&quot;:1200,&quot;bytes&quot;:6544936,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://www.undolog.com/i/194492984?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fba83f93d-f8c4-424d-8a5a-ec2b8e55febf_2752x1536.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:&quot;center&quot;,&quot;offset&quot;:false}" class="sizing-large" alt="" srcset="https://substackcdn.com/image/fetch/$s_!YVXB!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fba83f93d-f8c4-424d-8a5a-ec2b8e55febf_2752x1536.png 424w, https://substackcdn.com/image/fetch/$s_!YVXB!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fba83f93d-f8c4-424d-8a5a-ec2b8e55febf_2752x1536.png 848w, https://substackcdn.com/image/fetch/$s_!YVXB!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fba83f93d-f8c4-424d-8a5a-ec2b8e55febf_2752x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!YVXB!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fba83f93d-f8c4-424d-8a5a-ec2b8e55febf_2752x1536.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2><strong>A day with twelve repositories</strong></h2><p><strong>Before.</strong> You have a <code>~/Projects</code> folder with twelve Git repos in it. To know which ones are dirty, you open Terminal, <code>cd</code> into each, run <code>git status</code>, repeat. Or you open Tower and wait for it to scan. Or you open VS Code twelve times. None of this is <em>looking at your files</em> &#8212; it&#8217;s ceremony around looking at your files.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!VGM7!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff7def6f4-75b2-4e0c-b6a8-ff1c4ac2cc5b_800x392.webp" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!VGM7!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff7def6f4-75b2-4e0c-b6a8-ff1c4ac2cc5b_800x392.webp 424w, https://substackcdn.com/image/fetch/$s_!VGM7!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff7def6f4-75b2-4e0c-b6a8-ff1c4ac2cc5b_800x392.webp 848w, https://substackcdn.com/image/fetch/$s_!VGM7!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff7def6f4-75b2-4e0c-b6a8-ff1c4ac2cc5b_800x392.webp 1272w, https://substackcdn.com/image/fetch/$s_!VGM7!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff7def6f4-75b2-4e0c-b6a8-ff1c4ac2cc5b_800x392.webp 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!VGM7!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff7def6f4-75b2-4e0c-b6a8-ff1c4ac2cc5b_800x392.webp" width="800" height="392" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f7def6f4-75b2-4e0c-b6a8-ff1c4ac2cc5b_800x392.webp&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:392,&quot;width&quot;:800,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;FinderGit&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="FinderGit" title="FinderGit" srcset="https://substackcdn.com/image/fetch/$s_!VGM7!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff7def6f4-75b2-4e0c-b6a8-ff1c4ac2cc5b_800x392.webp 424w, https://substackcdn.com/image/fetch/$s_!VGM7!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff7def6f4-75b2-4e0c-b6a8-ff1c4ac2cc5b_800x392.webp 848w, https://substackcdn.com/image/fetch/$s_!VGM7!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff7def6f4-75b2-4e0c-b6a8-ff1c4ac2cc5b_800x392.webp 1272w, https://substackcdn.com/image/fetch/$s_!VGM7!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff7def6f4-75b2-4e0c-b6a8-ff1c4ac2cc5b_800x392.webp 1456w" sizes="100vw"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><strong>After.</strong> You open FinderGit and the folder tree looks like Finder&#8217;s list view, plus four columns: branch name, clean/dirty/unpushed badge, number of changed files, and sizes and dates. Directories inside a dirty repo get a small orange dot with a count &#8212; so you spot modifications three levels deep without expanding anything.</p><p>That&#8217;s the whole pitch. The rest of this post is the details.</p><h2><strong>What is FinderGit?</strong></h2><p>FinderGit is a native macOS app built with SwiftUI. It&#8217;s positioned somewhere between Finder and a heavy Git client like Tower &#8212; it&#8217;s <strong>not a replacement</strong> for either. If your day involves interactive rebases, submodule surgery, or cherry-picking across a long history, you&#8217;ll still want Tower, GitX, or your CLI.</p><p>FinderGit is for the other 90% of the day: the part where you&#8217;re browsing your projects, quickly checking &#8220;what did I leave dirty yesterday?&#8221;, staging a few files, typing a commit message, pushing. Everything happens in the window that already shows your files &#8212; no context switch.</p><p>It runs on macOS 15 Sequoia or later. It&#8217;s free, open-source (the website repo is public, the app repo will follow), and this is the <strong>public beta</strong> &#8212; v0.1.0. Expect a few rough edges; file them, I fix them.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!NdOC!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1616fc91-097d-4f7a-9f25-484dd87a87b7_800x506.webp" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!NdOC!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1616fc91-097d-4f7a-9f25-484dd87a87b7_800x506.webp 424w, https://substackcdn.com/image/fetch/$s_!NdOC!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1616fc91-097d-4f7a-9f25-484dd87a87b7_800x506.webp 848w, https://substackcdn.com/image/fetch/$s_!NdOC!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1616fc91-097d-4f7a-9f25-484dd87a87b7_800x506.webp 1272w, https://substackcdn.com/image/fetch/$s_!NdOC!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1616fc91-097d-4f7a-9f25-484dd87a87b7_800x506.webp 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!NdOC!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1616fc91-097d-4f7a-9f25-484dd87a87b7_800x506.webp" width="800" height="506" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/1616fc91-097d-4f7a-9f25-484dd87a87b7_800x506.webp&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:506,&quot;width&quot;:800,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;FinderGif&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="FinderGif" title="FinderGif" srcset="https://substackcdn.com/image/fetch/$s_!NdOC!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1616fc91-097d-4f7a-9f25-484dd87a87b7_800x506.webp 424w, https://substackcdn.com/image/fetch/$s_!NdOC!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1616fc91-097d-4f7a-9f25-484dd87a87b7_800x506.webp 848w, https://substackcdn.com/image/fetch/$s_!NdOC!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1616fc91-097d-4f7a-9f25-484dd87a87b7_800x506.webp 1272w, https://substackcdn.com/image/fetch/$s_!NdOC!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1616fc91-097d-4f7a-9f25-484dd87a87b7_800x506.webp 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2><strong>&#10024; Key features</strong></h2><ul><li><p><strong>Tree view with sortable columns</strong> &#8212; Finder-style list view, but the columns are Name, Branch, Status, Changes, Size, Date Modified. Click any header to sort.</p></li><li><p><strong>Live Git status per repository</strong> &#8212; a small badge next to each repo: <code>CLEAN</code> (green), <code>DIRTY</code> (orange), <code>UNPUSHED</code> (blue). Updates in real time via FSEvents.</p></li><li><p><strong>Sub-directory dirty indicators</strong> &#8212; directories inside a repo show an orange dot with the number of modified files they contain. Walk down to the changes without expanding every folder.</p></li><li><p><strong>Inline diff viewer</strong> &#8212; click any modified file and the right pane renders a unified diff with green/red lines, hunk headers, and line numbers. Supports <code>HEAD</code>, staged, unstaged, and even untracked files (rendered as all-added).</p></li><li><p><strong>Git actions from the UI</strong> &#8212; Stage, Unstage, Commit, Push, Pull, Fetch. Per file or per repo. Commit messages get a <code>TextField</code> and two buttons: <em>Commit Staged</em> or <em>Commit All &amp; Push</em>.</p></li><li><p><strong>Native Markdown preview via Quick Look</strong> &#8212; press <strong>Space</strong> on any <code>.md</code> or <code>.markdown</code> file and it opens in a native renderer with a GitHub-style theme. Close with Space or Escape. Every other file type falls back to the system <code>qlmanage</code>, so you keep PDF, image, video previews for free.</p></li><li><p><strong>Smart context menus</strong> &#8212; right-click adapts to what you clicked: repo gets Fetch / Pull / Push / Stage All; tracked file gets Stage / Unstage; non-git directory gets Add as Root Folder; everything gets Open / Reveal in Finder / Copy Path / Open in Terminal.</p></li><li><p><strong>Multiple root folders</strong> &#8212; add as many as you want. Drop folders from Finder directly onto the sidebar; they become tree roots.</p></li><li><p><strong>Search &amp; filter</strong> &#8212; type in the toolbar to filter the tree by name (300ms debounce). Toggle <strong>Git Only</strong> to hide everything that isn&#8217;t a repo &#8212; instant overview of just your projects.</p></li><li><p><strong>Keyboard-driven</strong> &#8212; &#8984;&#8679;F Fetch, &#8984;&#8679;L Pull, &#8984;&#8679;P Push, &#8984;&#8679;A Stage All, &#8984;&#8679;S Stage selected, Space Quick Look, &#8984;+/&#8984;- zoom, &#8984;&#8679;R Reveal in Finder. The usual Mac muscle memory.</p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!4d9D!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe725d089-9275-491a-96a5-e89c5c7c7064_800x494.webp" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!4d9D!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe725d089-9275-491a-96a5-e89c5c7c7064_800x494.webp 424w, https://substackcdn.com/image/fetch/$s_!4d9D!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe725d089-9275-491a-96a5-e89c5c7c7064_800x494.webp 848w, https://substackcdn.com/image/fetch/$s_!4d9D!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe725d089-9275-491a-96a5-e89c5c7c7064_800x494.webp 1272w, https://substackcdn.com/image/fetch/$s_!4d9D!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe725d089-9275-491a-96a5-e89c5c7c7064_800x494.webp 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!4d9D!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe725d089-9275-491a-96a5-e89c5c7c7064_800x494.webp" width="800" height="494" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e725d089-9275-491a-96a5-e89c5c7c7064_800x494.webp&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:494,&quot;width&quot;:800,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Context Menu&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Context Menu" title="Context Menu" srcset="https://substackcdn.com/image/fetch/$s_!4d9D!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe725d089-9275-491a-96a5-e89c5c7c7064_800x494.webp 424w, https://substackcdn.com/image/fetch/$s_!4d9D!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe725d089-9275-491a-96a5-e89c5c7c7064_800x494.webp 848w, https://substackcdn.com/image/fetch/$s_!4d9D!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe725d089-9275-491a-96a5-e89c5c7c7064_800x494.webp 1272w, https://substackcdn.com/image/fetch/$s_!4d9D!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe725d089-9275-491a-96a5-e89c5c7c7064_800x494.webp 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2><strong>&#128736; What&#8217;s powering it</strong></h2><p>For the developer-curious reader:</p><ul><li><p><strong>Swift + SwiftUI</strong>, deployment target macOS 15. The UI uses <code>NavigationSplitView</code> with a native <code>Table(children:)</code> for the tree, so sorting, keyboard navigation and selection come from AppKit under the hood.</p></li><li><p><strong>Observation framework</strong> (<code>@Observable</code>) for state &#8212; no <code>ObservableObject</code> / <code>@Published</code> boilerplate. Structured concurrency (<code>async</code>/<code>await</code>) everywhere, no <code>DispatchQueue</code>.</p></li><li><p><strong>FSEvents</strong> via a small <code>GitWatcher</code> that observes both the worktree and <code>.git/HEAD</code>, <code>.git/index</code>, <code>.git/refs/</code>, <code>.git/FETCH_HEAD</code> &#8212; debounced at the user-configurable latency. So pushing in Terminal updates the FinderGit badge within milliseconds.</p></li><li><p><code>git status --porcelain=v2</code> under the hood. <code>/usr/bin/git</code> is called via <code>Process</code> with a parser for the machine-readable output. No libgit2, no reimplementing history &#8212; the real git binary is the source of truth.</p></li><li><p><strong><a href="https://github.com/gonzalezreal/swift-markdown-ui">MarkdownUI</a></strong> for the Markdown preview. Pattern is extensible &#8212; same &#8220;interceptor&#8221; will take <code>.css</code> / <code>.js</code> / <code>.swift</code> with syntax highlighting in a future release.</p></li><li><p><strong><a href="https://sparkle-project.org/">Sparkle</a></strong> for auto-updates, EdDSA-signed. See below.</p></li><li><p><strong>Personal Team signing</strong>, no Apple Developer Program ($99/year) required. That has implications for Gatekeeper &#8212; covered in the install section.</p></li></ul><h2><strong>&#128230; Install</strong></h2><h3><strong>Requirements</strong></h3><ul><li><p>macOS 15 (Sequoia) or later</p></li><li><p>Xcode Command Line Tools &#8212; FinderGit uses the system <code>git</code> binary, which on macOS is shipped as a stub. On a fresh Mac, run once:</p></li></ul><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;bash&quot;,&quot;nodeId&quot;:&quot;3f71e408-fe1c-4115-b7e9-cf4987154bdd&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-bash">  xcode-select --install
  sudo xcodebuild -license</code></pre></div><p>Without these, every Git action inside FinderGit returns <code>nonZeroExit(status: 89, &#8230;)</code>. This will be surfaced as a friendly dialog in a future release &#8212; for now, the Terminal workaround is a one-time setup.</p><h3><strong>Download</strong></h3><ol><li><p>Grab the latest <code>.dmg</code> from the <a href="https://github.com/gfazioli/findergit-website/releases/latest">GitHub Release</a>.</p></li><li><p>Open it and drag <strong>FinderGit</strong> into your Applications folder.</p></li></ol><h3><strong>First launch &#8212; Gatekeeper</strong></h3><p>The app is signed with a free <strong>Personal Team</strong>, not with an Apple Developer ID. That means macOS Gatekeeper doesn&#8217;t know it and will block the first launch with a generic warning. The fix is the standard Mac dance:</p><ol><li><p>Open <strong>Applications</strong> in Finder.</p></li><li><p><strong>Right-click</strong> on FinderGit.app &#8594; <strong>Open</strong>.</p></li><li><p>In the confirmation popup, click <strong>Open</strong> again.</p></li></ol><p>Done once &#8212; from then on the app launches normally, including auto-updates. No paid developer program is funding this; you&#8217;re trading a 5-second first-launch gesture for a free app.</p><h2><strong>&#128260; Auto-updates</strong></h2><p>FinderGit ships with Sparkle. The app checks <code>https://findergit.app/appcast.xml</code> on launch and periodically; when a new version is available, you get the standard Sparkle alert with release notes. Each release DMG is signed with an <strong>EdDSA key</strong> &#8212; the public key ships inside the app, so Sparkle refuses to install an update it cannot verify. Even if my GitHub account were compromised tomorrow, an attacker couldn&#8217;t push a malicious update without my private signing key.</p><p>You can also trigger a check manually via the <strong>FinderGit &#8594; Check for Updates&#8230;</strong> menu item.</p><h2><strong>&#128506; What&#8217;s next</strong></h2><p>The beta is functional, but plenty is already planned. Highlights of the public issue tracker:</p><ul><li><p><strong>Syntax highlighting in Quick Look</strong> for code files (<code>.swift</code>, <code>.js</code>, <code>.ts</code>, <code>.py</code>, <code>.css</code>, <code>.json</code>, &#8230;) &#8212; the Markdown interceptor pattern, extended.</p></li><li><p><strong>Double-click a folder to navigate into it</strong> (drill-down, with a breadcrumb to go back).</p></li><li><p><strong>Branch creation, renaming, deletion, checkout</strong> from the UI.</p></li><li><p><strong>Stash management</strong> (save, pop, apply, drop).</p></li><li><p><strong>GitHub / GitLab / Bitbucket providers</strong> &#8212; list Issues and Pull Requests for the current repo. Preference for <code>gh</code> / <code>glab</code> CLI when installed; REST fallback with a token in Keychain.</p></li><li><p><strong>Kaleidoscope integration</strong> &#8212; jump from the inline diff to the full Kaleidoscope comparison, &#224; la Tower.</p></li><li><p><strong>AI-generated commit messages</strong> &#8212; a built-in &#10024; AI button next to the commit text field that drafts a message from your staged diff, no external tools or API keys required.</p></li><li><p><strong>Auto-fetch on an interval</strong> wired to the setting that already exists.</p></li><li><p><strong>Drag files from the tree to external apps</strong> &#8212; without breaking row selection, which is why it&#8217;s not in v0.1.0.</p></li></ul><p>If any of these are on your wish list, say so &#8212; priorities move.</p><h2><strong>Download, feedback, sponsorship</strong></h2><p>Three things, in order of how much they help:</p><ol><li><p><strong>Download it and try it</strong> &#8594; <a href="https://github.com/gfazioli/findergit-website/releases/latest">latest release</a>. Ten minutes with real repos tells you more than any blog post.</p></li><li><p><strong>Tell me what works and what doesn&#8217;t</strong> &#8594; <a href="https://github.com/gfazioli/findergit-website/issues">open an issue</a> on the website repo. Bug reports, feature requests, workflow pain points &#8212; all welcome. Screenshots help.</p></li><li><p><strong>If it turns into something you rely on, consider sponsoring</strong> &#8594; <a href="https://github.com/sponsors/gfazioli">GitHub Sponsors</a>. FinderGit is free, unsigned by Apple, and maintained in evenings and weekends. Sponsorship keeps the momentum.</p></li></ol><h2><strong>Links</strong></h2><ul><li><p>&#127760; <strong>Website &amp; docs</strong>: <a href="https://findergit.app/">findergit.app</a></p></li><li><p>&#128229; <strong>Download</strong>: <a href="https://github.com/gfazioli/findergit-website/releases/latest">GitHub Release v0.1.0</a></p></li><li><p>&#128027; <strong>Issues &amp; feedback</strong>: <a href="https://github.com/gfazioli/findergit-website/issues">github.com/gfazioli/findergit-website/issues</a></p></li><li><p>&#11088; <strong>Sponsor</strong>: <a href="https://github.com/sponsors/gfazioli">github.com/sponsors/gfazioli</a></p></li></ul>]]></content:encoded></item><item><title><![CDATA[I Built a Shared Library for the Amiga in 68020 Assembly — and a Modern Documentation Site for It]]></title><description><![CDATA[A love letter to the Commodore Amiga &#8212; 50+ utility functions in hand-optimized 68020 assembly, with C bindings, a custom windowing system, and a Next.js documentation site.]]></description><link>https://www.undolog.com/p/i-built-a-shared-library-for-the</link><guid isPermaLink="false">https://www.undolog.com/p/i-built-a-shared-library-for-the</guid><dc:creator><![CDATA[Giovambattista Fazioli]]></dc:creator><pubDate>Wed, 08 Apr 2026 09:58:53 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!q31p!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F200a3ea8-8274-421e-993a-150c9a5d9797_1280x640.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!q31p!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F200a3ea8-8274-421e-993a-150c9a5d9797_1280x640.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!q31p!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F200a3ea8-8274-421e-993a-150c9a5d9797_1280x640.jpeg 424w, https://substackcdn.com/image/fetch/$s_!q31p!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F200a3ea8-8274-421e-993a-150c9a5d9797_1280x640.jpeg 848w, https://substackcdn.com/image/fetch/$s_!q31p!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F200a3ea8-8274-421e-993a-150c9a5d9797_1280x640.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!q31p!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F200a3ea8-8274-421e-993a-150c9a5d9797_1280x640.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!q31p!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F200a3ea8-8274-421e-993a-150c9a5d9797_1280x640.jpeg" width="1280" height="640" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/200a3ea8-8274-421e-993a-150c9a5d9797_1280x640.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:640,&quot;width&quot;:1280,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:133809,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/jpeg&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://www.undolog.com/i/193558127?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F200a3ea8-8274-421e-993a-150c9a5d9797_1280x640.jpeg&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!q31p!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F200a3ea8-8274-421e-993a-150c9a5d9797_1280x640.jpeg 424w, https://substackcdn.com/image/fetch/$s_!q31p!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F200a3ea8-8274-421e-993a-150c9a5d9797_1280x640.jpeg 848w, https://substackcdn.com/image/fetch/$s_!q31p!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F200a3ea8-8274-421e-993a-150c9a5d9797_1280x640.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!q31p!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F200a3ea8-8274-421e-993a-150c9a5d9797_1280x640.jpeg 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Back in <strong>1991</strong>, I started writing what would become a 5,800-line shared library for the Commodore Amiga &#8212; entirely in hand-optimized Motorola 68020 assembly. The first routines (string utilities in <code>libraries.asm</code>) were saved on July 29, 1991. Over the next four years, the library grew steadily &#8212; graphics in October 1994, file I/O and memory management in early 1995, the REI windowing system through mid-1995, and the last recorded update in September 1995.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!8xKd!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8e41b3bd-d280-4aee-88e3-83162aa0b420_2812x2320.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!8xKd!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8e41b3bd-d280-4aee-88e3-83162aa0b420_2812x2320.png 424w, https://substackcdn.com/image/fetch/$s_!8xKd!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8e41b3bd-d280-4aee-88e3-83162aa0b420_2812x2320.png 848w, https://substackcdn.com/image/fetch/$s_!8xKd!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8e41b3bd-d280-4aee-88e3-83162aa0b420_2812x2320.png 1272w, https://substackcdn.com/image/fetch/$s_!8xKd!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8e41b3bd-d280-4aee-88e3-83162aa0b420_2812x2320.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!8xKd!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8e41b3bd-d280-4aee-88e3-83162aa0b420_2812x2320.png" width="1456" height="1201" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/8e41b3bd-d280-4aee-88e3-83162aa0b420_2812x2320.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1201,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:368323,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.undolog.com/i/193558127?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8e41b3bd-d280-4aee-88e3-83162aa0b420_2812x2320.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!8xKd!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8e41b3bd-d280-4aee-88e3-83162aa0b420_2812x2320.png 424w, https://substackcdn.com/image/fetch/$s_!8xKd!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8e41b3bd-d280-4aee-88e3-83162aa0b420_2812x2320.png 848w, https://substackcdn.com/image/fetch/$s_!8xKd!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8e41b3bd-d280-4aee-88e3-83162aa0b420_2812x2320.png 1272w, https://substackcdn.com/image/fetch/$s_!8xKd!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8e41b3bd-d280-4aee-88e3-83162aa0b420_2812x2320.png 1456w" sizes="100vw"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>That code sat on floppy disks and hard drives for decades. Now, in 2026, I&#8217;ve dusted it off, open-sourced it, and built a modern documentation site for it with Next.js.</p><p>Let me tell you the story.</p><h2><strong>What Is This?</strong></h2><p>The <strong>Amiga Assembly Library</strong> is a shared library (<code>assembly.library</code>) for AmigaOS, written between <strong>1991 and 1995</strong>, that provides 50+ high-level utility functions in optimized 68020 assembly. Think of it as a &#8220;standard library&#8221; for Amiga developers &#8212; covering everything from memory management and file I/O to bitmap graphics, string manipulation, and even a full windowing abstraction.</p><p>It&#8217;s <strong>Public Domain</strong> &#8212; no copyright, no licensing, no restrictions. Use it however you want.</p><h2><strong>Why Assembly? And Why Now?</strong></h2><p>When I wrote this library in the early &#8216;90s, the Amiga was my daily machine. I was 19, obsessed with squeezing every cycle out of the 68020, and frustrated by rewriting the same utility routines for every project. So I did what any obsessive programmer would do &#8212; I built a shared library.</p><p>The Amiga community, remarkably, is still alive and well in 2026. People are building hardware accelerators, writing new software, and pushing these machines beyond what anyone thought possible. When you&#8217;re working with a 7 MHz 68000 (or a 50 MHz 68060 if you&#8217;re lucky), every cycle counts. Hand-optimized assembly isn&#8217;t a flex &#8212; it&#8217;s a necessity.</p><p>I decided to open-source the library and give it the documentation it always deserved &#8212; because if this code helped me build a dozen projects in the &#8216;90s, maybe it can help someone else today.</p><h2><strong>What&#8217;s Inside?</strong></h2><p>The library is organized into 8 modules:</p><p><strong>Exec</strong> &#8212; Memory and linked list management. <code>AllocNewList</code>, <code>AllocNode</code>, <code>FreeList</code>, <code>ReAllocVec</code>, and <code>RevertMem</code> for reversing memory regions.</p><p><strong>DOS</strong> &#8212; File operations. <code>Load</code> and <code>Save</code> files to/from memory, <code>FileInfo</code> for metadata, <code>CheckSum</code> for data integrity, <code>LineInput</code> for reading files line by line, and <code>UnitInfo</code> for volume information.</p><p><strong>Graphics</strong> &#8212; Bitmap rendering. Allocate and clone RastPorts, manage bitplanes dynamically, draw boxes and frames, and render printf-style formatted text directly to graphics surfaces.</p><p><strong>Libraries</strong> &#8212; String utilities and media parsing. Case conversion, character filtering, AIFF/IFF chunk parsing, and ILBM image decompression.</p><p><strong>Math</strong> &#8212; Number conversions between decimal, hex, binary strings and values &#8212; in both directions.</p><p><strong>Intuition &amp; GadTools</strong> &#8212; UI widgets. Custom dialog requesters with buttons, gadget property control by name (not just ID), and attribute manipulation.</p><p><strong>REI Interface</strong> &#8212; This is the crown jewel. A complete windowing abstraction that separates UI definitions from application logic. You define your window layout in a <code>.rei</code> binary file, load it at runtime, and interact with named gadgets through an event-driven message loop. Think of it as a declarative UI system &#8212; for a 1990s computer.</p><p><strong>C Interface</strong> &#8212; Full C prototypes and SAS/C pragma declarations so you can call every function from C with type safety.</p><h2><strong>The Magic of Auto-Opening</strong></h2><p>One of my favorite design decisions: when you open <code>assembly.library</code>, it automatically opens and caches <strong>12+ system libraries</strong> for you:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;c&quot;,&quot;nodeId&quot;:&quot;7499db73-a434-4d2b-86c2-47ebac140316&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-c">#include &lt;assembly/assemblybase.h&gt;
#include &lt;clib/assembly_protos.h&gt;

struct AssemblyBase *AssemblyBase;

void main(int argc, char **argv)
{
    if (AssemblyBase = (struct AssemblyBase *)
        OpenLibrary(ASSEMBLYNAME, ASSEMBLY_MINIMUM))
    {
        /* All sub-libraries are now available:
         * AssemblyBase-&gt;ab_DosBase
         * AssemblyBase-&gt;ab_IntuiBase
         * AssemblyBase-&gt;ab_GfxBase
         * AssemblyBase-&gt;ab_GadToolsBase
         * AssemblyBase-&gt;ab_AslBase
         * ... and 7 more
         */

        CloseLibrary((struct Library *)AssemblyBase);
    }
}</code></pre></div><p>One <code>OpenLibrary</code> call and you get <code>graphics.library</code>, <code>intuition.library</code>, <code>dos.library</code>, <code>gadtools.library</code>, <code>asl.library</code>, <code>icon.library</code>, <code>workbench.library</code>, <code>datatypes.library</code>, <code>locale.library</code>, <code>lowlevel.library</code>, <code>realtime.library</code> &#8212; all ready to use. No boilerplate. No forgetting to close one of them.</p><h2><strong>The REI Windowing System</strong></h2><p>The REI (Runtime External Interface) module deserves its own section. Instead of hardcoding window layouts in your application code, you define them in <code>.rei</code> files &#8212; binary interface definitions that describe windows, gadgets, menus, and their relationships.</p><p>At runtime, you load the <code>.rei</code> file and interact with UI elements by <strong>name</strong>:</p><pre><code><code>OpenREI          &#8594; Create the window from a .rei file
WaitREIMsg       &#8594; Event-driven message loop
FindAsmGadget    &#8594; Find a gadget by its name
SetREIAttrs      &#8594; Change window properties
LockREI/UnlockREI &#8594; Thread-safe access
CloseREI         &#8594; Clean shutdown</code></code></pre><p>The project even includes a visual <strong>REI Editor</strong> (v0.52) &#8212; a GUI tool for designing <code>.rei</code> files without writing code.</p><p>This approach is philosophically similar to what modern frameworks do with JSX or XML layouts. But this was designed for AmigaOS, running on a 68020.</p><h2><strong>The Documentation Site</strong></h2><p>Here&#8217;s where old meets new. The library documentation lives at <strong><a href="https://amiga-assembly-library.vercel.app/">amiga-assembly-library.vercel.app</a></strong> &#8212; a modern site built with:</p><ul><li><p><strong>Next.js 15</strong> with Nextra for MDX-based docs</p></li><li><p><strong>Mantine 9</strong> for the UI components</p></li><li><p><strong>TypeScript 6</strong> for type safety</p></li><li><p>Syntax-highlighted 68020 assembly source code</p></li><li><p>Complete API reference for all 50+ functions</p></li><li><p>Example programs with full source</p></li><li><p>An <strong>Amiga Mode</strong> toggle that applies retro styling</p></li></ul><p>Yes, a documentation site built with React and TypeScript &#8212; for a 68020 assembly library. The irony is not lost on me.</p><h2><strong>Getting Started</strong></h2><h3><strong>Requirements</strong></h3><ul><li><p><strong>CPU</strong>: Motorola 68020 or higher</p></li><li><p><strong>OS</strong>: AmigaOS with KickStart 3.0 (V39) or later</p></li><li><p><strong>Assembler</strong>: HiSoft DevPac or compatible</p></li><li><p><strong>C Compiler</strong> (optional): SAS/C, DICE, or compatible</p></li></ul><h3><strong>Installation</strong></h3><p>Copy <code>assembly.library</code> to your <code>LIBS:</code> directory. That&#8217;s it &#8212; it&#8217;s a standard AmigaOS shared library, available system-wide to all applications.</p><h3><strong>Assembly Usage</strong></h3><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;plaintext&quot;,&quot;nodeId&quot;:&quot;44468569-cc48-4d3a-ac31-4ec2da48f64b&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-plaintext">        move.l  _AbsExecBase,a6
        lea     AssemblyName(pc),a1
        moveq   #ASSEMBLY_MINIMUM,d0
        jsr     _LVOOpenLibrary(a6)
        move.l  d0,_AssemblyBase
        beq     .error

        ; Use library functions...
        move.l  _AssemblyBase,a6
        jsr     _LVOAllocNewList(a6)

        ; Clean up
        move.l  _AbsExecBase,a6
        move.l  _AssemblyBase,a1
        jsr     _LVOCloseLibrary(a6)</code></pre></div><h3><strong>C Usage</strong></h3><p>Include the headers, open the library, and call functions directly:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;c&quot;,&quot;nodeId&quot;:&quot;e568ef46-81ed-42b0-a050-aca70a9e4aef&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-c">#include &lt;assembly/assemblybase.h&gt;
#include &lt;clib/assembly_protos.h&gt;
#pragma libcall AssemblyBase AllocNewList 1e 00

struct List *myList = AllocNewList();</code></pre></div><h2><strong>What&#8217;s in the Source?</strong></h2><p>The library is about 5,800 lines of hand-optimized 68020 assembly across 8 modules:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!X6mI!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F895a7aa8-89d0-485d-ae4c-19001a47fafc_1118x876.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!X6mI!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F895a7aa8-89d0-485d-ae4c-19001a47fafc_1118x876.png 424w, https://substackcdn.com/image/fetch/$s_!X6mI!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F895a7aa8-89d0-485d-ae4c-19001a47fafc_1118x876.png 848w, https://substackcdn.com/image/fetch/$s_!X6mI!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F895a7aa8-89d0-485d-ae4c-19001a47fafc_1118x876.png 1272w, https://substackcdn.com/image/fetch/$s_!X6mI!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F895a7aa8-89d0-485d-ae4c-19001a47fafc_1118x876.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!X6mI!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F895a7aa8-89d0-485d-ae4c-19001a47fafc_1118x876.png" width="1118" height="876" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/895a7aa8-89d0-485d-ae4c-19001a47fafc_1118x876.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:876,&quot;width&quot;:1118,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:127406,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.undolog.com/i/193558127?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F895a7aa8-89d0-485d-ae4c-19001a47fafc_1118x876.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!X6mI!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F895a7aa8-89d0-485d-ae4c-19001a47fafc_1118x876.png 424w, https://substackcdn.com/image/fetch/$s_!X6mI!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F895a7aa8-89d0-485d-ae4c-19001a47fafc_1118x876.png 848w, https://substackcdn.com/image/fetch/$s_!X6mI!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F895a7aa8-89d0-485d-ae4c-19001a47fafc_1118x876.png 1272w, https://substackcdn.com/image/fetch/$s_!X6mI!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F895a7aa8-89d0-485d-ae4c-19001a47fafc_1118x876.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p></p><p>Plus C headers, pragma declarations, example programs (including a screen grabber, a window inspector, and the visual REI editor), and comprehensive documentation.</p><h2><strong>Why Open Source This?</strong></h2><p>Because the Amiga community thrives on sharing. The best Amiga software was always the stuff people passed around on floppy disks, uploaded to Aminet, and shared freely. This library is Public Domain &#8212; not MIT, not GPL, not Apache. <strong>Public Domain</strong>. Take it, use it, modify it, sell it, whatever you want. No attribution required.</p><p>If even one person building an Amiga project finds a useful function in here, it was worth it.</p><h2><strong>Links</strong></h2><ul><li><p><strong>Documentation</strong>: <a href="https://amiga-assembly-library.vercel.app/">amiga-assembly-library.vercel.app</a></p></li><li><p><strong>Source Code</strong>: <a href="https://github.com/gfazioli/Assembly-Library">github.com/gfazioli/Assembly-Library</a></p></li><li><p><strong>Documentation Source</strong>: <a href="https://github.com/gfazioli/amiga-assembly-library">github.com/gfazioli/amiga-assembly-library</a></p></li></ul><div><hr></div><p>This code is over 30 years old. I wrote it when I was a teenager, on a machine with 2 MB of RAM. And somehow it still works, still compiles, and still does exactly what it was designed to do. There&#8217;s something deeply satisfying about that.</p><p>If you&#8217;re into retro computing, 68k assembly, or just appreciate the craft of writing software for constrained platforms &#8212; I&#8217;d love to hear from you. Drop a star on the repo, open an issue, or just say hi.</p><p>The Amiga isn&#8217;t dead. It&#8217;s just warming up.</p>]]></content:encoded></item><item><title><![CDATA[Selection Through the Looking Glass: A 3D Stack Component for Mantine]]></title><description><![CDATA[A Mantine component that turns flat item selection into a spatial, 3D card-stack browsing experience &#8212; with keyboard, wheel, touch, and click navigation built in.]]></description><link>https://www.undolog.com/p/selection-through-the-looking-glass</link><guid isPermaLink="false">https://www.undolog.com/p/selection-through-the-looking-glass</guid><dc:creator><![CDATA[Giovambattista Fazioli]]></dc:creator><pubDate>Sat, 28 Mar 2026 08:56:06 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!G8mH!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F78993f24-a4a1-45f9-85a1-06e8f3ee7d34_1280x640.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!G8mH!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F78993f24-a4a1-45f9-85a1-06e8f3ee7d34_1280x640.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!G8mH!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F78993f24-a4a1-45f9-85a1-06e8f3ee7d34_1280x640.jpeg 424w, https://substackcdn.com/image/fetch/$s_!G8mH!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F78993f24-a4a1-45f9-85a1-06e8f3ee7d34_1280x640.jpeg 848w, https://substackcdn.com/image/fetch/$s_!G8mH!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F78993f24-a4a1-45f9-85a1-06e8f3ee7d34_1280x640.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!G8mH!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F78993f24-a4a1-45f9-85a1-06e8f3ee7d34_1280x640.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!G8mH!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F78993f24-a4a1-45f9-85a1-06e8f3ee7d34_1280x640.jpeg" width="1280" height="640" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/78993f24-a4a1-45f9-85a1-06e8f3ee7d34_1280x640.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:640,&quot;width&quot;:1280,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:94203,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/jpeg&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://www.undolog.com/i/192390114?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F78993f24-a4a1-45f9-85a1-06e8f3ee7d34_1280x640.jpeg&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!G8mH!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F78993f24-a4a1-45f9-85a1-06e8f3ee7d34_1280x640.jpeg 424w, https://substackcdn.com/image/fetch/$s_!G8mH!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F78993f24-a4a1-45f9-85a1-06e8f3ee7d34_1280x640.jpeg 848w, https://substackcdn.com/image/fetch/$s_!G8mH!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F78993f24-a4a1-45f9-85a1-06e8f3ee7d34_1280x640.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!G8mH!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F78993f24-a4a1-45f9-85a1-06e8f3ee7d34_1280x640.jpeg 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2><strong>Introduction</strong></h2><p>Remember the first time you used macOS Time Machine? That moment when your desktop peeled away into an infinite corridor of snapshots, and navigating through time suddenly felt <em>physical</em>. That spatial metaphor stuck with us. <strong>mantine-depth-select</strong> brings that same sense of depth to any selection UI in your React application &#8212; pricing plans, document versions, onboarding steps, or anything else where browsing through stacked content beats scrolling a flat list. It&#8217;s a single component with zero extra dependencies, built entirely on Mantine 8&#8217;s Styles API.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!eBN8!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F346244d1-21bf-4c62-8868-7e4955ba577d_800x879.webp" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!eBN8!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F346244d1-21bf-4c62-8868-7e4955ba577d_800x879.webp 424w, https://substackcdn.com/image/fetch/$s_!eBN8!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F346244d1-21bf-4c62-8868-7e4955ba577d_800x879.webp 848w, https://substackcdn.com/image/fetch/$s_!eBN8!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F346244d1-21bf-4c62-8868-7e4955ba577d_800x879.webp 1272w, https://substackcdn.com/image/fetch/$s_!eBN8!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F346244d1-21bf-4c62-8868-7e4955ba577d_800x879.webp 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!eBN8!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F346244d1-21bf-4c62-8868-7e4955ba577d_800x879.webp" width="800" height="879" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/346244d1-21bf-4c62-8868-7e4955ba577d_800x879.webp&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:879,&quot;width&quot;:800,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;mantine Depth Select&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="mantine Depth Select" title="mantine Depth Select" srcset="https://substackcdn.com/image/fetch/$s_!eBN8!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F346244d1-21bf-4c62-8868-7e4955ba577d_800x879.webp 424w, https://substackcdn.com/image/fetch/$s_!eBN8!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F346244d1-21bf-4c62-8868-7e4955ba577d_800x879.webp 848w, https://substackcdn.com/image/fetch/$s_!eBN8!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F346244d1-21bf-4c62-8868-7e4955ba577d_800x879.webp 1272w, https://substackcdn.com/image/fetch/$s_!eBN8!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F346244d1-21bf-4c62-8868-7e4955ba577d_800x879.webp 1456w" sizes="100vw"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2><strong>What is DepthSelect?</strong></h2><p>DepthSelect renders an array of items as a 3D stack of cards. The frontmost card is the active selection; cards behind it recede with progressive scale, vertical offset, opacity, and blur &#8212; all configurable per-level. When the user navigates (via keyboard, mouse wheel, trackpad swipe, click, or arrow controls), cards animate smoothly in and out of the stack with CSS transitions.</p><p>The component follows Mantine&#8217;s compound component pattern. Built-in arrow controls appear by default, but you can hide them and wire up your own UI through controlled <code>value</code>/<code>onChange</code>. It supports both string and numeric values, loop mode, and respects <code>prefers-reduced-motion</code>.</p><h2><strong>&#10024; Key Features</strong></h2><h3><strong>3D Perspective Stack</strong></h3><p>Each card in the stack receives progressive CSS transforms based on its depth. Four props control the effect:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;tsx&quot;,&quot;nodeId&quot;:&quot;c4fcb0f0-102c-4845-9c44-bd93ca682cd0&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-tsx">&lt;DepthSelect
  data={items}
  scaleStep={0.1}       // scale reduction per level (default)
  translateYStep={30}    // vertical offset in px per level
  opacityStep={0.15}     // opacity reduction per level
  blurStep={1}           // blur in px per level
  w={400}
  h={300}
/&gt;</code></pre></div><p>Cards that exit the stack (when navigating forward) animate toward the viewer with a scale-up and fade-out. Cards entering from behind animate in from the deepest visible position.</p><h3><strong>Multi-Input Navigation</strong></h3><p>Every common input method works out of the box:</p><ul><li><p><strong>Keyboard</strong>: <code>ArrowUp</code>/<code>ArrowDown</code> to step through, <code>Home</code>/<code>End</code> to jump</p></li><li><p><strong>Mouse wheel / Trackpad</strong>: Scroll over the component to navigate (page scroll is blocked automatically). Disable with <code>withScrollNavigation={false}</code></p></li><li><p><strong>Touch</strong>: Vertical swipe gestures for mobile</p></li><li><p><strong>Click</strong>: Click the second card (depth 1) to advance</p></li><li><p><strong>Controls</strong>: Built-in arrow buttons with optional label</p></li></ul><h3><strong>Built-in Controls with </strong><code>controlsProps</code></h3><p>The default controls sit to the right of the stack. You can reposition them, add a label, set a fixed width, change alignment, or swap the icons &#8212; all through a single <code>controlsProps</code> object:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;tsx&quot;,&quot;nodeId&quot;:&quot;6d0b5bc3-fac3-4611-b58f-16eaec6d491a&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-tsx">&lt;DepthSelect
  data={items}
  controlsPosition="left"
  controlsProps={{
    w: 100,
    justify: 'end',
    labelFormatter: (item) =&gt; `Step ${item.value}`,
    upIcon: &lt;IconChevronUp size={16} /&gt;,
    downIcon: &lt;IconChevronDown size={16} /&gt;,
  }}
  w={400}
  h={300}
/&gt;</code></pre></div><h3><strong>Compound Component Pattern</strong></h3><p>Need full layout control? Set <code>withControls={false}</code> and use <code>DepthSelect.Controls</code> as a child:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;tsx&quot;,&quot;nodeId&quot;:&quot;6ab34166-c4d0-4bd9-8e43-da91860dc563&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-tsx">&lt;DepthSelect data={items} withControls={false} w={400} h={300}&gt;
  &lt;DepthSelect.Controls labelFormatter={(item) =&gt; String(item.value)} /&gt;
&lt;/DepthSelect&gt;</code></pre></div><p>Or skip the built-in controls entirely and build your own navigation with <code>value</code> and <code>onChange</code>.</p><h3><strong>Loop Mode</strong></h3><p>Enable <code>loop</code> to let navigation wrap from the last item back to the first (and vice versa). The arrow controls never show a disabled state in loop mode:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;tsx&quot;,&quot;nodeId&quot;:&quot;b3e559d0-0910-4ca8-99ab-027fd25da2d4&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-tsx">&lt;DepthSelect data={items} loop w={400} h={200} /&gt;</code></pre></div><h2><strong>&#128640; Getting Started</strong></h2><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;bash&quot;,&quot;nodeId&quot;:&quot;65d2c80c-a92e-4d1f-91da-be87071ac7ec&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-bash">npm install @gfazioli/mantine-depth-select</code></pre></div><p>Import the styles at the root of your application:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;typescript&quot;,&quot;nodeId&quot;:&quot;e3122884-0688-405a-aec6-3a7760ff9800&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-typescript">import '@gfazioli/mantine-depth-select/styles.css';</code></pre></div><p>Minimal example:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;tsx&quot;,&quot;nodeId&quot;:&quot;b728e9d7-4c67-4e40-aac0-f260722d4331&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-tsx">import { Card, Text } from '@mantine/core';
import { DepthSelect, DepthSelectItem } from '@gfazioli/mantine-depth-select';

const data: DepthSelectItem[] = [
  { value: 'first', view: &lt;Card p="lg" withBorder h="100%"&gt;&lt;Text&gt;First item&lt;/Text&gt;&lt;/Card&gt; },
  { value: 'second', view: &lt;Card p="lg" withBorder h="100%"&gt;&lt;Text&gt;Second item&lt;/Text&gt;&lt;/Card&gt; },
  { value: 'third', view: &lt;Card p="lg" withBorder h="100%"&gt;&lt;Text&gt;Third item&lt;/Text&gt;&lt;/Card&gt; },
];

function Demo() {
  return &lt;DepthSelect data={data} w={400} h={200} /&gt;;
}</code></pre></div><p>The <code>w</code> and <code>h</code> props define the area where cards live. Cards should use <code>h="100%"</code> to fill the available height.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!kGf1!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F41bfa50e-7554-4160-9a64-653747353588_800x515.webp" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!kGf1!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F41bfa50e-7554-4160-9a64-653747353588_800x515.webp 424w, https://substackcdn.com/image/fetch/$s_!kGf1!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F41bfa50e-7554-4160-9a64-653747353588_800x515.webp 848w, https://substackcdn.com/image/fetch/$s_!kGf1!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F41bfa50e-7554-4160-9a64-653747353588_800x515.webp 1272w, https://substackcdn.com/image/fetch/$s_!kGf1!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F41bfa50e-7554-4160-9a64-653747353588_800x515.webp 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!kGf1!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F41bfa50e-7554-4160-9a64-653747353588_800x515.webp" width="800" height="515" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/41bfa50e-7554-4160-9a64-653747353588_800x515.webp&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:515,&quot;width&quot;:800,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Use Cases&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Use Cases" title="Use Cases" srcset="https://substackcdn.com/image/fetch/$s_!kGf1!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F41bfa50e-7554-4160-9a64-653747353588_800x515.webp 424w, https://substackcdn.com/image/fetch/$s_!kGf1!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F41bfa50e-7554-4160-9a64-653747353588_800x515.webp 848w, https://substackcdn.com/image/fetch/$s_!kGf1!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F41bfa50e-7554-4160-9a64-653747353588_800x515.webp 1272w, https://substackcdn.com/image/fetch/$s_!kGf1!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F41bfa50e-7554-4160-9a64-653747353588_800x515.webp 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2><strong>Props &amp; API</strong></h2><h3><strong>DepthSelect</strong></h3><ul><li><p><code>data</code> &#8212; <code>DepthSelectItem[]</code> &#8212; Array of <code>{ value, view }</code> objects. <code>value</code> can be <code>string</code> or <code>number</code>, <code>view</code> is any <code>ReactNode</code></p></li><li><p><code>value</code> / <code>defaultValue</code> / <code>onChange</code> &#8212; Standard controlled/uncontrolled pattern</p></li><li><p><code>w</code> / <code>h</code> &#8212; Dimensions of the card area (passed to the inner stack container)</p></li><li><p><code>visibleCards</code> &#8212; Number of cards visible in the stack (default: <code>4</code>)</p></li><li><p><code>withControls</code> &#8212; Show built-in arrow controls (default: <code>true</code>)</p></li><li><p><code>controlsPosition</code> &#8212; <code>"right"</code> (default) or <code>"left"</code></p></li><li><p><code>controlsProps</code> &#8212; All <code>DepthSelectControlsProps</code> passed to the built-in controls</p></li><li><p><code>loop</code> &#8212; Enable wrap-around navigation (default: <code>false</code>)</p></li><li><p><code>withScrollNavigation</code> &#8212; Enable mouse wheel / trackpad navigation (default: <code>true</code>)</p></li><li><p><code>transitionDuration</code> &#8212; Animation duration in ms (default: <code>400</code>)</p></li><li><p><code>scaleStep</code> / <code>translateYStep</code> / <code>opacityStep</code> / <code>blurStep</code> &#8212; Per-level depth effect parameters</p></li></ul><h3><strong>DepthSelect.Controls</strong></h3><ul><li><p><code>labelFormatter</code> &#8212; <code>(item: DepthSelectItem) =&gt; ReactNode</code> &#8212; Custom label between arrows</p></li><li><p><code>upIcon</code> / <code>downIcon</code> &#8212; Custom icons for the arrow buttons</p></li><li><p><code>justify</code> &#8212; Vertical alignment: <code>"start"</code>, <code>"center"</code> (default), <code>"end"</code></p></li><li><p>Plus all Mantine <code>BoxProps</code> (<code>w</code>, <code>h</code>, <code>style</code>, etc.)</p></li></ul><h2><strong>&#127912; Styles API</strong></h2><p><strong>Selectors</strong>: <code>root</code>, <code>stack</code>, <code>card</code>, <code>controls</code>, <code>controlUp</code>, <code>controlDown</code>, <code>controlLabel</code></p><p><strong>Data attributes</strong>:</p><ul><li><p><code>data-active</code> &#8212; Frontmost (selected) card</p></li><li><p><code>data-depth="N"</code> &#8212; Card depth level (0 = front)</p></li><li><p><code>data-exited</code> &#8212; Card that has animated out</p></li><li><p><code>data-disabled</code> &#8212; Disabled control button</p></li><li><p><code>data-controls-position</code> &#8212; <code>"right"</code> or <code>"left"</code> on root</p></li></ul><p><strong>CSS variables</strong>: <code>--ds-transition-duration</code>, <code>--ds-scale-step</code>, <code>--ds-translate-y-step</code>, <code>--ds-opacity-step</code>, <code>--ds-blur-step</code>, <code>--ds-visible-cards</code></p><h2><strong>&#9889; Performance</strong></h2><ul><li><p><strong>Render window</strong>: Only <code>activeIndex - 1</code> through <code>activeIndex + visibleCards + 1</code> are in the DOM. A dataset of 100 items renders ~6 nodes</p></li><li><p><strong>Memoized styles</strong>: Card CSS is computed once per navigation change via <code>useMemo</code></p></li><li><p><strong>Targeted </strong><code>will-change</code>: Only visible cards get GPU layer promotion; hidden cards use <code>will-change: auto</code></p></li><li><p><strong>Non-passive wheel listener</strong>: Blocks page scroll without layout thrashing</p></li><li><p><code>prefers-reduced-motion</code>: Transitions are disabled automatically for users who prefer reduced motion</p></li></ul><h2><strong>Live Demo &amp; Documentation</strong></h2><p>Explore interactive demos &#8212; including a configurator with live props, rich card examples, pricing plan selector, version history browser, onboarding wizard, and more:</p><p><strong><a href="https://gfazioli.github.io/mantine-depth-select/">gfazioli.github.io/mantine-depth-select</a></strong></p><h2><strong>Links</strong></h2><ul><li><p>&#128230; npm: <a href="https://www.npmjs.com/package/@gfazioli/mantine-depth-select">@gfazioli/mantine-depth-select</a></p></li><li><p>&#128214; Docs: <a href="https://gfazioli.github.io/mantine-depth-select/">gfazioli.github.io/mantine-depth-select</a></p></li><li><p>&#128279; GitHub: <a href="https://github.com/gfazioli/mantine-depth-select">github.com/gfazioli/mantine-depth-select</a></p></li><li><p>&#129513; Mantine Extensions: <a href="https://mantine-extensions.vercel.app/">mantine-extensions.vercel.app</a></p></li></ul>]]></content:encoded></item><item><title><![CDATA[Mantine Border Animate - A Complete Beam Rewrite with Dual Rendering Modes]]></title><description><![CDATA[The beam variant has been completely rebuilt with two rendering engines, multi-color gradient support, and a streamlined API.]]></description><link>https://www.undolog.com/p/mantine-border-animate-a-complete</link><guid isPermaLink="false">https://www.undolog.com/p/mantine-border-animate-a-complete</guid><dc:creator><![CDATA[Giovambattista Fazioli]]></dc:creator><pubDate>Fri, 27 Mar 2026 05:27:36 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!v4a0!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F29ed6546-b665-47d9-ac60-a81c8c2a1eb8_1280x640.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!v4a0!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F29ed6546-b665-47d9-ac60-a81c8c2a1eb8_1280x640.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!v4a0!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F29ed6546-b665-47d9-ac60-a81c8c2a1eb8_1280x640.jpeg 424w, https://substackcdn.com/image/fetch/$s_!v4a0!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F29ed6546-b665-47d9-ac60-a81c8c2a1eb8_1280x640.jpeg 848w, https://substackcdn.com/image/fetch/$s_!v4a0!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F29ed6546-b665-47d9-ac60-a81c8c2a1eb8_1280x640.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!v4a0!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F29ed6546-b665-47d9-ac60-a81c8c2a1eb8_1280x640.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!v4a0!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F29ed6546-b665-47d9-ac60-a81c8c2a1eb8_1280x640.jpeg" width="1280" height="640" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/29ed6546-b665-47d9-ac60-a81c8c2a1eb8_1280x640.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:640,&quot;width&quot;:1280,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:230648,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/jpeg&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://www.undolog.com/i/192025021?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F29ed6546-b665-47d9-ac60-a81c8c2a1eb8_1280x640.jpeg&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!v4a0!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F29ed6546-b665-47d9-ac60-a81c8c2a1eb8_1280x640.jpeg 424w, https://substackcdn.com/image/fetch/$s_!v4a0!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F29ed6546-b665-47d9-ac60-a81c8c2a1eb8_1280x640.jpeg 848w, https://substackcdn.com/image/fetch/$s_!v4a0!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F29ed6546-b665-47d9-ac60-a81c8c2a1eb8_1280x640.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!v4a0!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F29ed6546-b665-47d9-ac60-a81c8c2a1eb8_1280x640.jpeg 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2><strong>Introduction</strong></h2><p><a href="https://gfazioli.github.io/mantine-border-animate/">Version 1.0.0 of </a><code>@gfazioli/mantine-border-animate</code> is a major release that reimagines how animated borders work. The beam effect &#8212; the heart of the component &#8212; now ships with two distinct rendering modes, letting you choose between pixel-perfect border tracing and smooth conic rotation. Combined with new multi-color gradients, hover pause, custom easing, and built-in accessibility, this release makes animated borders more flexible, more beautiful, and easier to use than ever.</p><h2><strong>What&#8217;s New</strong></h2><h3><strong>Two Beam Rendering Modes</strong></h3><p>The biggest addition in v1.0 is the <code>beamMode</code> prop, which lets you choose how the beam effect is rendered:</p><p><strong>Path mode</strong> (<code>beamMode="path"</code>, the new default) uses CSS <code>offset-path</code> to move a radial-gradient circle along the component&#8217;s border. The beam maintains a uniform size at every position and travels at constant speed along the perimeter. This is the best choice for most UI elements.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;tsx&quot;,&quot;nodeId&quot;:&quot;efa98fc6-9506-44a1-b59f-32798d149ebd&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-tsx">&lt;BorderAnimate beamMode="path" size="md" duration={5}&gt;
  &lt;Card&gt;Your content&lt;/Card&gt;
&lt;/BorderAnimate&gt;
</code></pre></div><p><strong>Conic mode</strong> (<code>beamMode="conic"</code>) uses a rotating <code>conic-gradient</code> wedge animated via CSS <code>@property</code>. The rotation is buttery smooth, and the <code>size</code> prop controls the angular spread of the wedge (from a tight 18-degree spotlight at <code>xs</code> to a full 180-degree sweep at <code>xl</code>). It works best on circular or near-square elements.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;tsx&quot;,&quot;nodeId&quot;:&quot;e95b2633-2e18-4a31-b34e-d80e58371a3b&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-tsx">&lt;BorderAnimate beamMode="conic" size="lg" duration={3}&gt;
  &lt;Avatar radius="100%" /&gt;
&lt;/BorderAnimate&gt;
</code></pre></div><h3><strong>Multi-Color Gradients with </strong><code>colorStops</code></h3><p>Move beyond two-color gradients. The new <code>colorStops</code> prop gives you full control over the beam&#8217;s colors:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;tsx&quot;,&quot;nodeId&quot;:&quot;213bd6d6-74a2-45aa-94b3-31701b29bcdb&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-tsx">&lt;BorderAnimate
  colorStops={[
    { color: 'transparent', position: 0 },
    { color: 'green', position: 20 },
    { color: 'cyan', position: 40 },
    { color: 'yellow', position: 60 },
    { color: 'red', position: 80 },
    { color: 'transparent', position: 100 },
  ]}
&gt;
  &lt;Paper&gt;Multi-color beam&lt;/Paper&gt;
&lt;/BorderAnimate&gt;
</code></pre></div><p><code>colorStops</code> works with both beam modes:</p><ul><li><p><strong>Path mode</strong> generates a <code>radial-gradient</code> with concentric color rings</p></li><li><p><strong>Conic mode</strong> generates a custom <code>conic-gradient</code> &#8212; use transparent stops for beam/wedge effects, or fill the entire circle for a rotating rainbow border</p></li></ul><h3><strong>Pause on Hover</strong></h3><p>Interactive elements often need the animation to pause during user interaction. The new <code>pauseOnHover</code> prop does exactly that &#8212; using pure CSS <code>animation-play-state</code> with zero JavaScript overhead:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;tsx&quot;,&quot;nodeId&quot;:&quot;6d8ac68a-f51b-428e-ba67-21e2c934445f&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-tsx">&lt;BorderAnimate pauseOnHover&gt;
  &lt;Button&gt;Hover me to pause&lt;/Button&gt;
&lt;/BorderAnimate&gt;
</code></pre></div><h3><strong>Custom Timing Functions</strong></h3><p>Fine-tune animation feel with <code>timingFunction</code>. By default, beam uses <code>linear</code> and glow/pulse use <code>ease-in-out</code>, but you can override with any CSS timing function:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;tsx&quot;,&quot;nodeId&quot;:&quot;0e30a8aa-7a46-48dc-b832-a70c1659513a&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-tsx">&lt;BorderAnimate timingFunction="ease-in-out" /&gt;
&lt;BorderAnimate timingFunction="cubic-bezier(0.4, 0, 0.2, 1)" /&gt;
&lt;BorderAnimate timingFunction="steps(8)" /&gt;  {/* retro pixel effect */}
</code></pre></div><h3><strong>Accessibility Out of the Box</strong></h3><p>BorderAnimate now respects <code>prefers-reduced-motion</code> automatically. Users who have enabled &#8220;Reduce motion&#8221; in their OS settings will see no animations &#8212; no configuration needed, no extra props. It just works.</p><h2><strong>Breaking Changes</strong></h2><p>This is a major release with several breaking changes. Here&#8217;s what you need to update:</p><h3><code>gradient</code><strong> variant removed</strong></h3><p>The <code>gradient</code> variant is gone. Use <code>beamMode="conic"</code> with full <code>colorStops</code> instead:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;tsx&quot;,&quot;nodeId&quot;:&quot;1fb24ccb-f3dc-4107-81d0-b68d9e996cb4&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-tsx">// v0.x
&lt;BorderAnimate variant="gradient" colorFrom="red" colorTo="blue" /&gt;

// v1.0
&lt;BorderAnimate
  beamMode="conic"
  colorStops={[
    { color: 'red', position: 0 },
    { color: 'blue', position: 50 },
    { color: 'red', position: 100 },
  ]}
/&gt;
</code></pre></div><h3><code>opacity</code><strong> renamed to </strong><code>borderOpacity</code></h3><p>The old <code>opacity</code> prop conflicted with the CSS <code>opacity</code> property inherited from Mantine&#8217;s <code>BoxProps</code>. It&#8217;s now <code>borderOpacity</code>:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;tsx&quot;,&quot;nodeId&quot;:&quot;c92d322e-d941-44ee-897d-6c1d2420933d&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-tsx">// v0.x
&lt;BorderAnimate opacity={0.5} /&gt;

// v1.0
&lt;BorderAnimate borderOpacity={0.5} /&gt;
</code></pre></div><h3><code>anchor</code><strong> and </strong><code>count</code><strong> props removed</strong></h3><p>Both props have been removed with no direct replacement. Simply remove them from your components.</p><h3><code>size</code><strong> semantics changed</strong></h3><p>The <code>size</code> prop now behaves differently depending on <code>beamMode</code>:</p><ul><li><p><strong>Path mode</strong>: still pixel-based (<code>xs</code>=64px to <code>xl</code>=480px) &#8212; same as v0.x</p></li><li><p><strong>Conic mode</strong>: angular spread of the wedge (<code>xs</code>=18 degrees to <code>xl</code>=180 degrees)</p></li></ul><h3><code>BorderAnimateVariant</code><strong> type updated</strong></h3><p>The type changed from <code>'beam' | 'glow' | 'gradient' | 'pulse'</code> to <code>'beam' | 'glow' | 'pulse'</code>. Update any type assertions or switch statements in your code.</p><h2><strong>Improvements</strong></h2><ul><li><p><strong>Glow z-index fix</strong> &#8212; the glow variant now correctly renders behind content, preventing the overlay from obscuring text</p></li><li><p><strong>Accurate demos</strong> &#8212; all documentation demos now show code that exactly matches the rendered output</p></li><li><p><strong>New exported types</strong> &#8212; <code>BorderAnimateBeamMode</code>, <code>BorderAnimateColorStop</code>, and <code>BorderAnimateVariant</code> are now properly exported for consumers</p></li></ul><h2><strong>Getting Started</strong></h2><p>Install or update:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;bash&quot;,&quot;nodeId&quot;:&quot;8b10b4a9-a5eb-4d0c-a5c8-bef28d844f3a&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-bash">npm install @gfazioli/mantine-border-animate@1
</code></pre></div><p>Import styles at the root of your application:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;typescript&quot;,&quot;nodeId&quot;:&quot;c019bbdc-c3fe-473e-8165-b8f7bf979f6a&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-typescript">import '@gfazioli/mantine-border-animate/styles.css';
</code></pre></div><p>Basic usage:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;tsx&quot;,&quot;nodeId&quot;:&quot;3ee65ebb-7615-4f56-b0a8-1b40958c49d9&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-tsx">import { BorderAnimate } from '@gfazioli/mantine-border-animate';

function Demo() {
  return (
    &lt;BorderAnimate&gt;
      &lt;Paper withBorder p="md" radius="md"&gt;
        Your content with an animated border
      &lt;/Paper&gt;
    &lt;/BorderAnimate&gt;
  );
}
</code></pre></div><h2><strong>Links</strong></h2><ul><li><p><a href="https://www.npmjs.com/package/@gfazioli/mantine-border-animate">npm package</a></p></li><li><p><a href="https://gfazioli.github.io/mantine-border-animate/">Documentation and Demos</a></p></li><li><p><a href="https://github.com/gfazioli/mantine-border-animate">GitHub Repository</a></p></li><li><p><a href="https://mantine-extensions.vercel.app/">Mantine Extensions Hub</a></p></li></ul>]]></content:encoded></item><item><title><![CDATA[Mantine Rings Progress - A Complete Rewrite Inspired by Apple Watch]]></title><description><![CDATA[The concentric ring progress component for Mantine gets a major upgrade: native RingProgress, glow effects, staggered animations, unified tooltips, and full accessibility &#8212; all in one release.]]></description><link>https://www.undolog.com/p/mantine-rings-progress-a-complete</link><guid isPermaLink="false">https://www.undolog.com/p/mantine-rings-progress-a-complete</guid><dc:creator><![CDATA[Giovambattista Fazioli]]></dc:creator><pubDate>Wed, 25 Mar 2026 11:25:39 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/04f112e3-c3b5-49bc-91c1-e99be91a6286_1280x640.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="native-video-embed" data-component-name="VideoPlaceholder" data-attrs="{&quot;mediaUploadId&quot;:&quot;4a393aa7-8e00-4872-b5b2-4d9fd0990193&quot;,&quot;duration&quot;:null}"></div><h2><strong>Introduction</strong></h2><p><a href="https://gfazioli.github.io/mantine-rings-progress/">Version 3.0.0</a> of <code>@gfazioli/mantine-rings-progress</code> is a ground-up rewrite. The internal fork of Mantine&#8217;s <code>RingProgress</code> has been completely removed, replaced by the native component from <code>@mantine/core</code>. This eliminates ~600 lines of custom SVG and animation code while adding 10 new features that bring the component closer to the Apple Watch activity rings experience.</p><p>Whether you&#8217;re building fitness dashboards, goal trackers, or compact data visualizations, this release gives you the tools to make your rings shine &#8212; literally.</p><h2><strong>&#10024; New Features</strong></h2><h3><strong>Per-ring Customization</strong></h3><p>Each ring can now override global props individually. Mix thick and thin rings, toggle round caps per ring, or set a custom background track color:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;tsx&quot;,&quot;nodeId&quot;:&quot;c905b75f-ece9-437d-b542-df625deec20d&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-tsx">const rings = [
  { value: 40, color: 'cyan', thickness: 18 },
  { value: 65, color: 'red', thickness: 10, roundCaps: false },
  { value: 90, color: '#f90', thickness: 6, rootColor: 'gray' },
];

&lt;RingsProgress size={180} rings={rings} /&gt;
</code></pre></div><p>Supported per-ring overrides: <code>thickness</code>, <code>roundCaps</code>, <code>rootColor</code>, <code>glowIntensity</code>, <code>glowColor</code>, <code>ariaLabel</code>, <code>tooltip</code>.</p><h3><strong>Staggered Entrance Animation</strong></h3><p>Instead of all rings appearing at once, use <code>staggerDelay</code> to animate them one after another &#8212; outer ring first, inner ring last:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;tsx&quot;,&quot;nodeId&quot;:&quot;50a47541-4c7a-4dc2-9e51-de25e9345da2&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-tsx">&lt;RingsProgress animate staggerDelay={300} transitionDuration={1000} rings={rings} /&gt;
</code></pre></div><h3><strong>Glow / Neon Effect</strong></h3><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!JrEf!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa54cb2e5-cb37-44ab-9b6b-d71a96fb18ea_800x522.webp" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!JrEf!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa54cb2e5-cb37-44ab-9b6b-d71a96fb18ea_800x522.webp 424w, https://substackcdn.com/image/fetch/$s_!JrEf!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa54cb2e5-cb37-44ab-9b6b-d71a96fb18ea_800x522.webp 848w, https://substackcdn.com/image/fetch/$s_!JrEf!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa54cb2e5-cb37-44ab-9b6b-d71a96fb18ea_800x522.webp 1272w, https://substackcdn.com/image/fetch/$s_!JrEf!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa54cb2e5-cb37-44ab-9b6b-d71a96fb18ea_800x522.webp 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!JrEf!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa54cb2e5-cb37-44ab-9b6b-d71a96fb18ea_800x522.webp" width="800" height="522" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/a54cb2e5-cb37-44ab-9b6b-d71a96fb18ea_800x522.webp&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:522,&quot;width&quot;:800,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Glow&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Glow" title="Glow" srcset="https://substackcdn.com/image/fetch/$s_!JrEf!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa54cb2e5-cb37-44ab-9b6b-d71a96fb18ea_800x522.webp 424w, https://substackcdn.com/image/fetch/$s_!JrEf!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa54cb2e5-cb37-44ab-9b6b-d71a96fb18ea_800x522.webp 848w, https://substackcdn.com/image/fetch/$s_!JrEf!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa54cb2e5-cb37-44ab-9b6b-d71a96fb18ea_800x522.webp 1272w, https://substackcdn.com/image/fetch/$s_!JrEf!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa54cb2e5-cb37-44ab-9b6b-d71a96fb18ea_800x522.webp 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Add a neon-like glow that follows the ring shape using CSS <code>drop-shadow</code>. Set <code>glow={true}</code> for a default 6px blur, or pass a custom radius. Each ring can override the effect with <code>glowIntensity</code> and <code>glowColor</code>:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;tsx&quot;,&quot;nodeId&quot;:&quot;3a49807d-cfb2-4990-84a0-cf30e3c3b141&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-tsx">&lt;RingsProgress glow={8} rings={rings} /&gt;
</code></pre></div><h3><strong>Pulse on Completion</strong></h3><p>When a ring reaches 100%, trigger a satisfying scale + brightness pulse animation &#8212; inspired by the Apple Watch ring closure celebration:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;tsx&quot;,&quot;nodeId&quot;:&quot;b8e88765-8b2c-4ab8-8669-f8be197d40aa&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-tsx">&lt;RingsProgress pulseOnComplete rings={rings} /&gt;
</code></pre></div><p>The animation respects <code>prefers-reduced-motion</code> and uses a <code>data-pulsing</code> attribute for CSS customization.</p><h3><code>onRingComplete</code><strong> Callback</strong></h3><p>Run custom logic when a ring crosses the 100% threshold:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;tsx&quot;,&quot;nodeId&quot;:&quot;519a2a26-7d00-41f3-9b8f-d046421e5638&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-tsx">&lt;RingsProgress
  onRingComplete={(index, ring) =&gt; {
    showNotification({ message: `${ring.color} ring completed!` });
  }}
  rings={rings}
/&gt;
</code></pre></div><h3><strong>Start Angle &amp; Direction</strong></h3><p>Control where rings start filling and in which direction:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;tsx&quot;,&quot;nodeId&quot;:&quot;c90e1fe5-c929-45c3-8889-815bbcc9392b&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-tsx">&lt;RingsProgress startAngle={90} direction="counterclockwise" rings={rings} /&gt;
</code></pre></div><p><code>startAngle={0}</code> is the 12 o&#8217;clock position (default). Direction accepts <code>'clockwise'</code> or <code>'counterclockwise'</code>.</p><h3><strong>Unified Tooltip</strong></h3><p>The new <code>withTooltip</code> prop shows a single, chart-style tooltip with color swatches for all rings &#8212; no more overlapping per-ring tooltips:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;tsx&quot;,&quot;nodeId&quot;:&quot;42397d83-6b5f-42c5-8796-e235e3798fc2&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-tsx">&lt;RingsProgress
  withTooltip
  rings={[
    { value: 20, color: 'green', tooltip: 'Fitness &#8211; 40 Gb' },
    { value: 80, color: 'blue', tooltip: 'Running &#8211; 50 min' },
  ]}
/&gt;
</code></pre></div><p>If <code>tooltip</code> is not set on a ring, the percentage value is shown automatically.</p><h3><strong>Accessibility</strong></h3><p>The component is now fully accessible out of the box:</p><ul><li><p>Root element: <code>role="group"</code> with <code>aria-label="Progress rings"</code> (overridable)</p></li><li><p>Each ring: <code>role="progressbar"</code> with <code>aria-valuenow</code>, <code>aria-valuemin</code>, <code>aria-valuemax</code></p></li><li><p>Per-ring <code>ariaLabel</code> for custom screen reader text</p></li><li><p>Automatic <code>prefers-reduced-motion</code> support via <code>useReducedMotion</code></p></li></ul><h2><strong>&#128165; Breaking Changes</strong></h2><p>This is a major release with breaking changes. Here&#8217;s what you need to update:</p><h3><strong>Animation props renamed/removed</strong></h3><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;tsx&quot;,&quot;nodeId&quot;:&quot;148c74f7-ee26-4e78-bfa9-978f4fc298e5&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-tsx">  &lt;RingsProgress
-   animationDuration={1000}
-   animationSteps={60}
-   animationTimingFunction="ease"
+   transitionDuration={1000}
    animate
    rings={rings}
  /&gt;
</code></pre></div><ul><li><p><code>animationDuration</code> &#8594; <code>transitionDuration</code> (default changed from <code>1000</code> to <code>0</code>)</p></li><li><p><code>animationSteps</code> &#8212; removed (CSS transitions replaced JS <code>setInterval</code>)</p></li><li><p><code>animationTimingFunction</code> &#8212; removed (CSS uses <code>ease</code> by default)</p></li></ul><blockquote><p><strong>Tip</strong>: If you only use <code>animate</code> for entrance animation, you can omit <code>transitionDuration</code> entirely &#8212; the component auto-applies 1000ms during the entrance phase.</p></blockquote><h3><strong>Ring type changed</strong></h3><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;typescript&quot;,&quot;nodeId&quot;:&quot;660be7e2-cba3-4347-a7ff-304d9b153ab7&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-typescript">- import { RingProgressSection } from '@gfazioli/mantine-rings-progress';
- const rings: RingProgressSection[] = [...]
+ import { RingsProgressRing } from '@gfazioli/mantine-rings-progress';
+ const rings: RingsProgressRing[] = [...]
</code></pre></div><p>The base properties (<code>value</code>, <code>color</code>, <code>tooltip</code>) remain the same, so existing code that only uses those will work without changes.</p><h3><strong>Props interface</strong></h3><p><code>RingsProgressProps</code> now extends <code>BoxProps + StylesApiProps + ElementProps&lt;'div'&gt;</code> (standard Mantine pattern) instead of the forked <code>RingProgressProps</code>. All common props (<code>size</code>, <code>thickness</code>, <code>roundCaps</code>, <code>label</code>, <code>animate</code>) are still available.</p><h3><strong>Styles API</strong></h3><p>A new <code>'label'</code> style name was added. Style names are now <code>'root' | 'ring' | 'label'</code>.</p><h2><strong>&#128295; Improvements</strong></h2><h3><strong>Drastically simplified internals</strong></h3><p>The entire forked <code>RingProgress/</code> directory (~600 lines) has been deleted. The component now delegates entirely to Mantine&#8217;s native <code>RingProgress</code>, ensuring better compatibility with future Mantine releases and smaller bundle size.</p><h3><strong>Smart entrance animation</strong></h3><p>The component auto-applies 1000ms transition during the entrance phase only. After all rings mount, <code>transitionDuration</code> reverts to 0 &#8212; preventing geometry changes (like resizing) from triggering unwanted CSS transitions.</p><h3><strong>Interactive demos</strong></h3><p>The documentation now includes replay buttons for animation demos, interactive sliders for the pulse demo, and a configurator with all new props.</p><h2><strong>&#128027; Bug Fixes</strong></h2><ul><li><p><strong>Label centering</strong>: Fixed the label being offset from center &#8212; now rendered as a separate overlay</p></li><li><p><strong>Debug styles</strong>: Removed hardcoded <code>color: 'red'</code> on the label</p></li><li><p><strong>Theme overrides</strong>: Fixed <code>useProps('Rings', ...)</code> &#8594; <code>useProps('RingsProgress', ...)</code></p></li><li><p><strong>Countdown demo</strong>: Added <code>transitionDuration={0}</code> to prevent CSS transitions from interfering with rapid updates</p></li></ul><h2><strong>Getting Started</strong></h2><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;bash&quot;,&quot;nodeId&quot;:&quot;fb0cfa0b-b975-4400-985d-0ade9e32ee5a&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-bash">npm install @gfazioli/mantine-rings-progress@3
</code></pre></div><p>Import styles at the root of your application:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;typescript&quot;,&quot;nodeId&quot;:&quot;419d164a-09c4-48f6-9bc1-7f1f9e23eeba&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-typescript">import '@gfazioli/mantine-rings-progress/styles.css';
</code></pre></div><p>Basic usage with the new features:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;tsx&quot;,&quot;nodeId&quot;:&quot;d6b150aa-c2ba-436c-804d-a5fb367d236d&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-tsx">import { RingsProgress } from '@gfazioli/mantine-rings-progress';

function ActivityRings() {
  const rings = [
    { value: 75, color: 'green', tooltip: 'Move' },
    { value: 50, color: 'blue', tooltip: 'Exercise' },
    { value: 90, color: 'orange', tooltip: 'Stand' },
  ];

  return (
    &lt;RingsProgress
      size={180}
      rings={rings}
      animate
      staggerDelay={300}
      glow={6}
      withTooltip
      pulseOnComplete
      onRingComplete={(i) =&gt; console.log(`Ring ${i} done!`)}
    /&gt;
  );
}
</code></pre></div><h2><strong>Links</strong></h2><ul><li><p>&#128230; <a href="https://www.npmjs.com/package/@gfazioli/mantine-rings-progress">npm package</a></p></li><li><p>&#128214; <a href="https://gfazioli.github.io/mantine-rings-progress/">Documentation &amp; Demos</a></p></li><li><p>&#128279; <a href="https://github.com/gfazioli/mantine-rings-progress">GitHub Repository</a></p></li><li><p>&#129513; <a href="https://mantine-extensions.vercel.app/">Mantine Extensions Hub</a></p></li></ul>]]></content:encoded></item><item><title><![CDATA[Mantine QR Code - A Real QR Code Generator for Mantine]]></title><description><![CDATA[Generate fully scannable, beautifully styled QR codes with custom dot shapes, finder patterns, image overlays, and one-click SVG/PNG download &#8212; all integrated with the Mantine design system.]]></description><link>https://www.undolog.com/p/mantine-qr-code-a-real-qr-code-generator</link><guid isPermaLink="false">https://www.undolog.com/p/mantine-qr-code-a-real-qr-code-generator</guid><dc:creator><![CDATA[Giovambattista Fazioli]]></dc:creator><pubDate>Fri, 20 Mar 2026 09:01:19 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!AMjc!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcf428c96-e466-4951-83ac-15d9be5e3835_1280x640.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!AMjc!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcf428c96-e466-4951-83ac-15d9be5e3835_1280x640.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!AMjc!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcf428c96-e466-4951-83ac-15d9be5e3835_1280x640.jpeg 424w, https://substackcdn.com/image/fetch/$s_!AMjc!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcf428c96-e466-4951-83ac-15d9be5e3835_1280x640.jpeg 848w, https://substackcdn.com/image/fetch/$s_!AMjc!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcf428c96-e466-4951-83ac-15d9be5e3835_1280x640.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!AMjc!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcf428c96-e466-4951-83ac-15d9be5e3835_1280x640.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!AMjc!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcf428c96-e466-4951-83ac-15d9be5e3835_1280x640.jpeg" width="1280" height="640" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/cf428c96-e466-4951-83ac-15d9be5e3835_1280x640.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:640,&quot;width&quot;:1280,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:245090,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/jpeg&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://www.undolog.com/i/191453687?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcf428c96-e466-4951-83ac-15d9be5e3835_1280x640.jpeg&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!AMjc!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcf428c96-e466-4951-83ac-15d9be5e3835_1280x640.jpeg 424w, https://substackcdn.com/image/fetch/$s_!AMjc!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcf428c96-e466-4951-83ac-15d9be5e3835_1280x640.jpeg 848w, https://substackcdn.com/image/fetch/$s_!AMjc!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcf428c96-e466-4951-83ac-15d9be5e3835_1280x640.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!AMjc!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcf428c96-e466-4951-83ac-15d9be5e3835_1280x640.jpeg 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2><strong>Introduction</strong></h2><p>The Mantine QR Code component has been completely rebuilt from the ground up. What was previously an LED-style indicator has been transformed into a fully functional QR code generator that produces real, scannable codes from any string input. It renders pure SVG for crisp output at any resolution, follows Mantine&#8217;s Styles API conventions, and ships with a dedicated download hook for exporting to SVG, PNG, JPEG, or WebP.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!oS71!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F49bb3f12-b039-4d49-807b-8f99fffd6168_800x875.webp" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!oS71!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F49bb3f12-b039-4d49-807b-8f99fffd6168_800x875.webp 424w, https://substackcdn.com/image/fetch/$s_!oS71!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F49bb3f12-b039-4d49-807b-8f99fffd6168_800x875.webp 848w, https://substackcdn.com/image/fetch/$s_!oS71!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F49bb3f12-b039-4d49-807b-8f99fffd6168_800x875.webp 1272w, https://substackcdn.com/image/fetch/$s_!oS71!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F49bb3f12-b039-4d49-807b-8f99fffd6168_800x875.webp 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!oS71!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F49bb3f12-b039-4d49-807b-8f99fffd6168_800x875.webp" width="800" height="875" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/49bb3f12-b039-4d49-807b-8f99fffd6168_800x875.webp&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:875,&quot;width&quot;:800,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Mantine QR Code&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Mantine QR Code" title="Mantine QR Code" srcset="https://substackcdn.com/image/fetch/$s_!oS71!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F49bb3f12-b039-4d49-807b-8f99fffd6168_800x875.webp 424w, https://substackcdn.com/image/fetch/$s_!oS71!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F49bb3f12-b039-4d49-807b-8f99fffd6168_800x875.webp 848w, https://substackcdn.com/image/fetch/$s_!oS71!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F49bb3f12-b039-4d49-807b-8f99fffd6168_800x875.webp 1272w, https://substackcdn.com/image/fetch/$s_!oS71!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F49bb3f12-b039-4d49-807b-8f99fffd6168_800x875.webp 1456w" sizes="100vw"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2><strong>&#10024; New Features</strong></h2><h3><strong>Real QR Code Generation</strong></h3><p>The component now encodes actual QR code data using the <code>qrcode</code> library. Pass any string &#8212; a URL, WiFi credentials, vCard, plain text &#8212; and get a scannable QR code rendered as optimized SVG.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;tsx&quot;,&quot;nodeId&quot;:&quot;516308d9-9c5f-494a-b16f-678f005b7acf&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-tsx">import { QRCode } from '@gfazioli/mantine-qr-code';

function Demo() {
  return &lt;QRCode value="https://mantine.dev" /&gt;;
}
</code></pre></div><p>The <code>value</code> prop is the only required prop. Everything else has sensible defaults.</p><h3><strong>Dot Styles</strong></h3><p>Control the appearance of data modules with the <code>dotStyle</code> prop. Three built-in styles are available:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;tsx&quot;,&quot;nodeId&quot;:&quot;99701b43-af42-49dd-b5fd-6a4a8768f6c1&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-tsx">&lt;QRCode value="https://mantine.dev" dotStyle="square" /&gt;   {/* Default */}
&lt;QRCode value="https://mantine.dev" dotStyle="rounded" /&gt;
&lt;QRCode value="https://mantine.dev" dotStyle="dots" /&gt;
</code></pre></div><p>Each style generates a single combined <code>&lt;path&gt;</code> element for all data modules, keeping the DOM lightweight even for dense QR codes.</p><h3><strong>Corner Styles</strong></h3><p>The three finder patterns (the large squares in the corners) can be styled independently with <code>cornerStyle</code>:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;tsx&quot;,&quot;nodeId&quot;:&quot;02906df3-d064-4ddd-b4c7-0b2c1f6f3f67&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-tsx">&lt;QRCode value="https://mantine.dev" cornerStyle="square" /&gt;   {/* Default */}
&lt;QRCode value="https://mantine.dev" cornerStyle="rounded" /&gt;
&lt;QRCode value="https://mantine.dev" cornerStyle="dots" /&gt;
</code></pre></div><p>Mix and match dot and corner styles for unique designs:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;tsx&quot;,&quot;nodeId&quot;:&quot;a508f8b4-882e-4b5c-9924-5e36eece525f&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-tsx">&lt;QRCode
  value="https://mantine.dev"
  dotStyle="dots"
  cornerStyle="rounded"
  color="violet"
/&gt;
</code></pre></div><h3><strong>Image Overlay with Excavation</strong></h3><p>Add a logo or image at the center of the QR code. The component automatically removes (&#8221;excavates&#8221;) the data modules behind the image to keep the code scannable:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;tsx&quot;,&quot;nodeId&quot;:&quot;35ab1180-023a-4e4a-8e17-bd44004ca6d6&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-tsx">&lt;QRCode
  value="https://mantine.dev"
  size="xl"
  image="https://example.com/logo.png"
  imageSize={0.2}
  imageRadius="md"
  imagePadding={1}
  imageExcavate={true}
  errorCorrectionLevel="H"
/&gt;
</code></pre></div><blockquote><p><strong>Tip:</strong> Use <code>errorCorrectionLevel="H"</code> when overlaying images &#8212; it provides 30% error correction, ensuring the code remains scannable even with a portion covered.</p></blockquote><h3><strong>Error Correction Levels</strong></h3><p>Four standard error correction levels are supported via the <code>errorCorrectionLevel</code> prop:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!c4o9!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4a5c13be-40d7-4442-9b50-7adaa7c0bf8d_1078x494.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!c4o9!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4a5c13be-40d7-4442-9b50-7adaa7c0bf8d_1078x494.png 424w, https://substackcdn.com/image/fetch/$s_!c4o9!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4a5c13be-40d7-4442-9b50-7adaa7c0bf8d_1078x494.png 848w, https://substackcdn.com/image/fetch/$s_!c4o9!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4a5c13be-40d7-4442-9b50-7adaa7c0bf8d_1078x494.png 1272w, https://substackcdn.com/image/fetch/$s_!c4o9!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4a5c13be-40d7-4442-9b50-7adaa7c0bf8d_1078x494.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!c4o9!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4a5c13be-40d7-4442-9b50-7adaa7c0bf8d_1078x494.png" width="1078" height="494" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/4a5c13be-40d7-4442-9b50-7adaa7c0bf8d_1078x494.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:494,&quot;width&quot;:1078,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:64835,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.undolog.com/i/191453687?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4a5c13be-40d7-4442-9b50-7adaa7c0bf8d_1078x494.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!c4o9!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4a5c13be-40d7-4442-9b50-7adaa7c0bf8d_1078x494.png 424w, https://substackcdn.com/image/fetch/$s_!c4o9!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4a5c13be-40d7-4442-9b50-7adaa7c0bf8d_1078x494.png 848w, https://substackcdn.com/image/fetch/$s_!c4o9!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4a5c13be-40d7-4442-9b50-7adaa7c0bf8d_1078x494.png 1272w, https://substackcdn.com/image/fetch/$s_!c4o9!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4a5c13be-40d7-4442-9b50-7adaa7c0bf8d_1078x494.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p></p><h3><code>useQRCodeDownload</code><strong> Hook</strong></h3><p>A dedicated hook makes it easy to export the QR code in multiple formats:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;tsx&quot;,&quot;nodeId&quot;:&quot;fdc68860-60bd-499b-a7f9-e975895aa355&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-tsx">import { QRCode, useQRCodeDownload } from '@gfazioli/mantine-qr-code';

function Demo() {
  const { ref, download, getDataUrl } = useQRCodeDownload({
    fileName: 'my-qr-code',
    scale: 4,          // 4x resolution for raster formats
  });

  return (
    &lt;&gt;
      &lt;QRCode ref={ref} value="https://mantine.dev" /&gt;
      &lt;button onClick={() =&gt; download({ format: 'svg' })}&gt;Download SVG&lt;/button&gt;
      &lt;button onClick={() =&gt; download({ format: 'png' })}&gt;Download PNG&lt;/button&gt;
    &lt;/&gt;
  );
}
</code></pre></div><p>Supported formats: <code>svg</code>, <code>png</code>, <code>jpeg</code>, <code>webp</code>. The <code>getDataUrl</code> function returns a data URL &#8212; useful for preview thumbnails or embedding.</p><h3><strong>Theme-Aware Colors</strong></h3><p>Both foreground and background colors integrate with the Mantine theme:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;tsx&quot;,&quot;nodeId&quot;:&quot;bd5e6a92-0b00-4b0f-83ef-aa49273a7e9b&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-tsx">&lt;QRCode value="https://mantine.dev" color="blue" background="gray.1" /&gt;
&lt;QRCode value="https://mantine.dev" color="white" background="dark" /&gt;
&lt;QRCode value="https://mantine.dev" color="teal" background="transparent" /&gt;
</code></pre></div><h3><strong>Full Styles API</strong></h3><p>The component exposes 8 style selectors for granular customization:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!kFW1!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F525e3e4f-38fb-4baf-b7fa-44388afcdfe0_1064x874.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!kFW1!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F525e3e4f-38fb-4baf-b7fa-44388afcdfe0_1064x874.png 424w, https://substackcdn.com/image/fetch/$s_!kFW1!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F525e3e4f-38fb-4baf-b7fa-44388afcdfe0_1064x874.png 848w, https://substackcdn.com/image/fetch/$s_!kFW1!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F525e3e4f-38fb-4baf-b7fa-44388afcdfe0_1064x874.png 1272w, https://substackcdn.com/image/fetch/$s_!kFW1!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F525e3e4f-38fb-4baf-b7fa-44388afcdfe0_1064x874.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!kFW1!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F525e3e4f-38fb-4baf-b7fa-44388afcdfe0_1064x874.png" width="1064" height="874" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/525e3e4f-38fb-4baf-b7fa-44388afcdfe0_1064x874.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:874,&quot;width&quot;:1064,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:120495,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.undolog.com/i/191453687?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F525e3e4f-38fb-4baf-b7fa-44388afcdfe0_1064x874.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!kFW1!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F525e3e4f-38fb-4baf-b7fa-44388afcdfe0_1064x874.png 424w, https://substackcdn.com/image/fetch/$s_!kFW1!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F525e3e4f-38fb-4baf-b7fa-44388afcdfe0_1064x874.png 848w, https://substackcdn.com/image/fetch/$s_!kFW1!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F525e3e4f-38fb-4baf-b7fa-44388afcdfe0_1064x874.png 1272w, https://substackcdn.com/image/fetch/$s_!kFW1!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F525e3e4f-38fb-4baf-b7fa-44388afcdfe0_1064x874.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p></p><h3><strong>CSS Variables</strong></h3><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!8mIA!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff2b4ee84-b586-452c-9f59-abab65ee8755_958x490.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!8mIA!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff2b4ee84-b586-452c-9f59-abab65ee8755_958x490.png 424w, https://substackcdn.com/image/fetch/$s_!8mIA!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff2b4ee84-b586-452c-9f59-abab65ee8755_958x490.png 848w, https://substackcdn.com/image/fetch/$s_!8mIA!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff2b4ee84-b586-452c-9f59-abab65ee8755_958x490.png 1272w, https://substackcdn.com/image/fetch/$s_!8mIA!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff2b4ee84-b586-452c-9f59-abab65ee8755_958x490.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!8mIA!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff2b4ee84-b586-452c-9f59-abab65ee8755_958x490.png" width="958" height="490" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f2b4ee84-b586-452c-9f59-abab65ee8755_958x490.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:490,&quot;width&quot;:958,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:60050,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.undolog.com/i/191453687?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff2b4ee84-b586-452c-9f59-abab65ee8755_958x490.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!8mIA!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff2b4ee84-b586-452c-9f59-abab65ee8755_958x490.png 424w, https://substackcdn.com/image/fetch/$s_!8mIA!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff2b4ee84-b586-452c-9f59-abab65ee8755_958x490.png 848w, https://substackcdn.com/image/fetch/$s_!8mIA!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff2b4ee84-b586-452c-9f59-abab65ee8755_958x490.png 1272w, https://substackcdn.com/image/fetch/$s_!8mIA!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff2b4ee84-b586-452c-9f59-abab65ee8755_958x490.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Size presets: <code>xs</code> (80px), <code>sm</code> (120px), <code>md</code> (160px), <code>lg</code> (200px), <code>xl</code> (256px).</p><h2><strong>&#128295; Improvements</strong></h2><ul><li><p><strong>SVG Accessibility</strong>: The SVG element includes <code>role="img"</code> and an <code>aria-label</code> derived from the encoded value.</p></li><li><p><strong>Optimized Rendering</strong>: All data modules are combined into a single <code>&lt;path&gt;</code> element, keeping the DOM minimal regardless of QR code density.</p></li><li><p><strong>Memoized Computation</strong>: QR matrix generation and SVG path building are wrapped in <code>useMemo</code> &#8212; re-computation only happens when relevant props change.</p></li><li><p><strong>SSR Compatible</strong>: No browser-only APIs (<code>window</code>, <code>document</code>) are used during rendering. The download hook uses browser APIs only when explicitly called.</p></li></ul><h2><strong>&#128214; Complete Props Reference</strong></h2><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;typescript&quot;,&quot;nodeId&quot;:&quot;3c6fa61c-1ea8-46b0-977a-8ecfecc9b1db&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-typescript">interface QRCodeBaseProps {
  value: string;                              // Required &#8212; data to encode
  size?: MantineSize | string | number;       // Default: 'md'
  radius?: MantineRadius | string | number;   // Container border radius
  color?: MantineColor;                       // Default: 'dark'
  background?: MantineColor | 'transparent';  // Default: 'white'
  errorCorrectionLevel?: 'L' | 'M' | 'Q' | 'H'; // Default: 'M'
  quietZone?: number;                         // Default: 1
  dotStyle?: 'square' | 'rounded' | 'dots';  // Default: 'square'
  cornerStyle?: 'square' | 'rounded' | 'dots'; // Default: 'square'
  image?: string;                             // Center image URL
  imageSize?: number;                         // 0&#8211;1, default: 0.2
  imageRadius?: MantineRadius | string | number;
  imagePadding?: number;                      // Default: 1
  imageExcavate?: boolean;                    // Default: true
}
</code></pre></div><h2><strong>Getting Started</strong></h2><p>Install the package:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;bash&quot;,&quot;nodeId&quot;:&quot;c656daeb-1f20-478d-adc8-b3c63394f4b3&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-bash">npm install @gfazioli/mantine-qr-code
</code></pre></div><p>Import styles at the root of your application:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;typescript&quot;,&quot;nodeId&quot;:&quot;8ca57580-405c-4928-96fe-f924de128d5a&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-typescript">import '@gfazioli/mantine-qr-code/styles.css';
</code></pre></div><p>Use the component:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;tsx&quot;,&quot;nodeId&quot;:&quot;03c6918d-4563-4283-bc29-57d534ea29f4&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-tsx">import { QRCode } from '@gfazioli/mantine-qr-code';

function App() {
  return (
    &lt;QRCode
      value="https://mantine.dev"
      size="lg"
      dotStyle="rounded"
      cornerStyle="rounded"
      color="blue"
    /&gt;
  );
}
</code></pre></div><h2><strong>Use Cases</strong></h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!pQqw!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F23ab72cf-f9ef-46da-bdca-69aa88d6987e_800x1061.webp" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!pQqw!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F23ab72cf-f9ef-46da-bdca-69aa88d6987e_800x1061.webp 424w, https://substackcdn.com/image/fetch/$s_!pQqw!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F23ab72cf-f9ef-46da-bdca-69aa88d6987e_800x1061.webp 848w, https://substackcdn.com/image/fetch/$s_!pQqw!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F23ab72cf-f9ef-46da-bdca-69aa88d6987e_800x1061.webp 1272w, https://substackcdn.com/image/fetch/$s_!pQqw!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F23ab72cf-f9ef-46da-bdca-69aa88d6987e_800x1061.webp 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!pQqw!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F23ab72cf-f9ef-46da-bdca-69aa88d6987e_800x1061.webp" width="800" height="1061" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/23ab72cf-f9ef-46da-bdca-69aa88d6987e_800x1061.webp&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1061,&quot;width&quot;:800,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Use Cases&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Use Cases" title="Use Cases" srcset="https://substackcdn.com/image/fetch/$s_!pQqw!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F23ab72cf-f9ef-46da-bdca-69aa88d6987e_800x1061.webp 424w, https://substackcdn.com/image/fetch/$s_!pQqw!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F23ab72cf-f9ef-46da-bdca-69aa88d6987e_800x1061.webp 848w, https://substackcdn.com/image/fetch/$s_!pQqw!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F23ab72cf-f9ef-46da-bdca-69aa88d6987e_800x1061.webp 1272w, https://substackcdn.com/image/fetch/$s_!pQqw!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F23ab72cf-f9ef-46da-bdca-69aa88d6987e_800x1061.webp 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;tsx&quot;,&quot;nodeId&quot;:&quot;9d5affd8-1bcb-4832-875a-d4adcd64a474&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-tsx">{/* URL */}
&lt;QRCode value="https://mantine.dev" /&gt;

{/* WiFi credentials */}
&lt;QRCode value="WIFI:T:WPA;S:MyNetwork;P:MyPassword;;" /&gt;

{/* vCard contact */}
&lt;QRCode value="BEGIN:VCARD\nVERSION:3.0\nFN:John Doe\nTEL:+1234567890\nEND:VCARD" /&gt;
</code></pre></div><h2><strong>Links</strong></h2><ul><li><p>&#128230; <a href="https://www.npmjs.com/package/@gfazioli/mantine-qr-code">npm package</a></p></li><li><p>&#128214; <a href="https://gfazioli.github.io/mantine-qr-code/">Documentation &amp; Demos</a></p></li><li><p>&#128279; <a href="https://github.com/gfazioli/mantine-qr-code">GitHub Repository</a></p></li><li><p>&#129513; <a href="https://mantine-extensions.vercel.app/">More Mantine Extensions</a></p></li></ul>]]></content:encoded></item><item><title><![CDATA[Mantine Scene - Cinematic Backgrounds for Your React Apps]]></title><description><![CDATA[Layer gradients, glowing blobs, star fields, auroras, and more into rich, composable decorative backgrounds &#8212; all powered by Mantine.]]></description><link>https://www.undolog.com/p/mantine-scene-cinematic-backgrounds</link><guid isPermaLink="false">https://www.undolog.com/p/mantine-scene-cinematic-backgrounds</guid><dc:creator><![CDATA[Giovambattista Fazioli]]></dc:creator><pubDate>Thu, 19 Mar 2026 08:08:31 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!sgYr!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6ab31680-08c1-4306-9ae1-9f8803ff7c3c_1280x640.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!sgYr!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6ab31680-08c1-4306-9ae1-9f8803ff7c3c_1280x640.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!sgYr!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6ab31680-08c1-4306-9ae1-9f8803ff7c3c_1280x640.jpeg 424w, https://substackcdn.com/image/fetch/$s_!sgYr!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6ab31680-08c1-4306-9ae1-9f8803ff7c3c_1280x640.jpeg 848w, https://substackcdn.com/image/fetch/$s_!sgYr!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6ab31680-08c1-4306-9ae1-9f8803ff7c3c_1280x640.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!sgYr!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6ab31680-08c1-4306-9ae1-9f8803ff7c3c_1280x640.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!sgYr!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6ab31680-08c1-4306-9ae1-9f8803ff7c3c_1280x640.jpeg" width="1280" height="640" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/6ab31680-08c1-4306-9ae1-9f8803ff7c3c_1280x640.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:640,&quot;width&quot;:1280,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:151641,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/jpeg&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://www.undolog.com/i/191452945?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6ab31680-08c1-4306-9ae1-9f8803ff7c3c_1280x640.jpeg&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!sgYr!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6ab31680-08c1-4306-9ae1-9f8803ff7c3c_1280x640.jpeg 424w, https://substackcdn.com/image/fetch/$s_!sgYr!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6ab31680-08c1-4306-9ae1-9f8803ff7c3c_1280x640.jpeg 848w, https://substackcdn.com/image/fetch/$s_!sgYr!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6ab31680-08c1-4306-9ae1-9f8803ff7c3c_1280x640.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!sgYr!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6ab31680-08c1-4306-9ae1-9f8803ff7c3c_1280x640.jpeg 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2><strong>Introduction</strong></h2><p><strong>Mantine Scene</strong> is a brand-new composable background component for React that lets you build atmospheric, animated backgrounds by stacking visual layers. Think of it as a stage director for your UI &#8212; you compose the mood by combining gradients, glow effects, dot grids, star fields, snowfall, auroras, and more, all with first-class Mantine theme integration, responsive props, and accessibility baked in from day one.</p><p>This 1.0.0 release ships with <strong>10 composable sub-components</strong>, full interactive mouse-tracking support, GPU-accelerated animations, and a complete documentation site with live configurators for every layer type.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://www.undolog.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Undolog is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><h2><strong>What&#8217;s New in 1.0.0</strong></h2><h3><strong>The Scene Component</strong></h3><p>The root <code>Scene</code> component acts as a container for all visual layers. It supports fullscreen mode, interactive mouse tracking, and respects <code>prefers-reduced-motion</code> out of the box.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;tsx&quot;,&quot;nodeId&quot;:&quot;58fb5ff3-50e9-40fe-bc6b-c41eccf96d8a&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-tsx">import { Scene } from '@gfazioli/mantine-scene';

function HeroBackground() {
  return (
    &lt;Scene fullscreen interactive&gt;
      &lt;Scene.Gradient from="violet" to="transparent" animate /&gt;
      &lt;Scene.Glow color="blue" size={300} /&gt;
      &lt;Scene.StarField count={80} twinkle /&gt;
      &lt;Scene.Noise opacity={0.03} /&gt;
    &lt;/Scene&gt;
  );
}
</code></pre></div><p><strong>Root Props:</strong></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!qH1c!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F88150931-719a-41e8-82f9-070618afc9d9_1516x770.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!qH1c!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F88150931-719a-41e8-82f9-070618afc9d9_1516x770.png 424w, https://substackcdn.com/image/fetch/$s_!qH1c!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F88150931-719a-41e8-82f9-070618afc9d9_1516x770.png 848w, https://substackcdn.com/image/fetch/$s_!qH1c!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F88150931-719a-41e8-82f9-070618afc9d9_1516x770.png 1272w, https://substackcdn.com/image/fetch/$s_!qH1c!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F88150931-719a-41e8-82f9-070618afc9d9_1516x770.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!qH1c!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F88150931-719a-41e8-82f9-070618afc9d9_1516x770.png" width="1456" height="740" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/88150931-719a-41e8-82f9-070618afc9d9_1516x770.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:740,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:126115,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.undolog.com/i/191452945?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F88150931-719a-41e8-82f9-070618afc9d9_1516x770.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!qH1c!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F88150931-719a-41e8-82f9-070618afc9d9_1516x770.png 424w, https://substackcdn.com/image/fetch/$s_!qH1c!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F88150931-719a-41e8-82f9-070618afc9d9_1516x770.png 848w, https://substackcdn.com/image/fetch/$s_!qH1c!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F88150931-719a-41e8-82f9-070618afc9d9_1516x770.png 1272w, https://substackcdn.com/image/fetch/$s_!qH1c!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F88150931-719a-41e8-82f9-070618afc9d9_1516x770.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p></p><h3><strong>10 Composable Sub-Components</strong></h3><p>Every sub-component is a visual layer you can freely compose. Layer order follows DOM order &#8212; the last child renders on top.</p><div><hr></div><h4><strong>&#10024; Scene.Gradient</strong></h4><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!CWVd!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5be62a10-7966-4531-b564-476b5dc156c4_800x1323.webp" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!CWVd!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5be62a10-7966-4531-b564-476b5dc156c4_800x1323.webp 424w, https://substackcdn.com/image/fetch/$s_!CWVd!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5be62a10-7966-4531-b564-476b5dc156c4_800x1323.webp 848w, https://substackcdn.com/image/fetch/$s_!CWVd!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5be62a10-7966-4531-b564-476b5dc156c4_800x1323.webp 1272w, https://substackcdn.com/image/fetch/$s_!CWVd!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5be62a10-7966-4531-b564-476b5dc156c4_800x1323.webp 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!CWVd!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5be62a10-7966-4531-b564-476b5dc156c4_800x1323.webp" width="800" height="1323" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/5be62a10-7966-4531-b564-476b5dc156c4_800x1323.webp&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1323,&quot;width&quot;:800,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Gradient&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Gradient" title="Gradient" srcset="https://substackcdn.com/image/fetch/$s_!CWVd!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5be62a10-7966-4531-b564-476b5dc156c4_800x1323.webp 424w, https://substackcdn.com/image/fetch/$s_!CWVd!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5be62a10-7966-4531-b564-476b5dc156c4_800x1323.webp 848w, https://substackcdn.com/image/fetch/$s_!CWVd!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5be62a10-7966-4531-b564-476b5dc156c4_800x1323.webp 1272w, https://substackcdn.com/image/fetch/$s_!CWVd!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5be62a10-7966-4531-b564-476b5dc156c4_800x1323.webp 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Radial, linear, or conic gradients with optional rotate or pulse animation. Supports interactive mouse tracking for radial/conic types.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;tsx&quot;,&quot;nodeId&quot;:&quot;88c45b7f-e877-405b-abb3-30f9e5189eb2&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-tsx">&lt;Scene.Gradient
  type="radial"
  from="violet"
  to="transparent"
  fromOpacity={0.2}
  animate
  animationType="rotate"
  duration={20}
/&gt;
</code></pre></div><div><hr></div><h4><strong>&#10024; Scene.Glow</strong></h4><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!XslC!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F14847110-d097-49cf-99cd-e7b1cf87f645_800x1275.webp" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!XslC!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F14847110-d097-49cf-99cd-e7b1cf87f645_800x1275.webp 424w, https://substackcdn.com/image/fetch/$s_!XslC!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F14847110-d097-49cf-99cd-e7b1cf87f645_800x1275.webp 848w, https://substackcdn.com/image/fetch/$s_!XslC!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F14847110-d097-49cf-99cd-e7b1cf87f645_800x1275.webp 1272w, https://substackcdn.com/image/fetch/$s_!XslC!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F14847110-d097-49cf-99cd-e7b1cf87f645_800x1275.webp 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!XslC!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F14847110-d097-49cf-99cd-e7b1cf87f645_800x1275.webp" width="800" height="1275" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/14847110-d097-49cf-99cd-e7b1cf87f645_800x1275.webp&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1275,&quot;width&quot;:800,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Glow&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Glow" title="Glow" srcset="https://substackcdn.com/image/fetch/$s_!XslC!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F14847110-d097-49cf-99cd-e7b1cf87f645_800x1275.webp 424w, https://substackcdn.com/image/fetch/$s_!XslC!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F14847110-d097-49cf-99cd-e7b1cf87f645_800x1275.webp 848w, https://substackcdn.com/image/fetch/$s_!XslC!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F14847110-d097-49cf-99cd-e7b1cf87f645_800x1275.webp 1272w, https://substackcdn.com/image/fetch/$s_!XslC!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F14847110-d097-49cf-99cd-e7b1cf87f645_800x1275.webp 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Floating, animated glow blobs. Choose between <code>float</code>, <code>pulse</code>, or <code>breathe</code> animation styles. Supports <strong>responsive </strong><code>size</code><strong> and </strong><code>blur</code> props and interactive mouse tracking.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;tsx&quot;,&quot;nodeId&quot;:&quot;f469c99d-5fd8-42df-b0b2-eeb81dd8562e&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-tsx">&lt;Scene.Glow
  color="cyan"
  size={{ base: 200, md: 400 }}
  blur={{ base: 80, md: 120 }}
  opacity={0.5}
  animate
  animationType="float"
  driftX="30px"
  driftY="20px"
/&gt;
</code></pre></div><div><hr></div><h4><strong>&#10024; Scene.DotGrid</strong></h4><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!4DWR!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd5346686-562d-4efc-b0af-f5d136a0cb06_800x893.webp" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!4DWR!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd5346686-562d-4efc-b0af-f5d136a0cb06_800x893.webp 424w, https://substackcdn.com/image/fetch/$s_!4DWR!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd5346686-562d-4efc-b0af-f5d136a0cb06_800x893.webp 848w, https://substackcdn.com/image/fetch/$s_!4DWR!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd5346686-562d-4efc-b0af-f5d136a0cb06_800x893.webp 1272w, https://substackcdn.com/image/fetch/$s_!4DWR!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd5346686-562d-4efc-b0af-f5d136a0cb06_800x893.webp 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!4DWR!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd5346686-562d-4efc-b0af-f5d136a0cb06_800x893.webp" width="800" height="893" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d5346686-562d-4efc-b0af-f5d136a0cb06_800x893.webp&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:893,&quot;width&quot;:800,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;DotGrid&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="DotGrid" title="DotGrid" srcset="https://substackcdn.com/image/fetch/$s_!4DWR!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd5346686-562d-4efc-b0af-f5d136a0cb06_800x893.webp 424w, https://substackcdn.com/image/fetch/$s_!4DWR!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd5346686-562d-4efc-b0af-f5d136a0cb06_800x893.webp 848w, https://substackcdn.com/image/fetch/$s_!4DWR!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd5346686-562d-4efc-b0af-f5d136a0cb06_800x893.webp 1272w, https://substackcdn.com/image/fetch/$s_!4DWR!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd5346686-562d-4efc-b0af-f5d136a0cb06_800x893.webp 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Repeating dot patterns with optional stagger (honeycomb) layout, fade masks, and twinkle animation. Supports <strong>responsive </strong><code>spacing</code>.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;tsx&quot;,&quot;nodeId&quot;:&quot;806e4466-a9d8-4b39-9a2e-f3073f2596b1&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-tsx">&lt;Scene.DotGrid
  color="gray"
  spacing={{ base: 16, md: 24 }}
  stagger
  fade="radial"
  animate
/&gt;
</code></pre></div><div><hr></div><h4><strong>&#10024; Scene.Mesh</strong></h4><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!ox6a!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F593b6c73-85ad-45c3-a1e6-55e1db967665_800x904.webp" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!ox6a!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F593b6c73-85ad-45c3-a1e6-55e1db967665_800x904.webp 424w, https://substackcdn.com/image/fetch/$s_!ox6a!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F593b6c73-85ad-45c3-a1e6-55e1db967665_800x904.webp 848w, https://substackcdn.com/image/fetch/$s_!ox6a!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F593b6c73-85ad-45c3-a1e6-55e1db967665_800x904.webp 1272w, https://substackcdn.com/image/fetch/$s_!ox6a!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F593b6c73-85ad-45c3-a1e6-55e1db967665_800x904.webp 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!ox6a!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F593b6c73-85ad-45c3-a1e6-55e1db967665_800x904.webp" width="800" height="904" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/593b6c73-85ad-45c3-a1e6-55e1db967665_800x904.webp&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:904,&quot;width&quot;:800,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Mesh&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Mesh" title="Mesh" srcset="https://substackcdn.com/image/fetch/$s_!ox6a!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F593b6c73-85ad-45c3-a1e6-55e1db967665_800x904.webp 424w, https://substackcdn.com/image/fetch/$s_!ox6a!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F593b6c73-85ad-45c3-a1e6-55e1db967665_800x904.webp 848w, https://substackcdn.com/image/fetch/$s_!ox6a!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F593b6c73-85ad-45c3-a1e6-55e1db967665_800x904.webp 1272w, https://substackcdn.com/image/fetch/$s_!ox6a!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F593b6c73-85ad-45c3-a1e6-55e1db967665_800x904.webp 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Multi-stop radial gradient overlay simulating a mesh gradient. Supports hue-rotate animation and blend modes.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;tsx&quot;,&quot;nodeId&quot;:&quot;91d8850e-74bf-4066-bafd-5be7fa16db49&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-tsx">&lt;Scene.Mesh
  stops={[
    { color: 'violet', position: '20% 30%', spread: 50 },
    { color: 'pink', position: '80% 20%', spread: 40 },
    { color: 'blue', position: '50% 80%', spread: 60 },
  ]}
  animate
  blend="screen"
/&gt;
</code></pre></div><div><hr></div><h4><strong>&#10024; Scene.Noise</strong></h4><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!XJ-N!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6620dc45-5ece-43ba-a222-392fd3684dd3_800x875.webp" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!XJ-N!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6620dc45-5ece-43ba-a222-392fd3684dd3_800x875.webp 424w, https://substackcdn.com/image/fetch/$s_!XJ-N!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6620dc45-5ece-43ba-a222-392fd3684dd3_800x875.webp 848w, https://substackcdn.com/image/fetch/$s_!XJ-N!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6620dc45-5ece-43ba-a222-392fd3684dd3_800x875.webp 1272w, https://substackcdn.com/image/fetch/$s_!XJ-N!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6620dc45-5ece-43ba-a222-392fd3684dd3_800x875.webp 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!XJ-N!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6620dc45-5ece-43ba-a222-392fd3684dd3_800x875.webp" width="800" height="875" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/6620dc45-5ece-43ba-a222-392fd3684dd3_800x875.webp&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:875,&quot;width&quot;:800,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Noise&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Noise" title="Noise" srcset="https://substackcdn.com/image/fetch/$s_!XJ-N!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6620dc45-5ece-43ba-a222-392fd3684dd3_800x875.webp 424w, https://substackcdn.com/image/fetch/$s_!XJ-N!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6620dc45-5ece-43ba-a222-392fd3684dd3_800x875.webp 848w, https://substackcdn.com/image/fetch/$s_!XJ-N!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6620dc45-5ece-43ba-a222-392fd3684dd3_800x875.webp 1272w, https://substackcdn.com/image/fetch/$s_!XJ-N!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6620dc45-5ece-43ba-a222-392fd3684dd3_800x875.webp 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>SVG-based film grain texture. Add subtle texture to any background with configurable grain, seed, and color tint.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;tsx&quot;,&quot;nodeId&quot;:&quot;9034bd1e-e2b4-46db-91d9-c1cf4ea3f14e&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-tsx">&lt;Scene.Noise opacity={0.03} grain={0.65} type="fractalNoise" tint="blue" /&gt;
</code></pre></div><div><hr></div><h4><strong>&#10024; Scene.StarField</strong></h4><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!a2Lw!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F90eb4ed0-46e7-4927-b7bf-2d630ea72438_800x927.webp" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!a2Lw!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F90eb4ed0-46e7-4927-b7bf-2d630ea72438_800x927.webp 424w, https://substackcdn.com/image/fetch/$s_!a2Lw!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F90eb4ed0-46e7-4927-b7bf-2d630ea72438_800x927.webp 848w, https://substackcdn.com/image/fetch/$s_!a2Lw!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F90eb4ed0-46e7-4927-b7bf-2d630ea72438_800x927.webp 1272w, https://substackcdn.com/image/fetch/$s_!a2Lw!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F90eb4ed0-46e7-4927-b7bf-2d630ea72438_800x927.webp 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!a2Lw!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F90eb4ed0-46e7-4927-b7bf-2d630ea72438_800x927.webp" width="800" height="927" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/90eb4ed0-46e7-4927-b7bf-2d630ea72438_800x927.webp&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:927,&quot;width&quot;:800,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;StarField&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="StarField" title="StarField" srcset="https://substackcdn.com/image/fetch/$s_!a2Lw!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F90eb4ed0-46e7-4927-b7bf-2d630ea72438_800x927.webp 424w, https://substackcdn.com/image/fetch/$s_!a2Lw!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F90eb4ed0-46e7-4927-b7bf-2d630ea72438_800x927.webp 848w, https://substackcdn.com/image/fetch/$s_!a2Lw!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F90eb4ed0-46e7-4927-b7bf-2d630ea72438_800x927.webp 1272w, https://substackcdn.com/image/fetch/$s_!a2Lw!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F90eb4ed0-46e7-4927-b7bf-2d630ea72438_800x927.webp 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>CSS-only star field with deterministic PRNG positioning and twinkle animation across 3 staggered layers. Supports <strong>responsive </strong><code>count</code>.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;tsx&quot;,&quot;nodeId&quot;:&quot;57b6f8b5-f9d8-4e1c-abdb-f1a394a7acd1&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-tsx">&lt;Scene.StarField count={{ base: 50, md: 100 }} twinkle duration={3} seed={42} /&gt;
</code></pre></div><div><hr></div><h4><strong>&#10024; Scene.StarWarp</strong></h4><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!bsmK!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F08b46d75-a1f6-48a5-a140-78e702425f1b_800x1215.webp" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!bsmK!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F08b46d75-a1f6-48a5-a140-78e702425f1b_800x1215.webp 424w, https://substackcdn.com/image/fetch/$s_!bsmK!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F08b46d75-a1f6-48a5-a140-78e702425f1b_800x1215.webp 848w, https://substackcdn.com/image/fetch/$s_!bsmK!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F08b46d75-a1f6-48a5-a140-78e702425f1b_800x1215.webp 1272w, https://substackcdn.com/image/fetch/$s_!bsmK!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F08b46d75-a1f6-48a5-a140-78e702425f1b_800x1215.webp 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!bsmK!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F08b46d75-a1f6-48a5-a140-78e702425f1b_800x1215.webp" width="800" height="1215" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/08b46d75-a1f6-48a5-a140-78e702425f1b_800x1215.webp&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1215,&quot;width&quot;:800,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;StarWarp&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="StarWarp" title="StarWarp" srcset="https://substackcdn.com/image/fetch/$s_!bsmK!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F08b46d75-a1f6-48a5-a140-78e702425f1b_800x1215.webp 424w, https://substackcdn.com/image/fetch/$s_!bsmK!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F08b46d75-a1f6-48a5-a140-78e702425f1b_800x1215.webp 848w, https://substackcdn.com/image/fetch/$s_!bsmK!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F08b46d75-a1f6-48a5-a140-78e702425f1b_800x1215.webp 1272w, https://substackcdn.com/image/fetch/$s_!bsmK!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F08b46d75-a1f6-48a5-a140-78e702425f1b_800x1215.webp 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Hyperspace warp-speed effect with configurable focal point, direction, and streak trails. Supports <strong>responsive </strong><code>count</code> and interactive mouse tracking for the focal point.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;tsx&quot;,&quot;nodeId&quot;:&quot;b6acad96-d065-4510-a94e-4fdaaac663dd&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-tsx">&lt;Scene.StarWarp
  count={100}
  speed={1.5}
  direction="out"
  trail
  color="white"
/&gt;
</code></pre></div><div><hr></div><h4><strong>&#10024; Scene.ShootingStar</strong></h4><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!iyAP!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff9c1e508-6710-4fef-a7b4-44d3537e1ead_800x1001.webp" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!iyAP!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff9c1e508-6710-4fef-a7b4-44d3537e1ead_800x1001.webp 424w, https://substackcdn.com/image/fetch/$s_!iyAP!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff9c1e508-6710-4fef-a7b4-44d3537e1ead_800x1001.webp 848w, https://substackcdn.com/image/fetch/$s_!iyAP!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff9c1e508-6710-4fef-a7b4-44d3537e1ead_800x1001.webp 1272w, https://substackcdn.com/image/fetch/$s_!iyAP!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff9c1e508-6710-4fef-a7b4-44d3537e1ead_800x1001.webp 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!iyAP!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff9c1e508-6710-4fef-a7b4-44d3537e1ead_800x1001.webp" width="800" height="1001" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f9c1e508-6710-4fef-a7b4-44d3537e1ead_800x1001.webp&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1001,&quot;width&quot;:800,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;ShootingStar&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="ShootingStar" title="ShootingStar" srcset="https://substackcdn.com/image/fetch/$s_!iyAP!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff9c1e508-6710-4fef-a7b4-44d3537e1ead_800x1001.webp 424w, https://substackcdn.com/image/fetch/$s_!iyAP!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff9c1e508-6710-4fef-a7b4-44d3537e1ead_800x1001.webp 848w, https://substackcdn.com/image/fetch/$s_!iyAP!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff9c1e508-6710-4fef-a7b4-44d3537e1ead_800x1001.webp 1272w, https://substackcdn.com/image/fetch/$s_!iyAP!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff9c1e508-6710-4fef-a7b4-44d3537e1ead_800x1001.webp 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Animated shooting star trails at configurable angles and intervals. Multiple lanes with randomized timing for a natural feel.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;tsx&quot;,&quot;nodeId&quot;:&quot;1ff775d7-1b6b-4811-aed5-6cbf08d274c1&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-tsx">&lt;Scene.ShootingStar
  count={3}
  angle={215}
  trailLength={80}
  speed={1}
  minInterval={3}
  maxInterval={8}
/&gt;
</code></pre></div><div><hr></div><h4><strong>&#10024; Scene.Snow</strong></h4><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!iS19!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9bba55dd-2ca5-40ef-b837-57911ee115c4_800x945.webp" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!iS19!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9bba55dd-2ca5-40ef-b837-57911ee115c4_800x945.webp 424w, https://substackcdn.com/image/fetch/$s_!iS19!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9bba55dd-2ca5-40ef-b837-57911ee115c4_800x945.webp 848w, https://substackcdn.com/image/fetch/$s_!iS19!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9bba55dd-2ca5-40ef-b837-57911ee115c4_800x945.webp 1272w, https://substackcdn.com/image/fetch/$s_!iS19!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9bba55dd-2ca5-40ef-b837-57911ee115c4_800x945.webp 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!iS19!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9bba55dd-2ca5-40ef-b837-57911ee115c4_800x945.webp" width="800" height="945" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/9bba55dd-2ca5-40ef-b837-57911ee115c4_800x945.webp&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:945,&quot;width&quot;:800,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Snow&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Snow" title="Snow" srcset="https://substackcdn.com/image/fetch/$s_!iS19!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9bba55dd-2ca5-40ef-b837-57911ee115c4_800x945.webp 424w, https://substackcdn.com/image/fetch/$s_!iS19!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9bba55dd-2ca5-40ef-b837-57911ee115c4_800x945.webp 848w, https://substackcdn.com/image/fetch/$s_!iS19!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9bba55dd-2ca5-40ef-b837-57911ee115c4_800x945.webp 1272w, https://substackcdn.com/image/fetch/$s_!iS19!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9bba55dd-2ca5-40ef-b837-57911ee115c4_800x945.webp 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Falling snowflakes with horizontal drift and wind control. Supports <strong>responsive </strong><code>count</code> for performance-conscious responsive designs.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;tsx&quot;,&quot;nodeId&quot;:&quot;3b73851a-5600-410b-80a9-9917093d0bb4&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-tsx">&lt;Scene.Snow
  count={{ base: 30, md: 50 }}
  speed={1}
  drift={30}
  wind={0.2}
/&gt;
</code></pre></div><div><hr></div><h4><strong>&#10024; Scene.Aurora</strong></h4><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!qito!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F20378b12-9721-4df4-bcf9-1e53de93a6b9_800x794.webp" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!qito!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F20378b12-9721-4df4-bcf9-1e53de93a6b9_800x794.webp 424w, https://substackcdn.com/image/fetch/$s_!qito!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F20378b12-9721-4df4-bcf9-1e53de93a6b9_800x794.webp 848w, https://substackcdn.com/image/fetch/$s_!qito!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F20378b12-9721-4df4-bcf9-1e53de93a6b9_800x794.webp 1272w, https://substackcdn.com/image/fetch/$s_!qito!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F20378b12-9721-4df4-bcf9-1e53de93a6b9_800x794.webp 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!qito!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F20378b12-9721-4df4-bcf9-1e53de93a6b9_800x794.webp" width="800" height="794" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/20378b12-9721-4df4-bcf9-1e53de93a6b9_800x794.webp&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:794,&quot;width&quot;:800,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Aurora&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Aurora" title="Aurora" srcset="https://substackcdn.com/image/fetch/$s_!qito!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F20378b12-9721-4df4-bcf9-1e53de93a6b9_800x794.webp 424w, https://substackcdn.com/image/fetch/$s_!qito!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F20378b12-9721-4df4-bcf9-1e53de93a6b9_800x794.webp 848w, https://substackcdn.com/image/fetch/$s_!qito!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F20378b12-9721-4df4-bcf9-1e53de93a6b9_800x794.webp 1272w, https://substackcdn.com/image/fetch/$s_!qito!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F20378b12-9721-4df4-bcf9-1e53de93a6b9_800x794.webp 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Shimmering aurora borealis bands with wave animation. Configurable band count, position, and colors.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;tsx&quot;,&quot;nodeId&quot;:&quot;2403a8d6-4253-4d54-8032-9cf3f11dc655&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-tsx">&lt;Scene.Aurora
  colors={['green', 'teal', 'cyan']}
  bands={3}
  position="top"
  opacity={0.3}
  blur={60}
/&gt;
</code></pre></div><h2><strong>Key Features</strong></h2><h3><strong>&#127912; Full Mantine Theme Integration</strong></h3><p>Every color prop accepts <code>MantineColor</code> values, so your backgrounds stay consistent with your theme. Shade overrides are available on components like <code>Glow</code> and <code>DotGrid</code>.</p><h3><strong>&#128241; Responsive Props</strong></h3><p>Key numeric props support Mantine&#8217;s responsive object syntax. This means you can adjust visual density based on breakpoint &#8212; fewer stars on mobile, larger glows on desktop:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;tsx&quot;,&quot;nodeId&quot;:&quot;633eb206-02e0-4244-b23f-db1c7c08e5da&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-tsx">&lt;Scene.StarField count={{ base: 40, sm: 60, md: 100 }} /&gt;
&lt;Scene.Glow size={{ base: 150, md: 400 }} blur={{ base: 60, md: 120 }} /&gt;
</code></pre></div><p>Responsive props are available on: <code>Glow</code> (size, blur), <code>DotGrid</code> (spacing), <code>StarField</code> (count), <code>StarWarp</code> (count), <code>Snow</code> (count).</p><h3><strong>&#128433;&#65039; Interactive Mouse Tracking</strong></h3><p>Set <code>interactive</code> on the root <code>Scene</code> to enable cursor-driven effects:</p><ul><li><p><strong>Glow</strong> &#8212; blob position follows the mouse</p></li><li><p><strong>Gradient</strong> &#8212; radial/conic center follows the mouse</p></li><li><p><strong>StarWarp</strong> &#8212; focal point tracks the mouse</p></li></ul><p>The <code>interactiveEasing</code> prop controls how smoothly elements follow the cursor (0 = sluggish, 1 = instant). Components gracefully fall back to their default positions when the mouse leaves.</p><h3><strong>&#9855; Accessibility</strong></h3><ul><li><p>The root component renders with <code>aria-hidden="true"</code> &#8212; it&#8217;s purely decorative</p></li><li><p><code>reducedMotion="auto"</code> respects <code>prefers-reduced-motion</code> by default</p></li><li><p>Set <code>reducedMotion="always"</code> to disable all animations, or <code>"never"</code> to force them on</p></li></ul><h3><strong>&#9889; Performance</strong></h3><ul><li><p>All animations use GPU-accelerated CSS (<code>transform</code>, <code>opacity</code>) &#8212; no JavaScript animation loops</p></li><li><p>Deterministic PRNG (seeded) for StarField, StarWarp, ShootingStar, and Snow ensures reproducible layouts without runtime randomness</p></li><li><p>StarWarp caps at 200 elements; Snow recommends ~80 max for smooth performance</p></li><li><p>Responsive <code>count</code> props let you reduce particle counts on lower-powered devices</p></li></ul><h3><strong>&#127899;&#65039; Styles API</strong></h3><p>Full Mantine Styles API support. Override styles for any part of any sub-component using <code>classNames</code>, <code>styles</code>, or <code>unstyled</code> props. CSS variables are exposed for all dynamic values (e.g., <code>--scene-glow-size</code>, <code>--scene-aurora-duration</code>).</p><h2><strong>Putting It All Together</strong></h2><p>Here&#8217;s a complete example combining multiple layers into a rich hero background:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;tsx&quot;,&quot;nodeId&quot;:&quot;6943d778-b126-49ad-840d-56b164e73350&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-tsx">import { Scene } from '@gfazioli/mantine-scene';

function CosmicHero() {
  return (
    &lt;Scene fullscreen interactive interactiveEasing={0.08}&gt;
      {/* Base gradient */}
      &lt;Scene.Gradient type="radial" from="indigo" fromOpacity={0.2} animate /&gt;

      {/* Mesh overlay */}
      &lt;Scene.Mesh
        stops={[
          { color: 'violet', position: '25% 25%' },
          { color: 'blue', position: '75% 75%' },
        ]}
        blend="screen"
        opacity={0.6}
      /&gt;

      {/* Floating glows */}
      &lt;Scene.Glow color="cyan" size={350} top="20%" left="30%" /&gt;
      &lt;Scene.Glow color="violet" size={250} top="60%" left="70%" delay={2} /&gt;

      {/* Stars */}
      &lt;Scene.StarField count={{ base: 60, md: 120 }} twinkle /&gt;
      &lt;Scene.ShootingStar count={2} /&gt;

      {/* Subtle texture */}
      &lt;Scene.Noise opacity={0.02} /&gt;
    &lt;/Scene&gt;
  );
}
</code></pre></div><h2><strong>Getting Started</strong></h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!WKew!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F77475c17-73d1-4284-b2a6-34f7f437a56f_800x1183.webp" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!WKew!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F77475c17-73d1-4284-b2a6-34f7f437a56f_800x1183.webp 424w, https://substackcdn.com/image/fetch/$s_!WKew!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F77475c17-73d1-4284-b2a6-34f7f437a56f_800x1183.webp 848w, https://substackcdn.com/image/fetch/$s_!WKew!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F77475c17-73d1-4284-b2a6-34f7f437a56f_800x1183.webp 1272w, https://substackcdn.com/image/fetch/$s_!WKew!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F77475c17-73d1-4284-b2a6-34f7f437a56f_800x1183.webp 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!WKew!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F77475c17-73d1-4284-b2a6-34f7f437a56f_800x1183.webp" width="800" height="1183" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/77475c17-73d1-4284-b2a6-34f7f437a56f_800x1183.webp&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1183,&quot;width&quot;:800,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Combined&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Combined" title="Combined" srcset="https://substackcdn.com/image/fetch/$s_!WKew!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F77475c17-73d1-4284-b2a6-34f7f437a56f_800x1183.webp 424w, https://substackcdn.com/image/fetch/$s_!WKew!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F77475c17-73d1-4284-b2a6-34f7f437a56f_800x1183.webp 848w, https://substackcdn.com/image/fetch/$s_!WKew!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F77475c17-73d1-4284-b2a6-34f7f437a56f_800x1183.webp 1272w, https://substackcdn.com/image/fetch/$s_!WKew!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F77475c17-73d1-4284-b2a6-34f7f437a56f_800x1183.webp 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h3><strong>Installation</strong></h3><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;bash&quot;,&quot;nodeId&quot;:&quot;a4c8d005-1f31-474a-b220-9aa227a16bae&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-bash">npm install @gfazioli/mantine-scene
# or
yarn add @gfazioli/mantine-scene
</code></pre></div><h3><strong>Import Styles</strong></h3><p>Add the styles import to your app entry point:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;typescript&quot;,&quot;nodeId&quot;:&quot;f43d48b8-6602-4e6a-81e1-4c34e015ac3f&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-typescript">import '@gfazioli/mantine-scene/styles.css';
</code></pre></div><p>Or use the <code>@layer</code> variant for CSS cascade control:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;typescript&quot;,&quot;nodeId&quot;:&quot;a8a97c10-88c2-4703-942c-e669366ea861&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-typescript">import '@gfazioli/mantine-scene/styles.layer.css';
</code></pre></div><h3><strong>Requirements</strong></h3><ul><li><p>React 18 or 19</p></li><li><p>@mantine/core &gt;= 7.0.0</p></li><li><p>@mantine/hooks &gt;= 7.0.0</p></li></ul><h2><strong>Links</strong></h2><ul><li><p>&#128230; <a href="https://www.npmjs.com/package/@gfazioli/mantine-scene">npm package</a></p></li><li><p>&#128214; <a href="https://gfazioli.github.io/mantine-scene/">Documentation &amp; Live Demos</a></p></li><li><p>&#128279; <a href="https://github.com/gfazioli/mantine-scene">GitHub Repository</a></p></li><li><p>&#127760; <a href="https://mantine-extensions.vercel.app/">Mantine Extensions Hub</a></p></li></ul><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://www.undolog.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Undolog is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[Mantine Text Animate v3 - The Text Animation Toolkit Gets a Major Upgrade]]></title><description><![CDATA[Five new compound components, loop animations, viewport triggers, sound effects, and full accessibility support &#8212; all in one release.]]></description><link>https://www.undolog.com/p/mantine-text-animate-v3-the-text</link><guid isPermaLink="false">https://www.undolog.com/p/mantine-text-animate-v3-the-text</guid><dc:creator><![CDATA[Giovambattista Fazioli]]></dc:creator><pubDate>Sun, 15 Mar 2026 11:15:03 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!2AMF!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F45678e8c-e6dc-4e98-964a-a7aca308a907_1280x640.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!2AMF!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F45678e8c-e6dc-4e98-964a-a7aca308a907_1280x640.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!2AMF!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F45678e8c-e6dc-4e98-964a-a7aca308a907_1280x640.jpeg 424w, https://substackcdn.com/image/fetch/$s_!2AMF!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F45678e8c-e6dc-4e98-964a-a7aca308a907_1280x640.jpeg 848w, https://substackcdn.com/image/fetch/$s_!2AMF!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F45678e8c-e6dc-4e98-964a-a7aca308a907_1280x640.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!2AMF!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F45678e8c-e6dc-4e98-964a-a7aca308a907_1280x640.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!2AMF!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F45678e8c-e6dc-4e98-964a-a7aca308a907_1280x640.jpeg" width="1280" height="640" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/45678e8c-e6dc-4e98-964a-a7aca308a907_1280x640.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:640,&quot;width&quot;:1280,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:218740,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/jpeg&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://www.undolog.com/i/190853009?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F45678e8c-e6dc-4e98-964a-a7aca308a907_1280x640.jpeg&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!2AMF!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F45678e8c-e6dc-4e98-964a-a7aca308a907_1280x640.jpeg 424w, https://substackcdn.com/image/fetch/$s_!2AMF!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F45678e8c-e6dc-4e98-964a-a7aca308a907_1280x640.jpeg 848w, https://substackcdn.com/image/fetch/$s_!2AMF!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F45678e8c-e6dc-4e98-964a-a7aca308a907_1280x640.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!2AMF!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F45678e8c-e6dc-4e98-964a-a7aca308a907_1280x640.jpeg 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2><strong>Introduction</strong></h2><p><a href="https://gfazioli.github.io/mantine-text-animate/">Version 3.0.0</a> of <code>@gfazioli/mantine-text-animate</code> is the biggest release yet. It doubles the number of compound components from five to ten, introduces powerful new animation modes like looping and viewport-triggered animations, and brings first-class accessibility with <code>prefers-reduced-motion</code> support across every component. Whether you need a retro airport departure board, a smooth text morphing transition, or an animated gradient heading, this release has you covered.</p><h2><strong>What&#8217;s New</strong></h2><h3><strong>Five New Compound Components</strong></h3><h4><strong>TextAnimate.Gradient &#8212; Animated Gradient Text</strong></h4><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!zVil!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0a2d16db-0f0f-4cbb-88ad-06780e8c31a0_800x1125.webp" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!zVil!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0a2d16db-0f0f-4cbb-88ad-06780e8c31a0_800x1125.webp 424w, https://substackcdn.com/image/fetch/$s_!zVil!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0a2d16db-0f0f-4cbb-88ad-06780e8c31a0_800x1125.webp 848w, https://substackcdn.com/image/fetch/$s_!zVil!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0a2d16db-0f0f-4cbb-88ad-06780e8c31a0_800x1125.webp 1272w, https://substackcdn.com/image/fetch/$s_!zVil!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0a2d16db-0f0f-4cbb-88ad-06780e8c31a0_800x1125.webp 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!zVil!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0a2d16db-0f0f-4cbb-88ad-06780e8c31a0_800x1125.webp" width="800" height="1125" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/0a2d16db-0f0f-4cbb-88ad-06780e8c31a0_800x1125.webp&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1125,&quot;width&quot;:800,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Gradient&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Gradient" title="Gradient" srcset="https://substackcdn.com/image/fetch/$s_!zVil!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0a2d16db-0f0f-4cbb-88ad-06780e8c31a0_800x1125.webp 424w, https://substackcdn.com/image/fetch/$s_!zVil!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0a2d16db-0f0f-4cbb-88ad-06780e8c31a0_800x1125.webp 848w, https://substackcdn.com/image/fetch/$s_!zVil!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0a2d16db-0f0f-4cbb-88ad-06780e8c31a0_800x1125.webp 1272w, https://substackcdn.com/image/fetch/$s_!zVil!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0a2d16db-0f0f-4cbb-88ad-06780e8c31a0_800x1125.webp 1456w" sizes="100vw"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Apply an animated gradient effect to any text, with full support for Mantine theme colors. Choose from 8 animation directions and pass your own color palette.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;tsx&quot;,&quot;nodeId&quot;:&quot;9d4e79d6-4e38-42c4-b371-fa599be8bcab&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-tsx">&lt;TextAnimate.Gradient colors={['blue', 'cyan', 'green']} speed={1.5} direction="to-right"&gt;
  Gradient Text
&lt;/TextAnimate.Gradient&gt;</code></pre></div><p>The component resolves Mantine color tokens like <code>'blue.5'</code> via <code>parseThemeColor</code>, so your gradients stay consistent with your theme.</p><h4><strong>TextAnimate.Highlight &#8212; Animated Highlighter Marker</strong></h4><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!kg-3!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2f7d8946-68ee-47dd-b3a5-6a589a1127e2_800x834.webp" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!kg-3!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2f7d8946-68ee-47dd-b3a5-6a589a1127e2_800x834.webp 424w, https://substackcdn.com/image/fetch/$s_!kg-3!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2f7d8946-68ee-47dd-b3a5-6a589a1127e2_800x834.webp 848w, https://substackcdn.com/image/fetch/$s_!kg-3!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2f7d8946-68ee-47dd-b3a5-6a589a1127e2_800x834.webp 1272w, https://substackcdn.com/image/fetch/$s_!kg-3!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2f7d8946-68ee-47dd-b3a5-6a589a1127e2_800x834.webp 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!kg-3!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2f7d8946-68ee-47dd-b3a5-6a589a1127e2_800x834.webp" width="800" height="834" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/2f7d8946-68ee-47dd-b3a5-6a589a1127e2_800x834.webp&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:834,&quot;width&quot;:800,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Highlight&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Highlight" title="Highlight" srcset="https://substackcdn.com/image/fetch/$s_!kg-3!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2f7d8946-68ee-47dd-b3a5-6a589a1127e2_800x834.webp 424w, https://substackcdn.com/image/fetch/$s_!kg-3!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2f7d8946-68ee-47dd-b3a5-6a589a1127e2_800x834.webp 848w, https://substackcdn.com/image/fetch/$s_!kg-3!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2f7d8946-68ee-47dd-b3a5-6a589a1127e2_800x834.webp 1272w, https://substackcdn.com/image/fetch/$s_!kg-3!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2f7d8946-68ee-47dd-b3a5-6a589a1127e2_800x834.webp 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>A CSS-only highlighter effect that sweeps a colored marker across your text. No JavaScript animation loop needed &#8212; it&#8217;s all CSS keyframes.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;tsx&quot;,&quot;nodeId&quot;:&quot;a40a2e8d-95f6-4429-8dc7-c8d65c999c46&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-tsx">&lt;TextAnimate.Highlight color="yellow" highlightHeight="40%" speed={1}&gt;
  Important Text
&lt;/TextAnimate.Highlight&gt;</code></pre></div><h4><strong>TextAnimate.SplitFlap &#8212; Airport Departure Board</strong></h4><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!zPWz!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F85d2fd9f-c988-428d-99af-cadce8cde1e0_800x1172.webp" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!zPWz!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F85d2fd9f-c988-428d-99af-cadce8cde1e0_800x1172.webp 424w, https://substackcdn.com/image/fetch/$s_!zPWz!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F85d2fd9f-c988-428d-99af-cadce8cde1e0_800x1172.webp 848w, https://substackcdn.com/image/fetch/$s_!zPWz!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F85d2fd9f-c988-428d-99af-cadce8cde1e0_800x1172.webp 1272w, https://substackcdn.com/image/fetch/$s_!zPWz!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F85d2fd9f-c988-428d-99af-cadce8cde1e0_800x1172.webp 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!zPWz!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F85d2fd9f-c988-428d-99af-cadce8cde1e0_800x1172.webp" width="800" height="1172" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/85d2fd9f-c988-428d-99af-cadce8cde1e0_800x1172.webp&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1172,&quot;width&quot;:800,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;SplitFlap&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="SplitFlap" title="SplitFlap" srcset="https://substackcdn.com/image/fetch/$s_!zPWz!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F85d2fd9f-c988-428d-99af-cadce8cde1e0_800x1172.webp 424w, https://substackcdn.com/image/fetch/$s_!zPWz!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F85d2fd9f-c988-428d-99af-cadce8cde1e0_800x1172.webp 848w, https://substackcdn.com/image/fetch/$s_!zPWz!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F85d2fd9f-c988-428d-99af-cadce8cde1e0_800x1172.webp 1272w, https://substackcdn.com/image/fetch/$s_!zPWz!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F85d2fd9f-c988-428d-99af-cadce8cde1e0_800x1172.webp 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Inspired by the classic Solari boards found in train stations and airports, this component flips through a character set with 3D CSS perspective transforms until each character reaches its target.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;tsx&quot;,&quot;nodeId&quot;:&quot;af195205-76cf-4c0a-89b9-a25d2cfe26a6&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-tsx">&lt;TextAnimate.SplitFlap value="DEPARTING 14:30" speed={1.5} /&gt;</code></pre></div><p>Each character animates independently with configurable stagger timing. Customize the <code>characterSet</code>, <code>flipDuration</code>, <code>radius</code>, and <code>dividerColor</code>. The companion <code>useSplitFlap</code> hook gives you full programmatic control.</p><h4><strong>TextAnimate.Morphing &#8212; Fluid Text Transitions</strong></h4><p>Smoothly morph between two strings. The component uses the Longest Common Subsequence (LCS) algorithm to identify shared characters &#8212; they stay in place while new characters fade in and old ones fade out.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;tsx&quot;,&quot;nodeId&quot;:&quot;ea662c7f-7c6b-46c1-95dc-f386751e841d&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-tsx">const [text, setText] = useState('Hello World');

&lt;TextAnimate.Morphing value={text} speed={1} /&gt;
&lt;Button onClick={() =&gt; setText('Hello Mantine')}&gt;Change&lt;/Button&gt;</code></pre></div><h4><strong>TextAnimate.RotatingText &#8212; Text Carousel</strong></h4><p>Cycle through an array of strings with animated transitions. Six built-in transition styles: <code>slideUp</code>, <code>slideDown</code>, <code>fade</code>, <code>blur</code>, <code>blurUp</code>, <code>blurDown</code>.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;tsx&quot;,&quot;nodeId&quot;:&quot;6a186213-0c50-4bff-8bbf-3bc5ece9cdf2&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-tsx">&lt;Text&gt;
  I speak{' '}
  &lt;TextAnimate.RotatingText
    values={['English', 'French', 'Italian', 'Spanish']}
    animation="blurUp"
    interval={3000}
  /&gt;
&lt;/Text&gt;</code></pre></div><p>The component renders inline (<code>&lt;span&gt;</code>) so it flows naturally within a sentence. The <code>useRotatingText</code> hook is available for headless usage.</p><div><hr></div><h3><strong>Loop Mode</strong></h3><p>The base <code>TextAnimate</code> component now supports continuous looping. Set <code>animate="loop"</code> and the animation will cycle: animate in, pause, animate out, pause, and repeat.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;tsx&quot;,&quot;nodeId&quot;:&quot;f369e0a3-355a-4af7-8a2e-8a98ebec7cd7&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-tsx">&lt;TextAnimate animate="loop" animation="blurDown" loopDelay={2000}&gt;
  This text loops forever
&lt;/TextAnimate&gt;</code></pre></div><p>The <code>loopDelay</code> prop controls the pause between phases (default: 2000ms).</p><h3><strong>Viewport-Triggered Animations</strong></h3><p>Delay animations until the element scrolls into view using IntersectionObserver:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;tsx&quot;,&quot;nodeId&quot;:&quot;96f2b994-44bf-4359-ae94-70dfe795693c&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-tsx">&lt;TextAnimate trigger="inView" triggerOptions={{ threshold: 0.5 }}&gt;
  I animate when you can see me
&lt;/TextAnimate&gt;</code></pre></div><p>Three trigger modes are available: <code>"mount"</code> (default, same as v2), <code>"inView"</code>, and <code>"manual"</code> for full programmatic control.</p><h3><strong>The </strong><code>useTextAnimate</code><strong> Hook</strong></h3><p>A new hook for controlling TextAnimate from the outside:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;tsx&quot;,&quot;nodeId&quot;:&quot;28668ec3-c62f-4940-84c0-93a262a47e15&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-tsx">const { animate, setAnimate, replay, isAnimating, key, onAnimationComplete } = useTextAnimate();

&lt;TextAnimate animate={animate} key={key} onAnimationComplete={onAnimationComplete}&gt;
  Controlled animation
&lt;/TextAnimate&gt;

&lt;Button onClick={replay}&gt;Replay&lt;/Button&gt;
&lt;Button onClick={() =&gt; setAnimate('out')}&gt;Animate Out&lt;/Button&gt;</code></pre></div><h3><code>onAnimationComplete</code><strong> Callback</strong></h3><p>Unlike <code>onAnimationEnd</code> (which fires per segment), <code>onAnimationComplete</code> fires once when <strong>all</strong> segments have finished animating &#8212; essential for orchestrating sequential animations.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;tsx&quot;,&quot;nodeId&quot;:&quot;9c03fa77-d2aa-4a7c-a4ad-683cc0c5d7e1&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-tsx">&lt;TextAnimate
  by="character"
  onAnimationComplete={(direction) =&gt; {
    if (direction === 'in') showNextElement();
  }}
&gt;
  Staggered characters
&lt;/TextAnimate&gt;</code></pre></div><h3><strong>TextTicker Scramble Mode</strong></h3><p>The existing TextTicker component gains a new &#8220;hacker terminal&#8221; mode. Set <code>scrambleDuration</code> to make each character cycle through random characters for a fixed duration before settling on the target.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;tsx&quot;,&quot;nodeId&quot;:&quot;614548ee-2be0-4ed1-a1bd-4c7acd40bc58&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-tsx">&lt;TextAnimate.TextTicker
  value="ACCESS GRANTED"
  scrambleDuration={800}
  staggerDelay={50}
  revealDirection="left-to-right"
/&gt;</code></pre></div><h3><strong>Typewriter Enhancements</strong></h3><p>Three new capabilities for the Typewriter component:</p><p><strong>Sound effects</strong> &#8212; Synthesized mechanical keyboard clicks via Web Audio API:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;tsx&quot;,&quot;nodeId&quot;:&quot;d6d9eeef-30d9-4e8b-8fc4-0226e08e6e56&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-tsx">&lt;TextAnimate.Typewriter value="Click clack..." withSound soundVolume={0.3} /&gt;</code></pre></div><p><strong>Per-character callbacks</strong> &#8212; React to each character as it&#8217;s typed:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;tsx&quot;,&quot;nodeId&quot;:&quot;6a29bd94-d73b-4c57-94ad-2234b58f6d6a&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-tsx">&lt;TextAnimate.Typewriter
  value="Hello"
  onCharType={(char, index) =&gt; console.log(`Typed: ${char}`)}
/&gt;</code></pre></div><p><strong>Custom pauses</strong> &#8212; Insert dramatic pauses at specific character positions:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;tsx&quot;,&quot;nodeId&quot;:&quot;490954fd-c057-41e1-8dcf-a1087c876153&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-tsx">&lt;TextAnimate.Typewriter
  value="Hello, world!"
  pauseAt={{ 5: 1000, 6: 500 }}  // Pause 1s after comma, 500ms after space
/&gt;</code></pre></div><h3><strong>NumberTicker Custom Formatting</strong></h3><p>Override the default <code>Intl.NumberFormat</code> with your own formatting function:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;tsx&quot;,&quot;nodeId&quot;:&quot;94bdaf26-9da5-4f96-b530-e2ee1f18267f&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-tsx">&lt;TextAnimate.NumberTicker
  value={1234.56}
  formatValue={(v) =&gt; `$${v.toFixed(2)}`}
/&gt;</code></pre></div><div><hr></div><h2><strong>Breaking Changes</strong></h2><h3><code>speed</code><strong> Prop Semantics Standardized</strong></h3><p>The <code>speed</code> prop now works as a <strong>multiplier</strong> on all components: <code>speed={1}</code> is normal speed, <code>speed={2}</code> is twice as fast. This is a breaking change for <strong>Typewriter</strong>, <strong>Gradient</strong>, and <strong>Spinner</strong>, which previously used different units.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!cRgU!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1ee00032-ab07-46ce-baa8-c5ab0c5547ce_1514x576.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!cRgU!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1ee00032-ab07-46ce-baa8-c5ab0c5547ce_1514x576.png 424w, https://substackcdn.com/image/fetch/$s_!cRgU!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1ee00032-ab07-46ce-baa8-c5ab0c5547ce_1514x576.png 848w, https://substackcdn.com/image/fetch/$s_!cRgU!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1ee00032-ab07-46ce-baa8-c5ab0c5547ce_1514x576.png 1272w, https://substackcdn.com/image/fetch/$s_!cRgU!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1ee00032-ab07-46ce-baa8-c5ab0c5547ce_1514x576.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!cRgU!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1ee00032-ab07-46ce-baa8-c5ab0c5547ce_1514x576.png" width="1456" height="554" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/1ee00032-ab07-46ce-baa8-c5ab0c5547ce_1514x576.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:554,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:103145,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.undolog.com/i/190853009?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1ee00032-ab07-46ce-baa8-c5ab0c5547ce_1514x576.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!cRgU!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1ee00032-ab07-46ce-baa8-c5ab0c5547ce_1514x576.png 424w, https://substackcdn.com/image/fetch/$s_!cRgU!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1ee00032-ab07-46ce-baa8-c5ab0c5547ce_1514x576.png 848w, https://substackcdn.com/image/fetch/$s_!cRgU!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1ee00032-ab07-46ce-baa8-c5ab0c5547ce_1514x576.png 1272w, https://substackcdn.com/image/fetch/$s_!cRgU!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1ee00032-ab07-46ce-baa8-c5ab0c5547ce_1514x576.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;tsx&quot;,&quot;nodeId&quot;:&quot;838e88c7-3a78-40aa-baf7-0e8fc77c5be3&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-tsx">// Before (v2)
&lt;TextAnimate.Typewriter value="Hello" speed={0.03} /&gt;
&lt;TextAnimate.Gradient speed={3}&gt;Hello&lt;/TextAnimate.Gradient&gt;
&lt;TextAnimate.Spinner speed={10}&gt;Hello&lt;/TextAnimate.Spinner&gt;

// After (v3)
&lt;TextAnimate.Typewriter value="Hello" speed={1} /&gt;
&lt;TextAnimate.Gradient speed={1}&gt;Hello&lt;/TextAnimate.Gradient&gt;
&lt;TextAnimate.Spinner speed={2}&gt;Hello&lt;/TextAnimate.Spinner&gt;</code></pre></div><h3><strong>Spinner Children Type Expanded</strong></h3><p><code>Spinner</code> now accepts <code>string | ReactNode[]</code>. If you relied on TypeScript enforcing <code>children: string</code>, the type is now wider. Existing string usage works identically.</p><div><hr></div><h2><strong>Improvements</strong></h2><h3><strong>Full Accessibility Support</strong></h3><p>Every component now respects <code>prefers-reduced-motion</code>:</p><ul><li><p><strong>TextAnimate</strong> detects reduced motion in JS to properly fire <code>onAnimationComplete</code> and advance loop phases even when CSS animations are disabled</p></li><li><p><strong>Typewriter</strong> shows the final text immediately</p></li><li><p><strong>NumberTicker</strong> shows the target value instantly</p></li><li><p><strong>SplitFlap</strong> displays the target text without flipping</p></li></ul><p>ARIA attributes have been added throughout: <code>aria-live="polite"</code> on TextAnimate hidden/static states, automatic <code>role="img"</code> and <code>aria-label</code> on Spinner for string children.</p><h3><strong>Safety and Robustness</strong></h3><ul><li><p>All <code>speed</code> divisions are clamped to a minimum of <code>0.1</code>, preventing <code>Infinity</code> or <code>NaN</code> timeouts</p></li><li><p>AudioContext creation/resume is wrapped in try/catch for restricted environments</p></li><li><p>Loop timer accumulation is prevented by clearing previous timers</p></li><li><p>NumberTicker uses epsilon-based float comparison to reliably detect animation completion</p></li></ul><h3><strong>Test Coverage</strong></h3><p>Expanded from 1 test to <strong>42 tests</strong> across 5 suites, covering rendering, props, ARIA attributes, animation directions, text splitting, trigger modes, and more.</p><div><hr></div><h2><strong>Getting Started</strong></h2><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;bash&quot;,&quot;nodeId&quot;:&quot;c3da6e03-a8d3-4c09-811e-31a5bdff08f6&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-bash"># Install or update
npm install @gfazioli/mantine-text-animate@3

# or with yarn
yarn add @gfazioli/mantine-text-animate@3</code></pre></div><p>All new components are accessed as compound components on <code>TextAnimate</code>:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;tsx&quot;,&quot;nodeId&quot;:&quot;359d31d6-c6c2-49d7-842c-fca89b0d2bfb&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-tsx">import { TextAnimate } from '@gfazioli/mantine-text-animate';

// New components
&lt;TextAnimate.Gradient&gt;...&lt;/TextAnimate.Gradient&gt;
&lt;TextAnimate.Highlight&gt;...&lt;/TextAnimate.Highlight&gt;
&lt;TextAnimate.SplitFlap value="..." /&gt;
&lt;TextAnimate.Morphing value="..." /&gt;
&lt;TextAnimate.RotatingText values={[...]} /&gt;</code></pre></div><p>Hooks are exported directly:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;typescript&quot;,&quot;nodeId&quot;:&quot;cd90025a-cfc8-41d0-a33b-5fab9b9f1527&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-typescript">import {
  useTextAnimate,
  useSplitFlap,
  useMorphing,
  useRotatingText,
} from '@gfazioli/mantine-text-animate';</code></pre></div><h2><strong>Links</strong></h2><ul><li><p>npm: <a href="https://www.npmjs.com/package/@gfazioli/mantine-text-animate">npmjs.com/package/@gfazioli/mantine-text-animate</a></p></li><li><p>Documentation: <a href="https://gfazioli.github.io/mantine-text-animate/">gfazioli.github.io/mantine-text-animate</a></p></li><li><p>GitHub: <a href="https://github.com/gfazioli/mantine-text-animate">github.com/gfazioli/mantine-text-animate</a></p></li><li><p>Mantine Extensions Hub: <a href="https://mantine-extensions.vercel.app/">mantine-extensions.vercel.app</a></p></li></ul>]]></content:encoded></item><item><title><![CDATA[Mantine Onboarding Tour v3 - Responsive by Design, Smoother Than Ever]]></title><description><![CDATA[Responsive popover positioning, granular lifecycle callbacks, persistent overlay, and type-safe step definitions &#8212; all in one major release.]]></description><link>https://www.undolog.com/p/mantine-onboarding-tour-v3-responsive</link><guid isPermaLink="false">https://www.undolog.com/p/mantine-onboarding-tour-v3-responsive</guid><dc:creator><![CDATA[Giovambattista Fazioli]]></dc:creator><pubDate>Mon, 09 Mar 2026 17:26:08 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!1Rnx!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F38905fb8-252a-41a8-adf4-9ae54181a52f_1280x640.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!1Rnx!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F38905fb8-252a-41a8-adf4-9ae54181a52f_1280x640.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!1Rnx!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F38905fb8-252a-41a8-adf4-9ae54181a52f_1280x640.jpeg 424w, https://substackcdn.com/image/fetch/$s_!1Rnx!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F38905fb8-252a-41a8-adf4-9ae54181a52f_1280x640.jpeg 848w, https://substackcdn.com/image/fetch/$s_!1Rnx!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F38905fb8-252a-41a8-adf4-9ae54181a52f_1280x640.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!1Rnx!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F38905fb8-252a-41a8-adf4-9ae54181a52f_1280x640.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!1Rnx!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F38905fb8-252a-41a8-adf4-9ae54181a52f_1280x640.jpeg" width="1280" height="640" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/38905fb8-252a-41a8-adf4-9ae54181a52f_1280x640.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:640,&quot;width&quot;:1280,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:237599,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/jpeg&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://www.undolog.com/i/190414202?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F38905fb8-252a-41a8-adf4-9ae54181a52f_1280x640.jpeg&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!1Rnx!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F38905fb8-252a-41a8-adf4-9ae54181a52f_1280x640.jpeg 424w, https://substackcdn.com/image/fetch/$s_!1Rnx!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F38905fb8-252a-41a8-adf4-9ae54181a52f_1280x640.jpeg 848w, https://substackcdn.com/image/fetch/$s_!1Rnx!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F38905fb8-252a-41a8-adf4-9ae54181a52f_1280x640.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!1Rnx!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F38905fb8-252a-41a8-adf4-9ae54181a52f_1280x640.jpeg 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2><strong>Introduction</strong></h2><p><a href="https://gfazioli.github.io/mantine-onboarding-tour/">Version 3.0.0</a> of <code>@gfazioli/mantine-onboarding-tour</code> is a major overhaul that makes guided tours feel polished on every screen size. The responsive system has been completely redesigned around Mantine&#8217;s <code>useMatches()</code> pattern, transitions between steps are now seamless, and the API is cleaner and fully type-safe. If you&#8217;re building onboarding experiences with Mantine 8, this release is a significant step forward.</p><h2><strong>What&#8217;s New</strong></h2><h3><strong>Responsive Popover Positioning</strong></h3><p>The old <code>responsive</code> / <code>mobileBreakpoint</code> / <code>mobilePosition</code> toggle has been replaced by something far more powerful: responsive objects on popover props. The <code>position</code>, <code>offset</code>, <code>width</code>, and <code>arrowSize</code> properties now accept breakpoint-to-value maps &#8212; the same pattern Mantine uses throughout its ecosystem.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;tsx&quot;,&quot;nodeId&quot;:&quot;77d2d760-dd0e-4f8c-8ead-62170488edd9&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-tsx">&lt;OnboardingTour
  focusRevealProps={{
    popoverProps: {
      position: { base: 'bottom', sm: 'right' },
      offset: { base: 8, sm: -4 },
      width: { base: 'target', md: 350 },
    },
  }}
/&gt;</code></pre></div><p>Each step can also define its own responsive overrides:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;typescript&quot;,&quot;nodeId&quot;:&quot;44d4b490-7dbb-4b02-a6d3-fa5167bc823f&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-typescript">const steps: OnboardingTourStep[] = [
  {
    id: 'sidebar',
    title: 'Sidebar Navigation',
    content: 'Explore the menu items here.',
    focusRevealProps: {
      popoverProps: {
        position: { base: 'bottom', sm: 'right', lg: 'left' },
      },
    },
  },
];</code></pre></div><p>On top of that, Floating UI <code>shift</code> and <code>flip</code> middlewares are now enabled by default &#8212; popovers stay within the viewport automatically, no extra configuration needed.</p><h3><strong>Granular Tour Lifecycle Callbacks</strong></h3><p>The single <code>onOnboardingTourClose</code> callback has been replaced with three distinct callbacks that let you react differently to how the tour ends:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;tsx&quot;,&quot;nodeId&quot;:&quot;e229163a-c2b9-4b87-9ac6-9fc1614857c7&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-tsx">&lt;OnboardingTour
  onOnboardingTourComplete={() =&gt; {
    // User finished all steps &#8212; mark as completed
    markTourAsCompleted();
  }}
  onOnboardingTourSkip={() =&gt; {
    // User clicked Skip &#8212; maybe show later
    markTourAsSkipped();
  }}
  onOnboardingTourEnd={() =&gt; {
    // Always fires &#8212; close the UI
    setStarted(false);
  }}
/&gt;</code></pre></div><p>The controller also exposes a new <code>skipTour()</code> method for programmatic use in custom popover content.</p><h3><strong>Persistent Overlay</strong></h3><p>The backdrop overlay is now rendered once at the <code>OnboardingTour</code> level and stays visible across step transitions. This eliminates the flicker that occurred when the overlay would unmount and remount between steps. Each step can customize the overlay appearance (color, opacity, blur) via <code>focusRevealProps.overlayProps</code>, with smooth CSS transitions between them.</p><h3><strong>Type-Safe Custom Step Properties</strong></h3><p>No more <code>[key: string]: any</code>. Custom step properties are now declared via a generic type parameter:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;typescript&quot;,&quot;nodeId&quot;:&quot;13bd0f65-dbfa-4582-9d43-92483ffbe0d9&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-typescript">type MyStep = { icon: string; badge?: string };

const steps: OnboardingTourStep&lt;MyStep&gt;[] = [
  { id: 'step1', title: 'Welcome', icon: 'home', badge: 'New' },
];</code></pre></div><p>The generic flows through <code>OnboardingTourController&lt;T&gt;</code>, so you get full autocomplete and type checking in render functions.</p><h3><strong>Smoother Step Transitions</strong></h3><p>A new two-phase transition mechanism ensures the current popover closes completely before the next one opens. No more flash of the wrong content during navigation.</p><h3><strong>Additional Improvements</strong></h3><ul><li><p><code>focusedZIndex</code><strong> prop</strong> &#8212; Control the z-index of focused elements (default: <code>201</code>)</p></li><li><p><code>factory()</code><strong> pattern</strong> &#8212; <code>OnboardingTour</code> now supports Mantine&#8217;s <code>factory()</code> API for <code>ref</code> forwarding and Styles API integration</p></li><li><p><strong>Scroll centering</strong> &#8212; Target elements are always scrolled to center of the viewport; Floating UI handles the rest</p></li><li><p><strong>No more horizontal scroll</strong> &#8212; <code>overflowX: hidden</code> is applied to <code>&lt;html&gt;</code> while the tour is active</p></li></ul><h2><strong>Breaking Changes</strong></h2><p>This is a major release with several breaking changes. Here&#8217;s a quick overview:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!RnN9!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F364d5c61-ff23-4fd8-90fe-d36b99f85d70_1522x1194.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!RnN9!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F364d5c61-ff23-4fd8-90fe-d36b99f85d70_1522x1194.png 424w, https://substackcdn.com/image/fetch/$s_!RnN9!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F364d5c61-ff23-4fd8-90fe-d36b99f85d70_1522x1194.png 848w, https://substackcdn.com/image/fetch/$s_!RnN9!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F364d5c61-ff23-4fd8-90fe-d36b99f85d70_1522x1194.png 1272w, https://substackcdn.com/image/fetch/$s_!RnN9!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F364d5c61-ff23-4fd8-90fe-d36b99f85d70_1522x1194.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!RnN9!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F364d5c61-ff23-4fd8-90fe-d36b99f85d70_1522x1194.png" width="1456" height="1142" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/364d5c61-ff23-4fd8-90fe-d36b99f85d70_1522x1194.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1142,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:242640,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.undolog.com/i/190414202?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F364d5c61-ff23-4fd8-90fe-d36b99f85d70_1522x1194.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!RnN9!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F364d5c61-ff23-4fd8-90fe-d36b99f85d70_1522x1194.png 424w, https://substackcdn.com/image/fetch/$s_!RnN9!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F364d5c61-ff23-4fd8-90fe-d36b99f85d70_1522x1194.png 848w, https://substackcdn.com/image/fetch/$s_!RnN9!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F364d5c61-ff23-4fd8-90fe-d36b99f85d70_1522x1194.png 1272w, https://substackcdn.com/image/fetch/$s_!RnN9!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F364d5c61-ff23-4fd8-90fe-d36b99f85d70_1522x1194.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2><strong>Migration Guide</strong></h2><h3><strong>1. Replace </strong><code>onOnboardingTourClose</code></h3><p>The simplest migration &#8212; <code>onOnboardingTourEnd</code> is a drop-in replacement:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;tsx&quot;,&quot;nodeId&quot;:&quot;5e51887c-5f85-4ed8-bb30-00098e75462b&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-tsx">// v2
&lt;OnboardingTour onOnboardingTourClose={() =&gt; setStarted(false)} /&gt;

// v3
&lt;OnboardingTour onOnboardingTourEnd={() =&gt; setStarted(false)} /&gt;</code></pre></div><h3><strong>2. Replace responsive props</strong></h3><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;tsx&quot;,&quot;nodeId&quot;:&quot;cef27e3d-e28d-4481-9f1f-08d8c9931b2d&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-tsx">// v2
&lt;OnboardingTour responsive mobileBreakpoint="sm" mobilePosition="bottom" /&gt;

// v3 &#8212; responsive is built-in, no toggle needed
&lt;OnboardingTour
  focusRevealProps={{
    popoverProps: {
      position: { base: 'bottom', sm: 'left' },
    },
  }}
/&gt;</code></pre></div><h3><strong>3. Remove </strong><code>useOnboardingTour</code><strong> import</strong></h3><p>This hook was internal and created isolated state. Use render functions instead:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;tsx&quot;,&quot;nodeId&quot;:&quot;a1c18e4b-3639-4206-ad49-9cb9e28eea8f&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-tsx">// v2
import { useOnboardingTour } from '@gfazioli/mantine-onboarding-tour';

// v3 &#8212; delete the import, use render functions
&lt;OnboardingTour
  content={(controller) =&gt; &lt;div&gt;Step {controller.currentStepIndex}&lt;/div&gt;}
/&gt;</code></pre></div><h3><strong>4. Add generics for custom step properties</strong></h3><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;tsx&quot;,&quot;nodeId&quot;:&quot;b5df9b39-9a83-4d93-ab11-71b4979388bd&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-tsx">// v2
const steps: OnboardingTourStep[] = [
  { id: 's1', price: 9.99 },
];

// v3
const steps: OnboardingTourStep&lt;{ price: number }&gt;[] = [
  { id: 's1', price: 9.99 },
];</code></pre></div><h2><strong>Bug Fixes</strong></h2><ul><li><p><strong>Popover content flash</strong> &#8212; No more brief flash of the next step&#8217;s content inside the current popover during transitions</p></li><li><p><strong>Overlay flickering</strong> &#8212; Persistent overlay eliminates unmount/remount flicker between steps</p></li><li><p><strong>Horizontal scroll</strong> &#8212; Portal-rendered popovers no longer cause horizontal scrollbars</p></li><li><p><strong>Popover behind overlay</strong> &#8212; Popovers now render via portal by default, always appearing above the overlay</p></li><li><p><code>withinPortal</code><strong> was locked</strong> &#8212; <code>popoverProps</code> now accepts the full <code>PopoverProps</code> type</p></li></ul><h2><strong>Links</strong></h2><ul><li><p><strong>npm</strong>: <a href="https://www.npmjs.com/package/@gfazioli/mantine-onboarding-tour">npmjs.com/package/@gfazioli/mantine-onboarding-tour</a></p></li><li><p><strong>Documentation</strong>: <a href="https://gfazioli.github.io/mantine-onboarding-tour/">gfazioli.github.io/mantine-onboarding-tour</a></p></li><li><p><strong>GitHub</strong>: <a href="https://github.com/gfazioli/mantine-onboarding-tour">github.com/gfazioli/mantine-onboarding-tour</a></p></li><li><p><a href="https://mantine-extensions.vercel.app/">Mantine Extensions Hub</a></p></li><li><p><strong>Mantine</strong>: Compatible with Mantine 8.x and React 18/19</p></li></ul>]]></content:encoded></item><item><title><![CDATA[Mantine Marquee v3 - CSS Mask Fade Edges, Responsive Props, and GPU-Accelerated Animations]]></title><description><![CDATA[A new CSS mask-image fade engine, responsive layout controls, and ultra-smooth GPU-accelerated marquee animations.]]></description><link>https://www.undolog.com/p/mantine-marquee-v3-css-mask-fade</link><guid isPermaLink="false">https://www.undolog.com/p/mantine-marquee-v3-css-mask-fade</guid><dc:creator><![CDATA[Giovambattista Fazioli]]></dc:creator><pubDate>Tue, 03 Mar 2026 06:40:18 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!_7mu!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe3d7a0af-c9a9-48af-bae0-fa01eb78da15_1280x640.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!_7mu!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe3d7a0af-c9a9-48af-bae0-fa01eb78da15_1280x640.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!_7mu!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe3d7a0af-c9a9-48af-bae0-fa01eb78da15_1280x640.jpeg 424w, https://substackcdn.com/image/fetch/$s_!_7mu!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe3d7a0af-c9a9-48af-bae0-fa01eb78da15_1280x640.jpeg 848w, https://substackcdn.com/image/fetch/$s_!_7mu!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe3d7a0af-c9a9-48af-bae0-fa01eb78da15_1280x640.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!_7mu!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe3d7a0af-c9a9-48af-bae0-fa01eb78da15_1280x640.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!_7mu!,w_2400,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe3d7a0af-c9a9-48af-bae0-fa01eb78da15_1280x640.jpeg" width="1200" height="600" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e3d7a0af-c9a9-48af-bae0-fa01eb78da15_1280x640.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:false,&quot;imageSize&quot;:&quot;large&quot;,&quot;height&quot;:640,&quot;width&quot;:1280,&quot;resizeWidth&quot;:1200,&quot;bytes&quot;:210514,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/jpeg&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://www.undolog.com/i/189737143?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe3d7a0af-c9a9-48af-bae0-fa01eb78da15_1280x640.jpeg&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:&quot;center&quot;,&quot;offset&quot;:false}" class="sizing-large" alt="" srcset="https://substackcdn.com/image/fetch/$s_!_7mu!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe3d7a0af-c9a9-48af-bae0-fa01eb78da15_1280x640.jpeg 424w, https://substackcdn.com/image/fetch/$s_!_7mu!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe3d7a0af-c9a9-48af-bae0-fa01eb78da15_1280x640.jpeg 848w, https://substackcdn.com/image/fetch/$s_!_7mu!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe3d7a0af-c9a9-48af-bae0-fa01eb78da15_1280x640.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!_7mu!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe3d7a0af-c9a9-48af-bae0-fa01eb78da15_1280x640.jpeg 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><blockquote><p>The fade edges system has been completely rebuilt on CSS <code>mask-image</code> -- background-independent, zero extra DOM nodes, and three distinct mask shapes to choose from.</p></blockquote><h2><strong>Introduction</strong></h2><p>This major release of <code>@gfazioli/mantine-marquee</code> represents the biggest architectural upgrade since the component&#8217;s initial release. The entire edge-fading system has been rewritten from scratch: instead of rendering overlay <code>&lt;div&gt;</code> elements with colored gradients, the component now uses CSS <code>mask-image</code> for true alpha compositing that works on any background -- solid colors, gradients, images, or transparent. On top of that, the <code>vertical</code> and <code>gap</code> props are now fully responsive via Mantine&#8217;s breakpoint system, and several CSS performance fixes ensure silky-smooth animations across all browsers.</p><h2><strong>What&#8217;s New</strong></h2><h3><strong>A New Fade Edges System Built on CSS Masks</strong></h3><p>The <code>fadeEdges</code> prop has evolved from a simple boolean toggle into a union type that lets you choose the exact shape of the fade effect:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;tsx&quot;,&quot;nodeId&quot;:&quot;1fb1909c-0c79-4b65-a1a2-ccf844f5e0b6&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-tsx">// Previously: only boolean
&lt;Marquee fadeEdges&gt;{children}&lt;/Marquee&gt;

// Now: choose your mask shape
&lt;Marquee fadeEdges="linear"&gt;{children}&lt;/Marquee&gt;   // same as fadeEdges={true}
&lt;Marquee fadeEdges="ellipse"&gt;{children}&lt;/Marquee&gt;  // radial vignette
&lt;Marquee fadeEdges="rect"&gt;{children}&lt;/Marquee&gt;     // all 4 edges independently</code></pre></div><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!LQMq!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcf0d706d-77e0-4b5c-b6d1-6f5d85cdfcd8_800x1117.webp" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!LQMq!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcf0d706d-77e0-4b5c-b6d1-6f5d85cdfcd8_800x1117.webp 424w, https://substackcdn.com/image/fetch/$s_!LQMq!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcf0d706d-77e0-4b5c-b6d1-6f5d85cdfcd8_800x1117.webp 848w, https://substackcdn.com/image/fetch/$s_!LQMq!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcf0d706d-77e0-4b5c-b6d1-6f5d85cdfcd8_800x1117.webp 1272w, https://substackcdn.com/image/fetch/$s_!LQMq!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcf0d706d-77e0-4b5c-b6d1-6f5d85cdfcd8_800x1117.webp 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!LQMq!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcf0d706d-77e0-4b5c-b6d1-6f5d85cdfcd8_800x1117.webp" width="800" height="1117" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/cf0d706d-77e0-4b5c-b6d1-6f5d85cdfcd8_800x1117.webp&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1117,&quot;width&quot;:800,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;fadEdges&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="fadEdges" title="fadEdges" srcset="https://substackcdn.com/image/fetch/$s_!LQMq!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcf0d706d-77e0-4b5c-b6d1-6f5d85cdfcd8_800x1117.webp 424w, https://substackcdn.com/image/fetch/$s_!LQMq!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcf0d706d-77e0-4b5c-b6d1-6f5d85cdfcd8_800x1117.webp 848w, https://substackcdn.com/image/fetch/$s_!LQMq!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcf0d706d-77e0-4b5c-b6d1-6f5d85cdfcd8_800x1117.webp 1272w, https://substackcdn.com/image/fetch/$s_!LQMq!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcf0d706d-77e0-4b5c-b6d1-6f5d85cdfcd8_800x1117.webp 1456w" sizes="100vw"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The type signature is:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;typescript&quot;,&quot;nodeId&quot;:&quot;01354f21-5506-4609-9bf7-5a510f1b6d15&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-typescript">type MarqueeFadeEdges = boolean | 'linear' | 'ellipse' | 'rect';</code></pre></div><p>Passing <code>true</code> still works and maps to <code>"linear"</code>, so existing boolean usage is fully backward-compatible.</p><p><strong>Why CSS masks?</strong> The previous overlay-div approach had a fundamental limitation: the fade color had to match the background. If your page background was a gradient, an image, or simply a different color than what you passed to <code>fadeEdgesColor</code>, the fade edges would look wrong. CSS <code>mask-image</code> performs true alpha compositing at the GPU level -- it masks pixels directly, independent of what is behind them.</p><p>Under the hood, the implementation uses <strong>one-sided gradients</strong> (one per edge) composited with <code>mask-composite: intersect</code>. This was a deliberate choice: a single double-sided gradient (<code>transparent -&gt; black -&gt; transparent</code>) breaks when the fade size exceeds 50% of the container, because the gradient stop positions swap and the browser clamps them per the CSS spec, creating a hard visible seam. One-sided gradients can never have overlapping stops, so they work correctly at any size.</p><h4><strong>Linear Mode</strong></h4><p>The classic fade effect: content fades in on the leading edge and fades out on the trailing edge. In vertical mode, the gradients switch from left/right to top/bottom automatically.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;tsx&quot;,&quot;nodeId&quot;:&quot;d1441906-cee6-48b9-a20a-91bd861fc7db&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-tsx">import { Marquee } from '@gfazioli/mantine-marquee';

function Demo() {
  return (
    &lt;Marquee fadeEdges="linear" fadeEdgesSize="sm"&gt;
      &lt;Box bg="blue" p="md" c="white"&gt;Item 1&lt;/Box&gt;
      &lt;Box bg="cyan" p="md" c="white"&gt;Item 2&lt;/Box&gt;
      &lt;Box bg="indigo" p="md" c="white"&gt;Item 3&lt;/Box&gt;
    &lt;/Marquee&gt;
  );
}</code></pre></div><h4><strong>Ellipse Mode</strong></h4><p>A radial vignette that fades all edges simultaneously, creating a spotlight effect. The ellipse uses <code>closest-side</code> sizing so that it adapts naturally to rectangular containers. No orientation variant is needed -- radial gradients are inherently direction-independent.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;tsx&quot;,&quot;nodeId&quot;:&quot;5f333798-2d4d-4ad7-b4e7-dc7218baa9b3&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-tsx">&lt;Marquee fadeEdges="ellipse" fadeEdgesSize="md" pauseOnHover&gt;
  &lt;ThemeIcon variant="transparent" size="120px"&gt;
    &lt;IconBrandGithub style={{ width: '70%', height: '70%' }} /&gt;
  &lt;/ThemeIcon&gt;
  &lt;ThemeIcon variant="transparent" size="120px"&gt;
    &lt;IconBrandMantine style={{ width: '70%', height: '70%' }} /&gt;
  &lt;/ThemeIcon&gt;
  {/* ... */}
&lt;/Marquee&gt;</code></pre></div><p>On a square element (where width equals height), the ellipse naturally produces a circular mask -- no additional props are required.</p><h4><strong>Rect Mode</strong></h4><p>Fades all four edges independently, with separate control over horizontal and vertical fade extent. This is where the new <code>[x, y]</code> tuple support for <code>fadeEdgesSize</code> really shines:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;tsx&quot;,&quot;nodeId&quot;:&quot;f2270c6f-bcab-4bdc-b32f-22903a9a801b&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-tsx">{/* Uniform fade on all 4 edges */}
&lt;Marquee fadeEdges="rect" fadeEdgesSize="md" h={60}&gt;
  {children}
&lt;/Marquee&gt;

{/* More fade on left/right (lg), less on top/bottom (xs) */}
&lt;Marquee fadeEdges="rect" fadeEdgesSize={['lg', 'xs']} h={60}&gt;
  {children}
&lt;/Marquee&gt;</code></pre></div><p>At corners, the alpha values multiply naturally (e.g. 0.5 x 0.5 = 0.25), producing smooth diagonal falloff with no special handling needed.</p><h3><strong>Tuple Support for </strong><code>fadeEdgesSize</code></h3><p>The <code>fadeEdgesSize</code> prop now accepts an <code>[x, y]</code> tuple for independent axis control:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;typescript&quot;,&quot;nodeId&quot;:&quot;9a07821d-daec-41a0-9e11-02ef8bd8aa4d&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-typescript">type MarqueeFadeEdgesSize =
  | MantineSize          // 'xs' | 'sm' | 'md' | 'lg' | 'xl'
  | (string &amp; {})        // any CSS value
  | [x, y];             // tuple: [horizontal, vertical]</code></pre></div><p>A single value applies uniformly to all edges. A tuple lets you set different sizes for horizontal (<code>x</code> = left/right) and vertical (<code>y</code> = top/bottom) fading. The tuple maps to two new CSS custom properties: <code>--marquee-fade-edge-size-x</code> and <code>--marquee-fade-edge-size-y</code>.</p><h3><strong>Responsive </strong><code>vertical</code><strong> Prop</strong></h3><p>The <code>vertical</code> prop now accepts a Mantine breakpoint object, enabling layout that switches between vertical and horizontal scrolling at different viewport widths:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;tsx&quot;,&quot;nodeId&quot;:&quot;53cc17e9-b9f3-4b90-84ad-c0b157aee593&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-tsx">&lt;Marquee vertical={{ base: true, md: false }} h={300} fadeEdges&gt;
  &lt;Box bg="blue" p="md" c="white"&gt;Item 1&lt;/Box&gt;
  &lt;Box bg="cyan" p="md" c="white"&gt;Item 2&lt;/Box&gt;
  &lt;Box bg="indigo" p="md" c="white"&gt;Item 3&lt;/Box&gt;
&lt;/Marquee&gt;</code></pre></div><p>In this example, the marquee scrolls vertically on small screens and switches to horizontal at the <code>md</code> breakpoint. Plain boolean values continue to work as before.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;typescript&quot;,&quot;nodeId&quot;:&quot;9fad857c-fb21-4441-b9d1-d479703978b7&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-typescript">type MarqueeVertical = boolean | Partial&lt;Record&lt;MantineBreakpoint, boolean&gt;&gt;;</code></pre></div><h3><strong>Responsive </strong><code>gap</code><strong> Prop</strong></h3><p>Similarly, the <code>gap</code> prop now accepts a responsive breakpoint object:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;tsx&quot;,&quot;nodeId&quot;:&quot;21c64ba1-4bfe-4ee6-9a43-1e22cccf171e&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-tsx">&lt;Marquee gap={{ base: 'xs', md: 'xl' }} fadeEdges="linear"&gt;
  &lt;Box bg="blue" p="md" c="white"&gt;Item 1&lt;/Box&gt;
  &lt;Box bg="cyan" p="md" c="white"&gt;Item 2&lt;/Box&gt;
  &lt;Box bg="indigo" p="md" c="white"&gt;Item 3&lt;/Box&gt;
&lt;/Marquee&gt;</code></pre></div><p>This gives you tight spacing on mobile and generous spacing on larger screens, without any custom CSS or media queries.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;typescript&quot;,&quot;nodeId&quot;:&quot;539d9c97-eb5b-443d-81cf-a6ddb02452b3&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-typescript">type MarqueeGap =
  | MantineSize
  | (string &amp; {})
  | Partial&lt;Record&lt;MantineBreakpoint, MantineSize | (string &amp; {})&gt;&gt;;</code></pre></div><h3><strong>New Exported Types</strong></h3><p>Four new TypeScript types are exported from the package, making it easier to type your own wrapper components:</p><ul><li><p><code>MarqueeFadeEdges</code> -- the union type for <code>fadeEdges</code></p></li><li><p><code>MarqueeFadeEdgesSize</code> -- the union type (with tuple) for <code>fadeEdgesSize</code></p></li><li><p><code>MarqueeVertical</code> -- the union type for responsive <code>vertical</code></p></li><li><p><code>MarqueeGap</code> -- the union type for responsive <code>gap</code></p></li></ul><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;typescript&quot;,&quot;nodeId&quot;:&quot;a0872c64-6630-40c9-ad0e-be7d2038189c&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-typescript">import type {
  MarqueeFadeEdges,
  MarqueeFadeEdgesSize,
  MarqueeVertical,
  MarqueeGap,
} from '@gfazioli/mantine-marquee';</code></pre></div><h2><strong>Breaking Changes</strong></h2><h3><strong>1. </strong><code>fadeEdgesColor</code><strong> Prop Removed</strong></h3><p>The <code>fadeEdgesColor</code> prop has been completely removed. The new CSS mask system is background-independent -- it does not need to know the background color.</p><p><strong>Before:</strong></p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;tsx&quot;,&quot;nodeId&quot;:&quot;a082348e-0adb-430c-bfb0-efa1021539ee&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-tsx">&lt;Marquee fadeEdges fadeEdgesColor="dark.7"&gt;
  {children}
&lt;/Marquee&gt;</code></pre></div><p><strong>After:</strong></p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;tsx&quot;,&quot;nodeId&quot;:&quot;5e37286a-dc4c-4141-b64a-be58003899ca&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-tsx">&lt;Marquee fadeEdges&gt;
  {children}
&lt;/Marquee&gt;</code></pre></div><p>Simply remove the <code>fadeEdgesColor</code> prop. The fade effect now works correctly on any background.</p><h3><strong>2. </strong><code>fadeEdges</code><strong> Type Changed</strong></h3><p>While <code>fadeEdges={true}</code> and the shorthand <code>fadeEdges</code> continue to work exactly as before, the TypeScript type is now a union. If your wrapper component declares <code>fadeEdges?: boolean</code>, update it:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;typescript&quot;,&quot;nodeId&quot;:&quot;c58b88be-9453-4265-a7f8-9be8aa6e2e69&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-typescript">// Before
fadeEdges?: boolean;

// After
fadeEdges?: MarqueeFadeEdges;
// or inline: boolean | 'linear' | 'ellipse' | 'rect'</code></pre></div><h3><strong>3. CSS Custom Properties Removed from Styles API</strong></h3><p>The following CSS variables have been <strong>removed</strong> from <code>MarqueeCssVariables</code> and are no longer available via the <code>vars</code> prop:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!uKc2!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0e90c258-71a7-4a5d-a8c4-33648a584e3c_1514x576.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!uKc2!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0e90c258-71a7-4a5d-a8c4-33648a584e3c_1514x576.png 424w, https://substackcdn.com/image/fetch/$s_!uKc2!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0e90c258-71a7-4a5d-a8c4-33648a584e3c_1514x576.png 848w, https://substackcdn.com/image/fetch/$s_!uKc2!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0e90c258-71a7-4a5d-a8c4-33648a584e3c_1514x576.png 1272w, https://substackcdn.com/image/fetch/$s_!uKc2!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0e90c258-71a7-4a5d-a8c4-33648a584e3c_1514x576.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!uKc2!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0e90c258-71a7-4a5d-a8c4-33648a584e3c_1514x576.png" width="1456" height="554" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/0e90c258-71a7-4a5d-a8c4-33648a584e3c_1514x576.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:554,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:96227,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.undolog.com/i/189737143?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0e90c258-71a7-4a5d-a8c4-33648a584e3c_1514x576.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!uKc2!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0e90c258-71a7-4a5d-a8c4-33648a584e3c_1514x576.png 424w, https://substackcdn.com/image/fetch/$s_!uKc2!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0e90c258-71a7-4a5d-a8c4-33648a584e3c_1514x576.png 848w, https://substackcdn.com/image/fetch/$s_!uKc2!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0e90c258-71a7-4a5d-a8c4-33648a584e3c_1514x576.png 1272w, https://substackcdn.com/image/fetch/$s_!uKc2!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0e90c258-71a7-4a5d-a8c4-33648a584e3c_1514x576.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>If you were overriding these through the Styles API <code>vars</code> prop, use the component props instead:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;tsx&quot;,&quot;nodeId&quot;:&quot;f6b67c99-7ef9-44e8-82c5-2c0e12186de1&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-tsx">// Before: overriding via vars
&lt;Marquee vars={{ root: { '--marquee-gap': '2rem' } }}&gt;
  {children}
&lt;/Marquee&gt;

// After: use the gap prop directly
&lt;Marquee gap="2rem"&gt;
  {children}
&lt;/Marquee&gt;</code></pre></div><h3><strong>4. Internal Overlay CSS Selectors Removed</strong></h3><p>The internal CSS classes <code>.marqueeFadeEdgeLeft</code>, <code>.marqueeFadeEdgeRight</code>, <code>.marqueeFadeEdgeTop</code>, and <code>.marqueeFadeEdgeBottom</code> no longer exist in the DOM. If you were targeting these with custom CSS, the fade is now controlled entirely through <code>mask-image</code> on the <code>.root</code> element. Customize the fade extent via the <code>fadeEdgesSize</code> prop or the <code>--marquee-fade-edge-size*</code> CSS custom properties.</p><h2><strong>Performance Improvements</strong></h2><ul><li><p><strong>GPU compositor layer promotion</strong> -- Added <code>will-change: transform</code> and <code>backface-visibility: hidden</code> to all animated clone wrappers. This tells the browser to promote each element to a dedicated GPU layer, preventing frame drops and eliminating the flicker visible on Safari/iOS during keyframe animation loop resets.</p></li><li><p><strong>Removed duplicate stacking context</strong> -- <code>overflow: hidden</code> was previously set on both <code>.root</code> and <code>.marqueeContainer</code>. The double declaration created an extra stacking context that could interfere with GPU layer compositing. It is now only on <code>.root</code>.</p></li><li><p><strong>Zero additional DOM nodes for fade edges</strong> -- The old system rendered 2-4 absolutely-positioned <code>&lt;div&gt;</code> elements. The CSS <code>mask-image</code> approach achieves the same visual effect with no extra DOM nodes at all.</p></li></ul><h2><strong>Bug Fixes</strong></h2><ul><li><p>Fixed a triple-dash typo in CSS variable fallback (<code>---marquee-gap-xl</code> corrected to <code>--marquee-gap-xl</code>) that caused the gap to always fall through to the hardcoded <code>16px</code> fallback.</p></li><li><p>Fixed <code>justify-content: space-around</code> in <code>.marqueeContent</code> to <code>flex-start</code>. The previous value distributed extra space between clones, breaking the seamless loop geometry.</p></li><li><p>Fixed missing <code>useMemo</code> dependencies (<code>gap</code>, <code>duration</code>) that prevented runtime prop changes from regenerating clone keys.</p></li><li><p>Fixed <code>libraryValue</code> mismatches in the configurator demo that showed incorrect &#8220;changed&#8221; indicators.</p></li><li><p>Fixed missing import statements in documentation code snippets (<code>ReactNode</code>, <code>Box</code>, <code>Flex</code>, <code>ThemeIcon</code>) so that users can copy and paste them correctly.</p></li></ul><h2><strong>Styles API Reference</strong></h2><h3><strong>Selectors</strong></h3><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!c5ZE!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc5b81e12-8774-4d76-b398-06c008103808_474x204.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!c5ZE!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc5b81e12-8774-4d76-b398-06c008103808_474x204.png 424w, https://substackcdn.com/image/fetch/$s_!c5ZE!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc5b81e12-8774-4d76-b398-06c008103808_474x204.png 848w, https://substackcdn.com/image/fetch/$s_!c5ZE!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc5b81e12-8774-4d76-b398-06c008103808_474x204.png 1272w, https://substackcdn.com/image/fetch/$s_!c5ZE!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc5b81e12-8774-4d76-b398-06c008103808_474x204.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!c5ZE!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc5b81e12-8774-4d76-b398-06c008103808_474x204.png" width="474" height="204" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c5b81e12-8774-4d76-b398-06c008103808_474x204.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:204,&quot;width&quot;:474,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:15019,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.undolog.com/i/189737143?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc5b81e12-8774-4d76-b398-06c008103808_474x204.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!c5ZE!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc5b81e12-8774-4d76-b398-06c008103808_474x204.png 424w, https://substackcdn.com/image/fetch/$s_!c5ZE!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc5b81e12-8774-4d76-b398-06c008103808_474x204.png 848w, https://substackcdn.com/image/fetch/$s_!c5ZE!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc5b81e12-8774-4d76-b398-06c008103808_474x204.png 1272w, https://substackcdn.com/image/fetch/$s_!c5ZE!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc5b81e12-8774-4d76-b398-06c008103808_474x204.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p></p><h3><strong>CSS Variables (set via </strong><code>varsResolver</code><strong>)</strong></h3><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!ZDmh!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F150f3243-87b1-426d-b9ce-b72a9340bc80_1516x762.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!ZDmh!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F150f3243-87b1-426d-b9ce-b72a9340bc80_1516x762.png 424w, https://substackcdn.com/image/fetch/$s_!ZDmh!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F150f3243-87b1-426d-b9ce-b72a9340bc80_1516x762.png 848w, https://substackcdn.com/image/fetch/$s_!ZDmh!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F150f3243-87b1-426d-b9ce-b72a9340bc80_1516x762.png 1272w, https://substackcdn.com/image/fetch/$s_!ZDmh!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F150f3243-87b1-426d-b9ce-b72a9340bc80_1516x762.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!ZDmh!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F150f3243-87b1-426d-b9ce-b72a9340bc80_1516x762.png" width="1456" height="732" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/150f3243-87b1-426d-b9ce-b72a9340bc80_1516x762.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:732,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:132666,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.undolog.com/i/189737143?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F150f3243-87b1-426d-b9ce-b72a9340bc80_1516x762.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!ZDmh!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F150f3243-87b1-426d-b9ce-b72a9340bc80_1516x762.png 424w, https://substackcdn.com/image/fetch/$s_!ZDmh!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F150f3243-87b1-426d-b9ce-b72a9340bc80_1516x762.png 848w, https://substackcdn.com/image/fetch/$s_!ZDmh!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F150f3243-87b1-426d-b9ce-b72a9340bc80_1516x762.png 1272w, https://substackcdn.com/image/fetch/$s_!ZDmh!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F150f3243-87b1-426d-b9ce-b72a9340bc80_1516x762.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h3><strong>CSS Variables (set via inline style, runtime-dependent)</strong></h3><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!ruZz!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9a8a6832-6942-4066-bb42-103a24e30e12_1468x394.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!ruZz!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9a8a6832-6942-4066-bb42-103a24e30e12_1468x394.png 424w, https://substackcdn.com/image/fetch/$s_!ruZz!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9a8a6832-6942-4066-bb42-103a24e30e12_1468x394.png 848w, https://substackcdn.com/image/fetch/$s_!ruZz!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9a8a6832-6942-4066-bb42-103a24e30e12_1468x394.png 1272w, https://substackcdn.com/image/fetch/$s_!ruZz!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9a8a6832-6942-4066-bb42-103a24e30e12_1468x394.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!ruZz!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9a8a6832-6942-4066-bb42-103a24e30e12_1468x394.png" width="1456" height="391" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/9a8a6832-6942-4066-bb42-103a24e30e12_1468x394.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:391,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:73505,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.undolog.com/i/189737143?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9a8a6832-6942-4066-bb42-103a24e30e12_1468x394.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!ruZz!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9a8a6832-6942-4066-bb42-103a24e30e12_1468x394.png 424w, https://substackcdn.com/image/fetch/$s_!ruZz!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9a8a6832-6942-4066-bb42-103a24e30e12_1468x394.png 848w, https://substackcdn.com/image/fetch/$s_!ruZz!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9a8a6832-6942-4066-bb42-103a24e30e12_1468x394.png 1272w, https://substackcdn.com/image/fetch/$s_!ruZz!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9a8a6832-6942-4066-bb42-103a24e30e12_1468x394.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h3><strong>Data Attributes</strong></h3><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!AV5g!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F652dac48-577a-4cdf-a128-13d73efe7020_1514x420.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!AV5g!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F652dac48-577a-4cdf-a128-13d73efe7020_1514x420.png 424w, https://substackcdn.com/image/fetch/$s_!AV5g!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F652dac48-577a-4cdf-a128-13d73efe7020_1514x420.png 848w, https://substackcdn.com/image/fetch/$s_!AV5g!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F652dac48-577a-4cdf-a128-13d73efe7020_1514x420.png 1272w, https://substackcdn.com/image/fetch/$s_!AV5g!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F652dac48-577a-4cdf-a128-13d73efe7020_1514x420.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!AV5g!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F652dac48-577a-4cdf-a128-13d73efe7020_1514x420.png" width="1456" height="404" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/652dac48-577a-4cdf-a128-13d73efe7020_1514x420.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:404,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:74595,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.undolog.com/i/189737143?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F652dac48-577a-4cdf-a128-13d73efe7020_1514x420.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!AV5g!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F652dac48-577a-4cdf-a128-13d73efe7020_1514x420.png 424w, https://substackcdn.com/image/fetch/$s_!AV5g!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F652dac48-577a-4cdf-a128-13d73efe7020_1514x420.png 848w, https://substackcdn.com/image/fetch/$s_!AV5g!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F652dac48-577a-4cdf-a128-13d73efe7020_1514x420.png 1272w, https://substackcdn.com/image/fetch/$s_!AV5g!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F652dac48-577a-4cdf-a128-13d73efe7020_1514x420.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2><strong>Getting Started</strong></h2><h3><strong>Installation</strong></h3><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;bash&quot;,&quot;nodeId&quot;:&quot;86a96d20-fe9f-477a-b82d-e1dfc5fd5b19&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-bash">npm install @gfazioli/mantine-marquee
# or
yarn add @gfazioli/mantine-marquee</code></pre></div><p>Make sure you have <code>@mantine/core</code> and <code>@mantine/hooks</code> (&gt;= 7.0.0) installed as peer dependencies.</p><h3><strong>Basic Usage</strong></h3><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;tsx&quot;,&quot;nodeId&quot;:&quot;fec7389b-816b-4e74-a174-0092ae0d0849&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-tsx">import { Marquee } from '@gfazioli/mantine-marquee';
import '@gfazioli/mantine-marquee/styles.css';

function Demo() {
  return (
    &lt;Marquee fadeEdges="linear" fadeEdgesSize="sm" pauseOnHover&gt;
      &lt;div&gt;Item 1&lt;/div&gt;
      &lt;div&gt;Item 2&lt;/div&gt;
      &lt;div&gt;Item 3&lt;/div&gt;
      &lt;div&gt;Item 4&lt;/div&gt;
    &lt;/Marquee&gt;
  );
}</code></pre></div><h3><strong>Migration from v2</strong></h3><ol><li><p>Remove all <code>fadeEdgesColor</code> props -- they are no longer needed.</p></li><li><p>If you were overriding <code>--marquee-gap</code> or <code>--marquee-direction</code> via the <code>vars</code> prop, switch to using the <code>gap</code> and <code>vertical</code> props directly.</p></li><li><p>If you have custom CSS targeting <code>.marqueeFadeEdgeLeft</code> / <code>.marqueeFadeEdgeRight</code> / etc., remove it and use <code>fadeEdgesSize</code> or the <code>--marquee-fade-edge-size*</code> CSS custom properties instead.</p></li><li><p>If your TypeScript wrapper types used <code>fadeEdges?: boolean</code>, update to <code>fadeEdges?: MarqueeFadeEdges</code>.</p></li></ol><p>All other existing usage (including <code>fadeEdges={true}</code>, <code>fadeEdges</code> shorthand, <code>fadeEdgesSize="md"</code>, and plain <code>vertical={true}</code> / <code>gap="xl"</code>) works without any changes.</p><h2><strong>Links</strong></h2><ul><li><p><a href="https://www.npmjs.com/package/@gfazioli/mantine-marquee">npm package</a></p></li><li><p><a href="https://gfazioli.github.io/mantine-marquee/">Documentation and live demos</a></p></li><li><p><a href="https://github.com/gfazioli/mantine-marquee">GitHub repository</a></p></li><li><p><a href="https://github.com/gfazioli/mantine-marquee/issues">Report an issue</a></p></li></ul>]]></content:encoded></item><item><title><![CDATA[Mantine SelectStepper: A Modern Alternative to Traditional Dropdowns]]></title><description><![CDATA[Enhance User Experience with a Modern, Intuitive Option Selector]]></description><link>https://www.undolog.com/p/mantine-selectstepper-a-modern-alternative</link><guid isPermaLink="false">https://www.undolog.com/p/mantine-selectstepper-a-modern-alternative</guid><dc:creator><![CDATA[Giovambattista Fazioli]]></dc:creator><pubDate>Thu, 22 Jan 2026 17:21:14 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!aoeG!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F95b32ba5-e912-41c3-886a-6db475dd16fa_2752x1536.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!aoeG!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F95b32ba5-e912-41c3-886a-6db475dd16fa_2752x1536.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!aoeG!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F95b32ba5-e912-41c3-886a-6db475dd16fa_2752x1536.png 424w, https://substackcdn.com/image/fetch/$s_!aoeG!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F95b32ba5-e912-41c3-886a-6db475dd16fa_2752x1536.png 848w, https://substackcdn.com/image/fetch/$s_!aoeG!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F95b32ba5-e912-41c3-886a-6db475dd16fa_2752x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!aoeG!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F95b32ba5-e912-41c3-886a-6db475dd16fa_2752x1536.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!aoeG!,w_2400,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F95b32ba5-e912-41c3-886a-6db475dd16fa_2752x1536.png" width="1200" height="670.054945054945" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/95b32ba5-e912-41c3-886a-6db475dd16fa_2752x1536.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:false,&quot;imageSize&quot;:&quot;large&quot;,&quot;height&quot;:813,&quot;width&quot;:1456,&quot;resizeWidth&quot;:1200,&quot;bytes&quot;:2219626,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://www.undolog.com/i/185426880?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F95b32ba5-e912-41c3-886a-6db475dd16fa_2752x1536.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:&quot;center&quot;,&quot;offset&quot;:false}" class="sizing-large" alt="" srcset="https://substackcdn.com/image/fetch/$s_!aoeG!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F95b32ba5-e912-41c3-886a-6db475dd16fa_2752x1536.png 424w, https://substackcdn.com/image/fetch/$s_!aoeG!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F95b32ba5-e912-41c3-886a-6db475dd16fa_2752x1536.png 848w, https://substackcdn.com/image/fetch/$s_!aoeG!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F95b32ba5-e912-41c3-886a-6db475dd16fa_2752x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!aoeG!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F95b32ba5-e912-41c3-886a-6db475dd16fa_2752x1536.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>In the world of modern web development, choosing the right UI component can make or break the user experience. Today, we&#8217;re excited to introduce you to <strong>Mantine SelectStepper</strong>, a React component that revolutionizes how users interact with predefined option lists. Built on top of the powerful <a href="https://mantine.dev/">Mantine UI</a> library, this component offers an elegant, intuitive alternative to traditional dropdown selects.</p><h2><strong>What is Mantine SelectStepper?</strong></h2><p>The <a href="https://gfazioli.github.io/mantine-select-stepper/">Mantine SelectStepper</a> is a Mantine-based React component that allows users to navigate through a list of options using increment and decrement buttons. Instead of clicking to open a dropdown menu, users can simply click left or right arrows to cycle through predefined values. This approach is particularly effective for:</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://www.undolog.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Undolog is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><ul><li><p>Configuration panels with sequential options</p></li><li><p>Forms where users need to select from an ordered list</p></li><li><p>Mobile interfaces where dropdowns can be cumbersome</p></li><li><p>Applications requiring keyboard-friendly navigation</p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!8Mrk!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F14e92b6b-3fac-4253-812d-b7e8b7dd2812_800x826.webp" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!8Mrk!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F14e92b6b-3fac-4253-812d-b7e8b7dd2812_800x826.webp 424w, https://substackcdn.com/image/fetch/$s_!8Mrk!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F14e92b6b-3fac-4253-812d-b7e8b7dd2812_800x826.webp 848w, https://substackcdn.com/image/fetch/$s_!8Mrk!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F14e92b6b-3fac-4253-812d-b7e8b7dd2812_800x826.webp 1272w, https://substackcdn.com/image/fetch/$s_!8Mrk!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F14e92b6b-3fac-4253-812d-b7e8b7dd2812_800x826.webp 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!8Mrk!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F14e92b6b-3fac-4253-812d-b7e8b7dd2812_800x826.webp" width="800" height="826" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/14e92b6b-3fac-4253-812d-b7e8b7dd2812_800x826.webp&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:826,&quot;width&quot;:800,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Mantine Select Stepper&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Mantine Select Stepper" title="Mantine Select Stepper" srcset="https://substackcdn.com/image/fetch/$s_!8Mrk!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F14e92b6b-3fac-4253-812d-b7e8b7dd2812_800x826.webp 424w, https://substackcdn.com/image/fetch/$s_!8Mrk!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F14e92b6b-3fac-4253-812d-b7e8b7dd2812_800x826.webp 848w, https://substackcdn.com/image/fetch/$s_!8Mrk!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F14e92b6b-3fac-4253-812d-b7e8b7dd2812_800x826.webp 1272w, https://substackcdn.com/image/fetch/$s_!8Mrk!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F14e92b6b-3fac-4253-812d-b7e8b7dd2812_800x826.webp 1456w" sizes="100vw"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2><strong>Getting Started: Incredibly Simple</strong></h2><p>One of the most impressive aspects of SelectStepper is its simplicity. Getting started requires just a few lines of code:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;bash&quot;,&quot;nodeId&quot;:&quot;29612895-bf56-42b7-9228-0cade641e70e&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-bash">yarn add @gfazioli/mantine-select-stepper
# or
npm install @gfazioli/mantine-select-stepper</code></pre></div><p>After installation, import the styles at the root of your application:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;tsx&quot;,&quot;nodeId&quot;:&quot;a990e7e7-a06f-4f9b-b44c-a863622b8ee9&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-tsx">import '@gfazioli/mantine-select-stepper/styles.css';</code></pre></div><p>And you&#8217;re ready to use it:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;tsx&quot;,&quot;nodeId&quot;:&quot;a8b3ba35-5514-422c-a749-af5524c7a9c1&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-tsx">import { SelectStepper } from '@gfazioli/mantine-select-stepper';

function Demo() {
  return &lt;SelectStepper data={['React', 'Vue', 'Angular']} /&gt;;
}</code></pre></div><p>That&#8217;s it! With just three lines of code, you have a fully functional, beautifully styled stepper component.</p><h2><strong>Key Features That Set It Apart</strong></h2><h3><strong>1. Infinite Loop Navigation</strong></h3><p>Enable seamless cycling through options with the <code>loop</code> prop. When users reach the end of the list, they automatically wrap back to the beginning&#8212;perfect for circular selections like months, days, or priority levels.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;tsx&quot;,&quot;nodeId&quot;:&quot;252e1be3-9949-4021-ae85-b8f55f6442e7&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-tsx">&lt;SelectStepper data={['January', 'February', 'March']} loop /&gt;</code></pre></div><h3><strong>2. Controlled Component Pattern</strong></h3><p>SelectStepper supports both controlled and uncontrolled modes, giving you complete flexibility in state management:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;tsx&quot;,&quot;nodeId&quot;:&quot;dca07c71-78c2-4de3-9497-3f951460f22d&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-tsx">import { useState } from 'react';
import { SelectStepper } from '@gfazioli/mantine-select-stepper';

function Demo() {
  const [value, setValue] = useState('react');

  return (
    &lt;SelectStepper
      data={['React', 'Vue', 'Angular']}
      value={value}
      onChange={(newValue) =&gt; setValue(newValue)}
    /&gt;
  );
}</code></pre></div><h3><strong>3. Disabled Items Support</strong></h3><p>Need to restrict certain options? Simply mark them as disabled in your data array:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;tsx&quot;,&quot;nodeId&quot;:&quot;9bdb2656-7482-4fd6-9ad1-ad7bf26014ff&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-tsx">const data = [
  { value: 'react', label: 'React' },
  { value: 'vue', label: 'Vue', disabled: true },
  { value: 'angular', label: 'Angular' },
];

&lt;SelectStepper data={data} /&gt;</code></pre></div><p>The stepper will intelligently skip disabled items during navigation.</p><h3><strong>4. Custom Icons</strong></h3><p>Personalize your stepper with custom icons using the <code>leftIcon</code> and <code>rightIcon</code> props:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;tsx&quot;,&quot;nodeId&quot;:&quot;266442ca-ce36-4d8d-b09b-d8105f879de3&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-tsx">import { IconChevronLeft, IconChevronRight } from '@tabler/icons-react';

&lt;SelectStepper
  data={['Small', 'Medium', 'Large']}
  leftIcon={&lt;IconChevronLeft /&gt;}
  rightIcon={&lt;IconChevronRight /&gt;}
/&gt;</code></pre></div><h3><strong>5. Custom Rendering</strong></h3><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!DWeh!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0ab3eabe-4f4a-489a-b145-a9bbe4c3d77f_800x58.webp" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!DWeh!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0ab3eabe-4f4a-489a-b145-a9bbe4c3d77f_800x58.webp 424w, https://substackcdn.com/image/fetch/$s_!DWeh!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0ab3eabe-4f4a-489a-b145-a9bbe4c3d77f_800x58.webp 848w, https://substackcdn.com/image/fetch/$s_!DWeh!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0ab3eabe-4f4a-489a-b145-a9bbe4c3d77f_800x58.webp 1272w, https://substackcdn.com/image/fetch/$s_!DWeh!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0ab3eabe-4f4a-489a-b145-a9bbe4c3d77f_800x58.webp 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!DWeh!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0ab3eabe-4f4a-489a-b145-a9bbe4c3d77f_800x58.webp" width="800" height="58" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/0ab3eabe-4f4a-489a-b145-a9bbe4c3d77f_800x58.webp&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:58,&quot;width&quot;:800,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Mantine Select Stepper&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Mantine Select Stepper" title="Mantine Select Stepper" srcset="https://substackcdn.com/image/fetch/$s_!DWeh!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0ab3eabe-4f4a-489a-b145-a9bbe4c3d77f_800x58.webp 424w, https://substackcdn.com/image/fetch/$s_!DWeh!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0ab3eabe-4f4a-489a-b145-a9bbe4c3d77f_800x58.webp 848w, https://substackcdn.com/image/fetch/$s_!DWeh!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0ab3eabe-4f4a-489a-b145-a9bbe4c3d77f_800x58.webp 1272w, https://substackcdn.com/image/fetch/$s_!DWeh!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0ab3eabe-4f4a-489a-b145-a9bbe4c3d77f_800x58.webp 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>Take full control over how options are displayed with the <code>renderOption</code> prop:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;tsx&quot;,&quot;nodeId&quot;:&quot;1a2892a1-a4ad-4b34-9e92-58a117da0c29&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-tsx">&lt;SelectStepper
  data={frameworks}
  renderOption={(item) =&gt; (
    &lt;Group gap="xs"&gt;
      &lt;Badge color={item.color}&gt;{item.label}&lt;/Badge&gt;
      &lt;span&gt;v{item.version}&lt;/span&gt;
    &lt;/Group&gt;
  )}
/&gt;</code></pre></div><h3><strong>6. Extended Data Properties</strong></h3><p>Need to store additional metadata with your options? Simply extend the <code>ComboboxItem</code> interface:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;tsx&quot;,&quot;nodeId&quot;:&quot;d1543aba-0941-4a0e-bca2-8ea9b3b41cd1&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-tsx">interface ComplexItem extends ComboboxItem {
  version?: string;
  color?: string;
}

const data: ComplexItem[] = [
  { value: 'react', label: 'React', version: '18.2.0', color: 'blue' },
  { value: 'vue', label: 'Vue.js', version: '3.2.37', color: 'green' },
];

function Demo() {
  const [value, setValue] = useState&lt;ComplexItem | null&gt;(null);

  return (
    &lt;SelectStepper
      data={data}
      value={value?.value}
      onChange={(_val, option) =&gt; setValue(option as ComplexItem)}
    /&gt;
  );
}</code></pre></div><p>The <code>onChange</code> handler provides the complete option object with all your custom properties.</p><h3><strong>7. Smooth Animations</strong></h3><p>Control animation behavior with dedicated props:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;tsx&quot;,&quot;nodeId&quot;:&quot;b9a6a125-d6b5-4e6e-8f8f-d84211ac20d1&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-tsx">&lt;SelectStepper
  data={['Option 1', 'Option 2', 'Option 3']}
  animate={true}
  animationDuration={300}
  animationTimingFunction="ease-in-out"
/&gt;</code></pre></div><p>Or disable animations entirely for a snappier feel.</p><h3><strong>8. Keyboard Navigation</strong></h3><p>Built-in keyboard support makes the component accessible and efficient:</p><ul><li><p><code>ArrowLeft</code> or <code>ArrowDown</code> - Move to previous item</p></li><li><p><code>ArrowRight</code> or <code>ArrowUp</code> - Move to next item</p></li></ul><h3><strong>9. Mantine Styles API Integration</strong></h3><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!JEbn!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc978895a-7d5c-4773-98eb-2055421a993e_800x730.webp" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!JEbn!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc978895a-7d5c-4773-98eb-2055421a993e_800x730.webp 424w, https://substackcdn.com/image/fetch/$s_!JEbn!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc978895a-7d5c-4773-98eb-2055421a993e_800x730.webp 848w, https://substackcdn.com/image/fetch/$s_!JEbn!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc978895a-7d5c-4773-98eb-2055421a993e_800x730.webp 1272w, https://substackcdn.com/image/fetch/$s_!JEbn!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc978895a-7d5c-4773-98eb-2055421a993e_800x730.webp 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!JEbn!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc978895a-7d5c-4773-98eb-2055421a993e_800x730.webp" width="800" height="730" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c978895a-7d5c-4773-98eb-2055421a993e_800x730.webp&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:730,&quot;width&quot;:800,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Mantine Styles API Integration&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Mantine Styles API Integration" title="Mantine Styles API Integration" srcset="https://substackcdn.com/image/fetch/$s_!JEbn!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc978895a-7d5c-4773-98eb-2055421a993e_800x730.webp 424w, https://substackcdn.com/image/fetch/$s_!JEbn!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc978895a-7d5c-4773-98eb-2055421a993e_800x730.webp 848w, https://substackcdn.com/image/fetch/$s_!JEbn!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc978895a-7d5c-4773-98eb-2055421a993e_800x730.webp 1272w, https://substackcdn.com/image/fetch/$s_!JEbn!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc978895a-7d5c-4773-98eb-2055421a993e_800x730.webp 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Fully integrated with Mantine&#8217;s Styles API, SelectStepper allows you to customize every inner element:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;tsx&quot;,&quot;nodeId&quot;:&quot;91c51d0c-d48d-4ef4-ae69-c38b454a3db3&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-tsx">&lt;SelectStepper
  data={['React', 'Vue', 'Angular']}
  classNames={{
    root: 'custom-root',
    view: 'custom-view',
    action: 'custom-action',
  }}
/&gt;</code></pre></div><h3><strong>10. Perfect for Forms</strong></h3><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!gI29!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5fc6cbbd-cb96-476b-8ae8-1a2214c89ea0_800x159.webp" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!gI29!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5fc6cbbd-cb96-476b-8ae8-1a2214c89ea0_800x159.webp 424w, https://substackcdn.com/image/fetch/$s_!gI29!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5fc6cbbd-cb96-476b-8ae8-1a2214c89ea0_800x159.webp 848w, https://substackcdn.com/image/fetch/$s_!gI29!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5fc6cbbd-cb96-476b-8ae8-1a2214c89ea0_800x159.webp 1272w, https://substackcdn.com/image/fetch/$s_!gI29!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5fc6cbbd-cb96-476b-8ae8-1a2214c89ea0_800x159.webp 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!gI29!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5fc6cbbd-cb96-476b-8ae8-1a2214c89ea0_800x159.webp" width="800" height="159" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/5fc6cbbd-cb96-476b-8ae8-1a2214c89ea0_800x159.webp&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:159,&quot;width&quot;:800,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Form Example&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Form Example" title="Form Example" srcset="https://substackcdn.com/image/fetch/$s_!gI29!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5fc6cbbd-cb96-476b-8ae8-1a2214c89ea0_800x159.webp 424w, https://substackcdn.com/image/fetch/$s_!gI29!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5fc6cbbd-cb96-476b-8ae8-1a2214c89ea0_800x159.webp 848w, https://substackcdn.com/image/fetch/$s_!gI29!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5fc6cbbd-cb96-476b-8ae8-1a2214c89ea0_800x159.webp 1272w, https://substackcdn.com/image/fetch/$s_!gI29!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5fc6cbbd-cb96-476b-8ae8-1a2214c89ea0_800x159.webp 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>SelectStepper integrates seamlessly with other Mantine form components:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;tsx&quot;,&quot;nodeId&quot;:&quot;c9c5674c-29af-4482-b629-cbd0100ef176&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-tsx">&lt;Stack&gt;
  &lt;TextInput label="Name" placeholder="Your name" /&gt;

  &lt;SelectStepper 
    data={['React', 'Vue', 'Angular']} 
    withBorder 
    placeholder="Select framework"
  /&gt;

  &lt;Group grow&gt;
    &lt;SelectStepper 
      data={['Junior', 'Mid', 'Senior']} 
      withBorder 
      placeholder="Experience level"
    /&gt;
    &lt;TextInput label="Years of experience" /&gt;
  &lt;/Group&gt;
&lt;/Stack&gt;</code></pre></div><h2><strong>Creative Use Cases</strong></h2><h3><strong>Boolean Stepper Pattern</strong></h3><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!GF3j!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc0268a00-7773-4f10-92eb-e7e96eee70a1_800x193.webp" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!GF3j!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc0268a00-7773-4f10-92eb-e7e96eee70a1_800x193.webp 424w, https://substackcdn.com/image/fetch/$s_!GF3j!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc0268a00-7773-4f10-92eb-e7e96eee70a1_800x193.webp 848w, https://substackcdn.com/image/fetch/$s_!GF3j!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc0268a00-7773-4f10-92eb-e7e96eee70a1_800x193.webp 1272w, https://substackcdn.com/image/fetch/$s_!GF3j!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc0268a00-7773-4f10-92eb-e7e96eee70a1_800x193.webp 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!GF3j!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc0268a00-7773-4f10-92eb-e7e96eee70a1_800x193.webp" width="800" height="193" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c0268a00-7773-4f10-92eb-e7e96eee70a1_800x193.webp&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:193,&quot;width&quot;:800,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Boolean Stepper Pattern&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Boolean Stepper Pattern" title="Boolean Stepper Pattern" srcset="https://substackcdn.com/image/fetch/$s_!GF3j!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc0268a00-7773-4f10-92eb-e7e96eee70a1_800x193.webp 424w, https://substackcdn.com/image/fetch/$s_!GF3j!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc0268a00-7773-4f10-92eb-e7e96eee70a1_800x193.webp 848w, https://substackcdn.com/image/fetch/$s_!GF3j!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc0268a00-7773-4f10-92eb-e7e96eee70a1_800x193.webp 1272w, https://substackcdn.com/image/fetch/$s_!GF3j!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc0268a00-7773-4f10-92eb-e7e96eee70a1_800x193.webp 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>One clever pattern is using SelectStepper as an enhanced boolean toggle:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;tsx&quot;,&quot;nodeId&quot;:&quot;dde6fe05-378a-412f-92a1-74a502448739&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-tsx">const [enabled, setEnabled] = useState(false);

&lt;SelectStepper
  data={[
    { value: 'false', label: 'Disabled' },
    { value: 'true', label: 'Enabled' },
  ]}
  value={enabled ? 'true' : 'false'}
  onChange={(val) =&gt; setEnabled(val === 'true')}
  leftIcon={enabled ? &lt;IconToggleRight /&gt; : &lt;IconToggleLeft /&gt;}
  renderOption={(item) =&gt; (
    &lt;Badge color={item.value === 'true' ? 'green' : 'red'}&gt;
      {item.label}
    &lt;/Badge&gt;
  )}
/&gt;</code></pre></div><h2><strong>Why Choose SelectStepper?</strong></h2><ol><li><p><strong>Simplicity</strong>: Minimal setup, maximum functionality</p></li><li><p><strong>TypeScript</strong>: Full type safety with TypeScript support</p></li><li><p><strong>Accessibility</strong>: Built-in keyboard navigation</p></li><li><p><strong>Customization</strong>: Extensive styling options through Mantine&#8217;s Styles API</p></li><li><p><strong>Performance</strong>: Smooth animations and optimized rendering</p></li><li><p><strong>Flexibility</strong>: Works in controlled and uncontrolled modes</p></li><li><p><strong>Modern</strong>: Built on React 18/19 with modern best practices</p></li></ol><h2><strong>Part of the Mantine Extensions Ecosystem</strong></h2><p><a href="https://gfazioli.github.io/mantine-select-stepper/">SelectStepper</a> is part of a growing collection of high-quality Mantine components. If you&#8217;re looking for more extensions to enhance your <a href="https://mantine.dev/">Mantine UI</a> applications, check out <a href="https://mantine-extensions.vercel.app/">Mantine Extensions</a> for additional components that integrate seamlessly with your existing Mantine setup.</p><h2><strong>Documentation and Demos</strong></h2><p>For comprehensive documentation, live demos, and interactive examples, visit the <a href="https://gfazioli.github.io/mantine-select-stepper/">official SelectStepper documentation</a>. The documentation includes:</p><ul><li><p>Interactive configurator to experiment with all props</p></li><li><p>Complete API reference</p></li><li><p>Real-world usage examples</p></li><li><p>Styles API documentation</p></li><li><p>TypeScript type definitions</p></li></ul><h2><strong>Conclusion</strong></h2><p><a href="https://gfazioli.github.io/mantine-select-stepper/">Mantine SelectStepper</a> proves that sometimes the best solutions are the simplest ones. By rethinking how users interact with option lists, it provides a refreshing alternative to traditional dropdowns that&#8217;s both elegant and highly functional. Whether you&#8217;re building a configuration panel, a multi-step form, or any interface requiring option selection, SelectStepper offers the perfect blend of simplicity, flexibility, and user experience.</p><p>Give it a try in your next project, and experience how a thoughtfully designed component can elevate your user interface!</p><div><hr></div><p><strong>Resources:</strong></p><ul><li><p>&#128230; <a href="https://www.npmjs.com/package/@gfazioli/mantine-select-stepper">npm package</a></p></li><li><p>&#128214; <a href="https://gfazioli.github.io/mantine-select-stepper/">Documentation &amp; Demos</a></p></li><li><p>&#128027; <a href="https://github.com/gfazioli/mantine-select-stepper">GitHub Repository</a></p></li><li><p>&#127912; <a href="https://mantine.dev/">Mantine UI</a></p></li><li><p>&#128268; <a href="https://mantine-extensions.vercel.app/">More Mantine Extensions</a></p></li></ul><div class="native-video-embed" data-component-name="VideoPlaceholder" data-attrs="{&quot;mediaUploadId&quot;:&quot;35da82c2-08cd-4002-a740-9505f98021b8&quot;,&quot;duration&quot;:null}"></div><p></p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://www.undolog.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Undolog is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[Building Reactive Status Indicators with the Mantine LED Component]]></title><description><![CDATA[A simple, type-safe LED status indicator for modern React dashboards]]></description><link>https://www.undolog.com/p/building-reactive-status-indicators</link><guid isPermaLink="false">https://www.undolog.com/p/building-reactive-status-indicators</guid><dc:creator><![CDATA[Giovambattista Fazioli]]></dc:creator><pubDate>Fri, 16 Jan 2026 11:03:08 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!y1gl!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5df70b3d-14fd-4351-9493-6d4c8a11299d_2752x1536.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!y1gl!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5df70b3d-14fd-4351-9493-6d4c8a11299d_2752x1536.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!y1gl!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5df70b3d-14fd-4351-9493-6d4c8a11299d_2752x1536.png 424w, https://substackcdn.com/image/fetch/$s_!y1gl!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5df70b3d-14fd-4351-9493-6d4c8a11299d_2752x1536.png 848w, https://substackcdn.com/image/fetch/$s_!y1gl!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5df70b3d-14fd-4351-9493-6d4c8a11299d_2752x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!y1gl!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5df70b3d-14fd-4351-9493-6d4c8a11299d_2752x1536.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!y1gl!,w_2400,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5df70b3d-14fd-4351-9493-6d4c8a11299d_2752x1536.png" width="1200" height="670.054945054945" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/5df70b3d-14fd-4351-9493-6d4c8a11299d_2752x1536.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:false,&quot;imageSize&quot;:&quot;large&quot;,&quot;height&quot;:813,&quot;width&quot;:1456,&quot;resizeWidth&quot;:1200,&quot;bytes&quot;:2329497,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://www.undolog.com/i/184526264?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5df70b3d-14fd-4351-9493-6d4c8a11299d_2752x1536.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:&quot;center&quot;,&quot;offset&quot;:false}" class="sizing-large" alt="" srcset="https://substackcdn.com/image/fetch/$s_!y1gl!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5df70b3d-14fd-4351-9493-6d4c8a11299d_2752x1536.png 424w, https://substackcdn.com/image/fetch/$s_!y1gl!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5df70b3d-14fd-4351-9493-6d4c8a11299d_2752x1536.png 848w, https://substackcdn.com/image/fetch/$s_!y1gl!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5df70b3d-14fd-4351-9493-6d4c8a11299d_2752x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!y1gl!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5df70b3d-14fd-4351-9493-6d4c8a11299d_2752x1536.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Designing clear, glanceable status indicators is one of the most underrated parts of UI design. Whether you are building an admin dashboard, a real&#8209;time monitoring panel, or a fun DEFCON&#8209;style alert system, users need an immediate visual cue about what is going on.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!5mRu!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb08e6133-98dd-49a9-bc7b-1b087862aead_800x898.webp" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!5mRu!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb08e6133-98dd-49a9-bc7b-1b087862aead_800x898.webp 424w, https://substackcdn.com/image/fetch/$s_!5mRu!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb08e6133-98dd-49a9-bc7b-1b087862aead_800x898.webp 848w, https://substackcdn.com/image/fetch/$s_!5mRu!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb08e6133-98dd-49a9-bc7b-1b087862aead_800x898.webp 1272w, https://substackcdn.com/image/fetch/$s_!5mRu!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb08e6133-98dd-49a9-bc7b-1b087862aead_800x898.webp 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!5mRu!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb08e6133-98dd-49a9-bc7b-1b087862aead_800x898.webp" width="800" height="898" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/b08e6133-98dd-49a9-bc7b-1b087862aead_800x898.webp&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:898,&quot;width&quot;:800,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Mantine Led&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Mantine Led" title="Mantine Led" srcset="https://substackcdn.com/image/fetch/$s_!5mRu!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb08e6133-98dd-49a9-bc7b-1b087862aead_800x898.webp 424w, https://substackcdn.com/image/fetch/$s_!5mRu!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb08e6133-98dd-49a9-bc7b-1b087862aead_800x898.webp 848w, https://substackcdn.com/image/fetch/$s_!5mRu!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb08e6133-98dd-49a9-bc7b-1b087862aead_800x898.webp 1272w, https://substackcdn.com/image/fetch/$s_!5mRu!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb08e6133-98dd-49a9-bc7b-1b087862aead_800x898.webp 1456w" sizes="100vw"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>In this article, we will explore the <a href="https://gfazioli.github.io/mantine-led/">Mantine Led</a> component from the <code>@gfazioli/mantine-led</code> package: a small, focused React component built specifically for the <a href="https://mantine.dev/">Mantine UI library</a>. </p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://www.undolog.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Undolog is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p>It is designed to be:</p><ul><li><p>Simple to install and use</p></li><li><p>Fully controlled and predictable</p></li><li><p>Seamlessly theme&#8209;aware</p></li><li><p>Easily customizable via Mantine&#8217;s Styles API</p></li></ul><p>You can also find this and other Mantine-based extensions on the Mantine Extensions website at <a href="https://mantine-extensions.vercel.app/">https://mantine-extensions.vercel.app/</a>.</p><div><hr></div><h2><strong>Installation and setup</strong></h2><p>The LED component is published as a standalone package. Install it via your favorite package manager:</p><pre><code><code>npm install @gfazioli/mantine-led
# or
yarn add @gfazioli/mantine-led</code></code></pre><p>Then, import the styles once at the root of your application:</p><pre><code><code>import '@gfazioli/mantine-led/styles.css';</code></code></pre><p>If you are using CSS layers and want finer control over the cascade, you can instead import the layered stylesheet:</p><pre><code><code>import '@gfazioli/mantine-led/styles.layer.css';</code></code></pre><p>That&#8217;s all you need to start using the component in any React + Mantine UI project.</p><div><hr></div><h2><strong>Basic usage: a single LED indicator</strong></h2><p>At its core, the <code>Led</code> component is a compact visual indicator controlled through props. A minimal example looks like this:</p><pre><code><code>import { Led } from '@gfazioli/mantine-led';

function ServerStatus() {
  return &lt;Led value="on" color="green" label="Server online" /&gt;;
}</code></code></pre><p>Key ideas here:</p><ul><li><p><code>value</code> controls the LED state (for example, <code>"on"</code>, <code>"off"</code>, or other states supported by your design).</p></li><li><p><code>color</code> is integrated with the Mantine theme, so you can reuse existing semantic colors.</p></li><li><p><code>label</code> lets you attach a text (or any React node) describing what the LED represents.</p></li></ul><p>This small piece of UI is immediately understandable to users, but remains type&#8209;safe and composable in a larger React codebase.</p><div><hr></div><h2><strong>Fully controlled with the </strong><code>value</code><strong> prop</strong></h2><p>One of the most important design decisions of this component is that it is fully controlled via the <code>value</code> prop. That means you always decide when and how the LED changes.</p><pre><code><code>import { useState } from 'react';
import { Button, Group } from '@mantine/core';
import { Led } from '@gfazioli/mantine-led';

export function ControlledExample() {
  const [status, setStatus] = useState&lt;'on' | 'off'&gt;('off');

  return (
    &lt;Group align="center" gap="md"&gt;
      &lt;Led value={status} color={status === 'on' ? 'green' : 'red'} label={status === 'on' ? 'Active' : 'Inactive'} /&gt;

      &lt;Button onClick={() =&gt; setStatus((current) =&gt; (current === 'on' ? 'off' : 'on'))}&gt;
        Toggle status
      &lt;/Button&gt;
    &lt;/Group&gt;
  );
}</code></code></pre><p>Because the <code>Led</code> is controlled, it behaves exactly like any other input or view component in a modern React + TypeScript app: your state is the single source of truth, and the UI simply reflects it.</p><div><hr></div><h2><strong>Theme-aware colors</strong></h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!HYCj!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a55109b-c0c3-42c0-af77-079fb289f507_800x485.webp" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!HYCj!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a55109b-c0c3-42c0-af77-079fb289f507_800x485.webp 424w, https://substackcdn.com/image/fetch/$s_!HYCj!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a55109b-c0c3-42c0-af77-079fb289f507_800x485.webp 848w, https://substackcdn.com/image/fetch/$s_!HYCj!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a55109b-c0c3-42c0-af77-079fb289f507_800x485.webp 1272w, https://substackcdn.com/image/fetch/$s_!HYCj!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a55109b-c0c3-42c0-af77-079fb289f507_800x485.webp 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!HYCj!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a55109b-c0c3-42c0-af77-079fb289f507_800x485.webp" width="800" height="485" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/1a55109b-c0c3-42c0-af77-079fb289f507_800x485.webp&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:485,&quot;width&quot;:800,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Mantine Led&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Mantine Led" title="Mantine Led" srcset="https://substackcdn.com/image/fetch/$s_!HYCj!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a55109b-c0c3-42c0-af77-079fb289f507_800x485.webp 424w, https://substackcdn.com/image/fetch/$s_!HYCj!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a55109b-c0c3-42c0-af77-079fb289f507_800x485.webp 848w, https://substackcdn.com/image/fetch/$s_!HYCj!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a55109b-c0c3-42c0-af77-079fb289f507_800x485.webp 1272w, https://substackcdn.com/image/fetch/$s_!HYCj!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a55109b-c0c3-42c0-af77-079fb289f507_800x485.webp 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The LED component integrates seamlessly with the Mantine theme. The <code>color</code> prop accepts any Mantine theme color, so your indicators always match the rest of your design system:</p><pre><code><code>import { Stack } from '@mantine/core';
import { Led } from '@gfazioli/mantine-led';

function ColorPalettePreview() {
  return (
    &lt;Stack&gt;
      &lt;Led value="on" color="green" label="Success" /&gt;
      &lt;Led value="on" color="yellow" label="Warning" /&gt;
      &lt;Led value="on" color="red" label="Error" /&gt;
      &lt;Led value="on" color="blue" label="Info" /&gt;
    &lt;/Stack&gt;
  );
}</code></code></pre><p>By reusing Mantine&#8217;s palette, LEDs automatically adapt when you change themes (for example, switching between light and dark mode) without any extra work.</p><div><hr></div><h2><strong>Built-in animations for real-time feedback</strong></h2><p>Static indicators are useful, but animated feedback can make status changes much more noticeable. The LED component ships with multiple built&#8209;in animations that you can apply via props:</p><ul><li><p><code>pulse</code>: smooth pulsing effect (default)</p></li><li><p><code>flash</code>: quick flashing for urgent alerts</p></li><li><p><code>breathe</code>: slow breathing effect for softer attention</p></li><li><p><code>blink</code>: regular blinking pattern</p></li><li><p><code>glow</code>: a strong glow for highly visible states</p></li></ul><p>A simple animated example:</p><pre><code><code>import { Group } from '@mantine/core';
import { Led } from '@gfazioli/mantine-led';

function AnimatedIndicators() {
  return (
    &lt;Group&gt;
      &lt;Led value="on" color="green" animation="pulse" label="Healthy" /&gt;
      &lt;Led value="on" color="yellow" animation="breathe" label="Degraded" /&gt;
      &lt;Led value="on" color="red" animation="flash" label="Critical" /&gt;
    &lt;/Group&gt;
  );
}</code></code></pre><p>These animations make it easy to differentiate between normal, warning, and critical states at a glance, while keeping the API surface intentionally small and easy to remember.</p><div><hr></div><h2><strong>Flexible labels and layout</strong></h2><p>Real-world dashboards rarely show an icon alone; labels are essential for clarity. The <code>Led</code> component provides a <code>label</code> prop that accepts any React node and a <code>labelPosition</code> prop to control layout.</p><pre><code><code>import { Badge } from '@mantine/core';
import { Led } from '@gfazioli/mantine-led';

function LabeledLed() {
  return (
    &lt;Led
      value="on"
      color="teal"
      labelPosition="right"
      label={&lt;Badge color="teal"&gt;Payment gateway&lt;/Badge&gt;}
    /&gt;
  );
}</code></code></pre><p>Because <code>label</code> is a React node, you are not limited to plain text: badges, icons, formatted text, or composite components all work out of the box.</p><div><hr></div><h2><strong>Styling with Mantine Styles API</strong></h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!qx_m!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F626744b0-ebc3-4363-b802-e3ab7e3b2019_800x387.webp" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!qx_m!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F626744b0-ebc3-4363-b802-e3ab7e3b2019_800x387.webp 424w, https://substackcdn.com/image/fetch/$s_!qx_m!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F626744b0-ebc3-4363-b802-e3ab7e3b2019_800x387.webp 848w, https://substackcdn.com/image/fetch/$s_!qx_m!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F626744b0-ebc3-4363-b802-e3ab7e3b2019_800x387.webp 1272w, https://substackcdn.com/image/fetch/$s_!qx_m!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F626744b0-ebc3-4363-b802-e3ab7e3b2019_800x387.webp 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!qx_m!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F626744b0-ebc3-4363-b802-e3ab7e3b2019_800x387.webp" width="800" height="387" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/626744b0-ebc3-4363-b802-e3ab7e3b2019_800x387.webp&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:387,&quot;width&quot;:800,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Mantine Led&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Mantine Led" title="Mantine Led" srcset="https://substackcdn.com/image/fetch/$s_!qx_m!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F626744b0-ebc3-4363-b802-e3ab7e3b2019_800x387.webp 424w, https://substackcdn.com/image/fetch/$s_!qx_m!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F626744b0-ebc3-4363-b802-e3ab7e3b2019_800x387.webp 848w, https://substackcdn.com/image/fetch/$s_!qx_m!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F626744b0-ebc3-4363-b802-e3ab7e3b2019_800x387.webp 1272w, https://substackcdn.com/image/fetch/$s_!qx_m!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F626744b0-ebc3-4363-b802-e3ab7e3b2019_800x387.webp 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The <code>Led</code> component is built following Mantine&#8217;s factory pattern and fully supports the Styles API (<a href="https://mantine.dev/styles/styles-api/">https://mantine.dev/styles/styles-api/</a>). This means you can target internal parts of the component using <code>classNames</code>, <code>styles</code>, or CSS variables.</p><p>For example, you can override styles of the root element, the LED itself, or the label container:</p><pre><code><code>import { Led } from '@gfazioli/mantine-led';

function CustomStyledLed() {
  return (
    &lt;Led
      value="on"
      color="blue"
      label="Custom styled"
      classNames={{
        root: 'my-root',
        led: 'my-led',
        label: 'my-label',
      }}
    /&gt;
  );
}</code></code></pre><p>Combined with CSS variables like <code>--led-size</code> and <code>--led-color</code>, you can fine&#8209;tune dimensions, spacing, and visual tone without losing the type safety and clarity of the public props.</p><div><hr></div><h2><strong>Real-world use cases</strong></h2><p>The official documentation showcases some practical patterns that are extremely easy to reproduce in your own codebase:</p><h3><strong>System status panel</strong></h3><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!7zsH!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1abfa268-bb54-4e35-ad10-71e578e5c058_800x469.webp" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!7zsH!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1abfa268-bb54-4e35-ad10-71e578e5c058_800x469.webp 424w, https://substackcdn.com/image/fetch/$s_!7zsH!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1abfa268-bb54-4e35-ad10-71e578e5c058_800x469.webp 848w, https://substackcdn.com/image/fetch/$s_!7zsH!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1abfa268-bb54-4e35-ad10-71e578e5c058_800x469.webp 1272w, https://substackcdn.com/image/fetch/$s_!7zsH!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1abfa268-bb54-4e35-ad10-71e578e5c058_800x469.webp 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!7zsH!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1abfa268-bb54-4e35-ad10-71e578e5c058_800x469.webp" width="800" height="469" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/1abfa268-bb54-4e35-ad10-71e578e5c058_800x469.webp&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:469,&quot;width&quot;:800,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Use cases&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Use cases" title="Use cases" srcset="https://substackcdn.com/image/fetch/$s_!7zsH!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1abfa268-bb54-4e35-ad10-71e578e5c058_800x469.webp 424w, https://substackcdn.com/image/fetch/$s_!7zsH!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1abfa268-bb54-4e35-ad10-71e578e5c058_800x469.webp 848w, https://substackcdn.com/image/fetch/$s_!7zsH!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1abfa268-bb54-4e35-ad10-71e578e5c058_800x469.webp 1272w, https://substackcdn.com/image/fetch/$s_!7zsH!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1abfa268-bb54-4e35-ad10-71e578e5c058_800x469.webp 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>A panel displaying multiple services, each represented by a label and an LED. This pattern is ideal for infrastructure dashboards, IoT control panels, and internal tooling.</p><ul><li><p>Each row uses a <code>Led</code> component with a descriptive label</p></li><li><p>Colors and animations convey health (green/pulse for healthy, yellow/breathe for partial, red/flash for critical)</p></li><li><p>The component&#8217;s simplicity keeps the JSX readable even with many indicators on screen</p></li></ul><h3><strong>DEFCON-style alert system</strong></h3><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!T5qS!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff2540e1b-e04c-40ce-a202-f400d42a9ad6_800x414.webp" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!T5qS!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff2540e1b-e04c-40ce-a202-f400d42a9ad6_800x414.webp 424w, https://substackcdn.com/image/fetch/$s_!T5qS!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff2540e1b-e04c-40ce-a202-f400d42a9ad6_800x414.webp 848w, https://substackcdn.com/image/fetch/$s_!T5qS!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff2540e1b-e04c-40ce-a202-f400d42a9ad6_800x414.webp 1272w, https://substackcdn.com/image/fetch/$s_!T5qS!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff2540e1b-e04c-40ce-a202-f400d42a9ad6_800x414.webp 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!T5qS!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff2540e1b-e04c-40ce-a202-f400d42a9ad6_800x414.webp" width="800" height="414" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f2540e1b-e04c-40ce-a202-f400d42a9ad6_800x414.webp&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:414,&quot;width&quot;:800,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Use cases&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Use cases" title="Use cases" srcset="https://substackcdn.com/image/fetch/$s_!T5qS!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff2540e1b-e04c-40ce-a202-f400d42a9ad6_800x414.webp 424w, https://substackcdn.com/image/fetch/$s_!T5qS!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff2540e1b-e04c-40ce-a202-f400d42a9ad6_800x414.webp 848w, https://substackcdn.com/image/fetch/$s_!T5qS!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff2540e1b-e04c-40ce-a202-f400d42a9ad6_800x414.webp 1272w, https://substackcdn.com/image/fetch/$s_!T5qS!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff2540e1b-e04c-40ce-a202-f400d42a9ad6_800x414.webp 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>For more playful or storytelling UIs, you can stack multiple LEDs with different colors and animations to represent escalating alert levels (like a DEFCON scale). Because the API is so small, the resulting code stays approachable, even as the visual result feels dynamic and polished.</p><div><hr></div><h2><strong>Why this LED component works so well with Mantine</strong></h2><p>What makes this component stand out is how naturally it fits into a <a href="https://mantine.dev/">Mantine UI</a> ecosystem:</p><ul><li><p>It uses Mantine&#8217;s polymorphic factory and Styles API under the hood, so its behavior is consistent with core Mantine components.</p></li><li><p>It respects theme colors and sizing scales, providing a familiar configuration surface for existing Mantine users.</p></li><li><p>It embraces controlled state via the <code>value</code> prop, making it easy to integrate with Zustand, Redux, React Query, or any other data layer.</p></li></ul><p>All of this is wrapped in a tiny, focused API that you can learn in minutes.</p><div><hr></div><h2><strong>Conclusion</strong></h2><p>If you need a compact, expressive way to visualize status in your React + Mantine UI projects, the <a href="https://gfazioli.github.io/mantine-led/">Mantine Led</a> component is a great fit. It combines:</p><ul><li><p>Minimal, predictable props</p></li><li><p>Theme&#8209;aware colors and sizes</p></li><li><p>Built&#8209;in animations for richer feedback</p></li><li><p>Flexible labels and a powerful Styles API</p></li></ul><p>Together, these features make it trivial to build anything from simple on/off indicators to complex monitoring dashboards, without sacrificing type safety or design consistency.</p><p>Explore more Mantine&#8209;powered components and ideas on Mantine UI at <a href="https://mantine.dev/">https://mantine.dev/ </a> and discover additional community extensions on Mantine Extensions at <a href="https://mantine-extensions.vercel.app/">https://mantine-extensions.vercel.app/</a>.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://www.undolog.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Undolog is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[Spotlight Your UI: Introducing Mantine Mask for Elegant Focus Effects]]></title><description><![CDATA[A friendly deep dive into a flexible, cursor&#8209;aware spotlight for Mantine]]></description><link>https://www.undolog.com/p/spotlight-your-ui-introducing-mantine</link><guid isPermaLink="false">https://www.undolog.com/p/spotlight-your-ui-introducing-mantine</guid><dc:creator><![CDATA[Giovambattista Fazioli]]></dc:creator><pubDate>Fri, 26 Dec 2025 07:01:23 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!jEDs!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F79e1df63-9fa5-45b4-b80e-553f407477b0_2752x1536.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!jEDs!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F79e1df63-9fa5-45b4-b80e-553f407477b0_2752x1536.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!jEDs!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F79e1df63-9fa5-45b4-b80e-553f407477b0_2752x1536.png 424w, https://substackcdn.com/image/fetch/$s_!jEDs!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F79e1df63-9fa5-45b4-b80e-553f407477b0_2752x1536.png 848w, https://substackcdn.com/image/fetch/$s_!jEDs!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F79e1df63-9fa5-45b4-b80e-553f407477b0_2752x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!jEDs!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F79e1df63-9fa5-45b4-b80e-553f407477b0_2752x1536.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!jEDs!,w_2400,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F79e1df63-9fa5-45b4-b80e-553f407477b0_2752x1536.png" width="1200" height="670.054945054945" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/79e1df63-9fa5-45b4-b80e-553f407477b0_2752x1536.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:false,&quot;imageSize&quot;:&quot;large&quot;,&quot;height&quot;:813,&quot;width&quot;:1456,&quot;resizeWidth&quot;:1200,&quot;bytes&quot;:1647628,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://www.undolog.com/i/181988569?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F79e1df63-9fa5-45b4-b80e-553f407477b0_2752x1536.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:&quot;center&quot;,&quot;offset&quot;:false}" class="sizing-large" alt="" srcset="https://substackcdn.com/image/fetch/$s_!jEDs!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F79e1df63-9fa5-45b4-b80e-553f407477b0_2752x1536.png 424w, https://substackcdn.com/image/fetch/$s_!jEDs!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F79e1df63-9fa5-45b4-b80e-553f407477b0_2752x1536.png 848w, https://substackcdn.com/image/fetch/$s_!jEDs!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F79e1df63-9fa5-45b4-b80e-553f407477b0_2752x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!jEDs!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F79e1df63-9fa5-45b4-b80e-553f407477b0_2752x1536.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>If you&#8217;ve ever wished for a subtle way to guide attention in a UI&#8212;highlighting imagery, content blocks, or interactive regions&#8212;Mantine Mask delivers a clean, performant &#8220;spotlight&#8221; you can drop anywhere in your React app. Built to pair beautifully with the Mantine UI Library, <a href="https://gfazioli.github.io/mantine-mask/">Mantine Mask</a> wraps any content and applies a CSS-driven radial or linear mask that can follow the cursor or stay fixed, giving you fine-grained control over size, feathering, opacity, and interaction.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!0KnC!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6baf9efe-ea34-4951-b727-13c2e3eecb4f_800x1069.webp" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!0KnC!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6baf9efe-ea34-4951-b727-13c2e3eecb4f_800x1069.webp 424w, https://substackcdn.com/image/fetch/$s_!0KnC!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6baf9efe-ea34-4951-b727-13c2e3eecb4f_800x1069.webp 848w, https://substackcdn.com/image/fetch/$s_!0KnC!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6baf9efe-ea34-4951-b727-13c2e3eecb4f_800x1069.webp 1272w, https://substackcdn.com/image/fetch/$s_!0KnC!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6baf9efe-ea34-4951-b727-13c2e3eecb4f_800x1069.webp 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!0KnC!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6baf9efe-ea34-4951-b727-13c2e3eecb4f_800x1069.webp" width="800" height="1069" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/6baf9efe-ea34-4951-b727-13c2e3eecb4f_800x1069.webp&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1069,&quot;width&quot;:800,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Demo&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Demo" title="Demo" srcset="https://substackcdn.com/image/fetch/$s_!0KnC!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6baf9efe-ea34-4951-b727-13c2e3eecb4f_800x1069.webp 424w, https://substackcdn.com/image/fetch/$s_!0KnC!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6baf9efe-ea34-4951-b727-13c2e3eecb4f_800x1069.webp 848w, https://substackcdn.com/image/fetch/$s_!0KnC!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6baf9efe-ea34-4951-b727-13c2e3eecb4f_800x1069.webp 1272w, https://substackcdn.com/image/fetch/$s_!0KnC!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6baf9efe-ea34-4951-b727-13c2e3eecb4f_800x1069.webp 1456w" sizes="100vw"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>At its core, Mask is a simple wrapper: place it around your content, and decide whether the spotlight should be static or cursor&#8209;tracked. The &#8220;inside&#8221; of the mask remains visible while the &#8220;outside&#8221; fades to transparent. Flip it with invertMask, and you get a &#8220;hole&#8221; effect to reveal backgrounds or layers beneath.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://www.undolog.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Undolog is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p>You&#8217;ll start by importing styles at your app root:</p><pre><code><code>import &#8216;@gfazioli/mantine-mask/styles.css&#8217;;
// or layer-aware:
import &#8216;@gfazioli/mantine-mask/styles.layer.css&#8217;;
</code></code></pre><h3><strong>Two ways to focus: cursor or static &#65532;</strong></h3><ul><li><p>Cursor spotlight: enable <code>withCursorMask</code> to have the mask follow the pointer. Tune the motion with animation and easing, from instant to lerp-like smoothing.</p></li><li><p>Static spotlight: fix the origin using <code>maskX/maskY</code> to create an anchored reveal. Ideal for hero images, headers, or guided tours.</p></li></ul><p>For size, choose maskRadius for a circular spotlight or <code>maskRadiusX/maskRadiusY</code> for elliptical control. For edge softness, <code>maskFeather</code> offers a quick, intuitive knob (0 = hard edge, 100 = full fade), while <code>maskTransparencyStart/End</code> give you advanced control over the fade curve.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!PhR9!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd610b1b8-92e5-487f-820c-d5e90811ab6d_800x742.webp" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!PhR9!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd610b1b8-92e5-487f-820c-d5e90811ab6d_800x742.webp 424w, https://substackcdn.com/image/fetch/$s_!PhR9!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd610b1b8-92e5-487f-820c-d5e90811ab6d_800x742.webp 848w, https://substackcdn.com/image/fetch/$s_!PhR9!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd610b1b8-92e5-487f-820c-d5e90811ab6d_800x742.webp 1272w, https://substackcdn.com/image/fetch/$s_!PhR9!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd610b1b8-92e5-487f-820c-d5e90811ab6d_800x742.webp 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!PhR9!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd610b1b8-92e5-487f-820c-d5e90811ab6d_800x742.webp" width="800" height="742" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d610b1b8-92e5-487f-820c-d5e90811ab6d_800x742.webp&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:742,&quot;width&quot;:800,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Convenience&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Convenience" title="Convenience" srcset="https://substackcdn.com/image/fetch/$s_!PhR9!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd610b1b8-92e5-487f-820c-d5e90811ab6d_800x742.webp 424w, https://substackcdn.com/image/fetch/$s_!PhR9!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd610b1b8-92e5-487f-820c-d5e90811ab6d_800x742.webp 848w, https://substackcdn.com/image/fetch/$s_!PhR9!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd610b1b8-92e5-487f-820c-d5e90811ab6d_800x742.webp 1272w, https://substackcdn.com/image/fetch/$s_!PhR9!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd610b1b8-92e5-487f-820c-d5e90811ab6d_800x742.webp 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h3><strong>Radial and linear variants &#65532;</strong></h3><ul><li><p>Radial (default): the classic circular/elliptical spotlight.</p></li><li><p>Linear: a band-like &#8220;scanner&#8221; that works wonders for reveal stripes, highlights, or motion-driven accents. Control thickness via maskRadius, softness via maskFeather or transparency boundaries, and orientation via maskAngle.</p></li></ul><h3><strong>Inverted masks and layered reveals &#65532;</strong></h3><p>Invert the logic with invertMask to make the spotlight area transparent and keep the outside visible. This unlocks &#8220;hole&#8221; bands in linear mode and background reveals in radial mode. For consistent aesthetics across themes, set a background on Mask (e.g., Mantine&#8217;s bg prop) so the glow looks uniform in light and dark modes.</p><h3><strong>Interaction and accessibility &#65532;</strong></h3><p>Activation determines when the mask is active: always, hover, pointer, or focus. Prefer activation=&#8220;focus&#8221; for keyboard-friendly UIs&#8212;Mask applies tabIndex by default, keeping your components accessible. Need full control? Use active and onActiveChange to manage visibility programmatically.</p><h3><strong>Keep the spotlight inside &#65532;</strong></h3><p>When using cursor mode, clampToBounds ensures the center never slips outside the container, and clampPadding adds margin from the edges. These small touches keep the experience polished and predictable.</p><h3><strong>Works with any content &#65532;</strong></h3><p>Mask is content-agnostic: images, cards, text blocks, custom React trees&#8212;wrap it and go. It&#8217;s great for:</p><ul><li><p>Reveal: different background on the container, shown through the spotlight for a playful discovery effect.</p></li><li><p>Zoom: a magnified background behind the same image, creating a lightweight loupe without complex canvas logic.</p></li></ul><h2><strong>Quick examples &#65532;</strong></h2><ul><li><p>Circular spotlight that tracks the cursor:</p></li></ul><pre><code><code>&lt;Mask withCursorMask maskRadius={360}&gt;
  &lt;Image ... /&gt;
&lt;/Mask&gt;</code></code></pre><p>Elliptical highlight for wide banners:</p><pre><code><code>&lt;Mask withCursorMask maskRadiusX={420} maskRadiusY={180}&gt;
  &lt;Image ... /&gt;
&lt;/Mask&gt;</code></code></pre><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!-mMm!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb3562f22-c623-4ccb-8455-16b0546413bb_800x567.webp" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!-mMm!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb3562f22-c623-4ccb-8455-16b0546413bb_800x567.webp 424w, https://substackcdn.com/image/fetch/$s_!-mMm!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb3562f22-c623-4ccb-8455-16b0546413bb_800x567.webp 848w, https://substackcdn.com/image/fetch/$s_!-mMm!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb3562f22-c623-4ccb-8455-16b0546413bb_800x567.webp 1272w, https://substackcdn.com/image/fetch/$s_!-mMm!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb3562f22-c623-4ccb-8455-16b0546413bb_800x567.webp 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!-mMm!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb3562f22-c623-4ccb-8455-16b0546413bb_800x567.webp" width="800" height="567" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/b3562f22-c623-4ccb-8455-16b0546413bb_800x567.webp&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:567,&quot;width&quot;:800,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Elliptical&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Elliptical" title="Elliptical" srcset="https://substackcdn.com/image/fetch/$s_!-mMm!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb3562f22-c623-4ccb-8455-16b0546413bb_800x567.webp 424w, https://substackcdn.com/image/fetch/$s_!-mMm!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb3562f22-c623-4ccb-8455-16b0546413bb_800x567.webp 848w, https://substackcdn.com/image/fetch/$s_!-mMm!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb3562f22-c623-4ccb-8455-16b0546413bb_800x567.webp 1272w, https://substackcdn.com/image/fetch/$s_!-mMm!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb3562f22-c623-4ccb-8455-16b0546413bb_800x567.webp 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Static origin with hard&#8209;edge reveal:</p><pre><code><code>&lt;Mask withCursorMask={false} maskX={25} maskY={35} maskFeather={0}&gt;
  &lt;Image ... /&gt;
&lt;/Mask&gt;</code></code></pre><p>Linear + inverted &#8220;hole band&#8221;:</p><pre><code><code>&lt;Mask variant=&#8221;linear&#8221; invertMask withCursorMask maskAngle={90} maskRadius={180} maskFeather={30}&gt;
  &lt;Image ... /&gt;
&lt;/Mask&gt;</code></code></pre><h2><strong>Why it fits naturally in a Mantine app &#65532;</strong></h2><p><a href="https://gfazioli.github.io/mantine-mask/">Mantine Mask</a> embraces Mantine&#8217;s design philosophy: composable, accessible, and theme-aware. It exposes sensible defaults yet gives you low-level controls when you need precision. Combined with Mantine&#8217;s layout, typography, and theming, you can craft focused, delightful interactions without heavy custom CSS or complex animation stacks. Explore Mantine&#8217;s full component ecosystem to round out your UI: <a href="https://mantine.dev/getting-started/">Mantine UI Library &#8599;</a>.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!kQew!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0ad3b356-d023-444d-bc7a-338211039419_800x992.webp" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!kQew!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0ad3b356-d023-444d-bc7a-338211039419_800x992.webp 424w, https://substackcdn.com/image/fetch/$s_!kQew!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0ad3b356-d023-444d-bc7a-338211039419_800x992.webp 848w, https://substackcdn.com/image/fetch/$s_!kQew!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0ad3b356-d023-444d-bc7a-338211039419_800x992.webp 1272w, https://substackcdn.com/image/fetch/$s_!kQew!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0ad3b356-d023-444d-bc7a-338211039419_800x992.webp 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!kQew!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0ad3b356-d023-444d-bc7a-338211039419_800x992.webp" width="800" height="992" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/0ad3b356-d023-444d-bc7a-338211039419_800x992.webp&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:992,&quot;width&quot;:800,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Zoom&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Zoom" title="Zoom" srcset="https://substackcdn.com/image/fetch/$s_!kQew!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0ad3b356-d023-444d-bc7a-338211039419_800x992.webp 424w, https://substackcdn.com/image/fetch/$s_!kQew!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0ad3b356-d023-444d-bc7a-338211039419_800x992.webp 848w, https://substackcdn.com/image/fetch/$s_!kQew!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0ad3b356-d023-444d-bc7a-338211039419_800x992.webp 1272w, https://substackcdn.com/image/fetch/$s_!kQew!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0ad3b356-d023-444d-bc7a-338211039419_800x992.webp 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>For more extensions that complement Mantine, including comparison sliders, reflection effects, and more, browse the curated hub: <a href="https://mantine-extensions.vercel.app/">Mantine Extensions HUB &#8599;</a>. It&#8217;s a great way to find production-ready building blocks that maintain consistency across your app.</p><h2><strong>Final thoughts &#65532;</strong></h2><p><a href="https://gfazioli.github.io/mantine-mask/">Mantine Mask</a> is a small component with outsized impact. Whether you&#8217;re guiding users through content, spotlighting important UI regions, or creating playful reveal effects, the combination of cursor awareness, radial/linear variants, inversion, and feathering lets you tune the exact mood and clarity your design calls for. Drop it in, clamp it to bounds, and watch your interface gain a subtle, professional focus.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://www.undolog.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Undolog is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[Mantine Compare: A Sleek, Accessible Way to Showcase Before/After in React]]></title><description><![CDATA[Crafting seamless before/after comparisons with Mantine&#8212;interactive, accessible, and fully customizable]]></description><link>https://www.undolog.com/p/mantine-compare-a-sleek-accessible</link><guid isPermaLink="false">https://www.undolog.com/p/mantine-compare-a-sleek-accessible</guid><dc:creator><![CDATA[Giovambattista Fazioli]]></dc:creator><pubDate>Fri, 19 Dec 2025 08:30:57 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!xJ6B!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd2628611-2a4d-4581-8a1f-d4551936ca0b_2752x1536.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!xJ6B!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd2628611-2a4d-4581-8a1f-d4551936ca0b_2752x1536.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!xJ6B!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd2628611-2a4d-4581-8a1f-d4551936ca0b_2752x1536.png 424w, https://substackcdn.com/image/fetch/$s_!xJ6B!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd2628611-2a4d-4581-8a1f-d4551936ca0b_2752x1536.png 848w, https://substackcdn.com/image/fetch/$s_!xJ6B!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd2628611-2a4d-4581-8a1f-d4551936ca0b_2752x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!xJ6B!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd2628611-2a4d-4581-8a1f-d4551936ca0b_2752x1536.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!xJ6B!,w_2400,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd2628611-2a4d-4581-8a1f-d4551936ca0b_2752x1536.png" width="1200" height="670.054945054945" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d2628611-2a4d-4581-8a1f-d4551936ca0b_2752x1536.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:false,&quot;imageSize&quot;:&quot;large&quot;,&quot;height&quot;:813,&quot;width&quot;:1456,&quot;resizeWidth&quot;:1200,&quot;bytes&quot;:1772506,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://www.undolog.com/i/181778362?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd2628611-2a4d-4581-8a1f-d4551936ca0b_2752x1536.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:&quot;center&quot;,&quot;offset&quot;:false}" class="sizing-large" alt="" srcset="https://substackcdn.com/image/fetch/$s_!xJ6B!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd2628611-2a4d-4581-8a1f-d4551936ca0b_2752x1536.png 424w, https://substackcdn.com/image/fetch/$s_!xJ6B!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd2628611-2a4d-4581-8a1f-d4551936ca0b_2752x1536.png 848w, https://substackcdn.com/image/fetch/$s_!xJ6B!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd2628611-2a4d-4581-8a1f-d4551936ca0b_2752x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!xJ6B!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd2628611-2a4d-4581-8a1f-d4551936ca0b_2752x1536.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><a href="https://gfazioli.github.io/mantine-compare">Mantine Compare</a> brings polished, responsive side&#8209;by&#8209;side comparisons to your Next.js + TypeScript projects with zero friction.</p><p>If you build product previews, image diff views, A/B demos, or UI changelogs, Mantine Compare is the extension that turns &#8220;quick comparison&#8221; into a delightful experience. Designed to pair perfectly with the Mantine UI Library, it ships with consistent styling, accessibility, and flexible layout controls&#8212;so you can focus on storytelling, not boilerplate.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://www.undolog.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Undolog is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><h2>Why Mantine Compare stands out&nbsp;&#65532;</h2><p>Mantine Compare is an interactive container that reveals two content sections&#8212;images or custom React nodes&#8212;along a movable divider. You get a smooth UI, keyboard&#8209;friendly controls, and a Styles API that lets you tailor every inner element. In practice, this means you can compare two product states, designs, or datasets with just a few lines of code while preserving your brand look and feel.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!-Xqd!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f1fa73d-e5d2-416b-a48a-b15f7edb448f_1900x918.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!-Xqd!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f1fa73d-e5d2-416b-a48a-b15f7edb448f_1900x918.png 424w, https://substackcdn.com/image/fetch/$s_!-Xqd!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f1fa73d-e5d2-416b-a48a-b15f7edb448f_1900x918.png 848w, https://substackcdn.com/image/fetch/$s_!-Xqd!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f1fa73d-e5d2-416b-a48a-b15f7edb448f_1900x918.png 1272w, https://substackcdn.com/image/fetch/$s_!-Xqd!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f1fa73d-e5d2-416b-a48a-b15f7edb448f_1900x918.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!-Xqd!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f1fa73d-e5d2-416b-a48a-b15f7edb448f_1900x918.png" width="1456" height="703" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/5f1fa73d-e5d2-416b-a48a-b15f7edb448f_1900x918.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:703,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1254452,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.undolog.com/i/181778362?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f1fa73d-e5d2-416b-a48a-b15f7edb448f_1900x918.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!-Xqd!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f1fa73d-e5d2-416b-a48a-b15f7edb448f_1900x918.png 424w, https://substackcdn.com/image/fetch/$s_!-Xqd!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f1fa73d-e5d2-416b-a48a-b15f7edb448f_1900x918.png 848w, https://substackcdn.com/image/fetch/$s_!-Xqd!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f1fa73d-e5d2-416b-a48a-b15f7edb448f_1900x918.png 1272w, https://substackcdn.com/image/fetch/$s_!-Xqd!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f1fa73d-e5d2-416b-a48a-b15f7edb448f_1900x918.png 1456w" sizes="100vw"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2>Key highlights:</h2><ul><li><p>Angle control for the divider: vertical, horizontal, or diagonal with any angle between 0&#8211;360.</p></li><li><p>Three interaction variants: drag (default), hover, and fixed&#8212;each suited to different storytelling patterns.</p></li><li><p>Works with any React content: not just images; pass custom components, charts, or annotated boxes.</p></li><li><p>Full Styles API support: style the slider, line, sections, and root with classNames across your theme.</p></li></ul><p>Installation and setup in seconds&nbsp;&#65532;</p><p>Add the package, then import styles at your app root:</p><pre><code>// install
// yarn add @gfazioli/mantine-compare
// or npm i @gfazioli/mantine-compare

// styles: pick one of the following
import &#8216;@gfazioli/mantine-compare/styles.css&#8217;;
// or, with CSS layers
import &#8216;@gfazioli/mantine-compare/styles.layer.css&#8217;;</code></pre><p>With Mantine and Next.js, this fits right into your App Router structure and theming model. If you&#8217;re new to Mantine, start here: the official [Getting Started](https://mantine.dev/getting-started/) guide. For a broader view of community components, explore the [Mantine Extensions HUB](https://mantine-extensions.vercel.app/).</p><h3>Use cases that shine&nbsp;&#65532;</h3><ul><li><p>Product &#8220;Before/After&#8221; reveals: demonstrate upgrades like performance, color correction, or UI polish.</p></li><li><p>Design reviews: compare two versions of a layout, component, or icon set.</p></li><li><p>Data visualizations: show state changes over time with charts or dashboards on either side.</p></li><li><p>Documentation demos: embed in Nextra or MDX pages to make release notes visual and interactive.</p></li></ul><h3>A clean API you&#8217;ll enjoy using&nbsp;&#65532;</h3><p>The core component accepts two sections&#8212;left and right&#8212;and renders them inside a resizable frame:</p><pre><code>import { Compare } from &#8216;@gfazioli/mantine-compare&#8217;;
import { Image } from &#8216;@mantine/core&#8217;;

export function ProductDiff() {
  return (
    &lt;Compare
      leftSection={
        &lt;Image
          src=&#8221;https://images.unsplash.com/photo-1506905925346-21bda4d32df4?w=800&amp;auto=format&amp;fit=crop&#8221;
          alt=&#8221;Before&#8221;
          style={{ width: &#8216;100%&#8217;, height: &#8216;100%&#8217;, objectFit: &#8216;cover&#8217; }}
        /&gt;
      }
      rightSection={
        &lt;Image
          src=&#8221;https://images.unsplash.com/photo-1519681393784-d120267933ba?w=800&amp;auto=format&amp;fit=crop&#8221;
          alt=&#8221;After&#8221;
          style={{ width: &#8216;100%&#8217;, height: &#8216;100%&#8217;, objectFit: &#8216;cover&#8217; }}
        /&gt;
      }
    /&gt;
  );
}
</code></pre><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!WJ2X!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F85c50b5c-26eb-4faf-8756-272f1f6ce611_1924x1744.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!WJ2X!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F85c50b5c-26eb-4faf-8756-272f1f6ce611_1924x1744.png 424w, https://substackcdn.com/image/fetch/$s_!WJ2X!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F85c50b5c-26eb-4faf-8756-272f1f6ce611_1924x1744.png 848w, https://substackcdn.com/image/fetch/$s_!WJ2X!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F85c50b5c-26eb-4faf-8756-272f1f6ce611_1924x1744.png 1272w, https://substackcdn.com/image/fetch/$s_!WJ2X!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F85c50b5c-26eb-4faf-8756-272f1f6ce611_1924x1744.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!WJ2X!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F85c50b5c-26eb-4faf-8756-272f1f6ce611_1924x1744.png" width="1456" height="1320" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/85c50b5c-26eb-4faf-8756-272f1f6ce611_1924x1744.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1320,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:2656123,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.undolog.com/i/181778362?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F85c50b5c-26eb-4faf-8756-272f1f6ce611_1924x1744.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!WJ2X!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F85c50b5c-26eb-4faf-8756-272f1f6ce611_1924x1744.png 424w, https://substackcdn.com/image/fetch/$s_!WJ2X!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F85c50b5c-26eb-4faf-8756-272f1f6ce611_1924x1744.png 848w, https://substackcdn.com/image/fetch/$s_!WJ2X!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F85c50b5c-26eb-4faf-8756-272f1f6ce611_1924x1744.png 1272w, https://substackcdn.com/image/fetch/$s_!WJ2X!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F85c50b5c-26eb-4faf-8756-272f1f6ce611_1924x1744.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h3>Need a horizontal or diagonal split? Set the angle:</h3><ul><li><p><code>angle={0}</code>: classic vertical divider (left/right).</p></li><li><p><code>angle={90}</code>: horizontal divider (top/bottom).</p></li><li><p><code>angle={30}</code> (or any 0&#8211;360): diagonal for more dynamic reveals.</p></li></ul><pre><code>&lt;Compare angle={30} leftSection={...} rightSection={...} /&gt;</code></pre><h3>Prefer non&#8209;interactive reveals? Choose a variant:</h3><ul><li><p><code>drag</code> (default): click and drag the slider.</p></li><li><p><code>hover</code>: reveal by moving the cursor; no slider button.</p></li><li><p><code>fixed</code>: static split at a preset percentage via defaultPosition.</p></li></ul><pre><code>&lt;Compare variant=&#8221;fixed&#8221; defaultPosition={35} leftSection={...} rightSection={...} /&gt;</code></pre><h2>Styling with precision&nbsp;&#65532;</h2><p><a href="https://gfazioli.github.io/mantine-compare">Mantine Compare</a> embraces Mantine&#8217;s Styles API, exposing classNames hooks like root, leftSection, rightSection, slider, sliderLine, and sliderButton. This gives you granular control to sync with your theme, support dark mode, and integrate brand motion or colors&#8212;without hacking CSS.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!k9R7!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F22529f7b-b274-4f86-9ed1-ef56cc607c5a_1916x1782.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!k9R7!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F22529f7b-b274-4f86-9ed1-ef56cc607c5a_1916x1782.png 424w, https://substackcdn.com/image/fetch/$s_!k9R7!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F22529f7b-b274-4f86-9ed1-ef56cc607c5a_1916x1782.png 848w, https://substackcdn.com/image/fetch/$s_!k9R7!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F22529f7b-b274-4f86-9ed1-ef56cc607c5a_1916x1782.png 1272w, https://substackcdn.com/image/fetch/$s_!k9R7!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F22529f7b-b274-4f86-9ed1-ef56cc607c5a_1916x1782.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!k9R7!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F22529f7b-b274-4f86-9ed1-ef56cc607c5a_1916x1782.png" width="1456" height="1354" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/22529f7b-b274-4f86-9ed1-ef56cc607c5a_1916x1782.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1354,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1319805,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.undolog.com/i/181778362?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F22529f7b-b274-4f86-9ed1-ef56cc607c5a_1916x1782.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!k9R7!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F22529f7b-b274-4f86-9ed1-ef56cc607c5a_1916x1782.png 424w, https://substackcdn.com/image/fetch/$s_!k9R7!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F22529f7b-b274-4f86-9ed1-ef56cc607c5a_1916x1782.png 848w, https://substackcdn.com/image/fetch/$s_!k9R7!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F22529f7b-b274-4f86-9ed1-ef56cc607c5a_1916x1782.png 1272w, https://substackcdn.com/image/fetch/$s_!k9R7!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F22529f7b-b274-4f86-9ed1-ef56cc607c5a_1916x1782.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Example targets:</p><ul><li><p><code>root</code>: the container</p></li><li><p><code>leftSection/rightSection</code>: wrappers for content panes</p></li><li><p><code>slider/sliderLine</code>: the draggable element and its accent line</p></li><li><p><code>sliderButton</code>: the clickable area for accessibility and hit target</p></li></ul><h2>Built for modern Mantine workflows&nbsp;&#65532;</h2><ul><li><p>Accessible by default: focus targets and interaction affordances are thoughtfully designed.</p></li><li><p>Responsive: fills parent dimensions and adapts to different aspect ratios.</p></li><li><p>Composable: accepts any React node&#8212;Mantine components, charts, or custom containers.</p></li><li><p>TypeScript&#8209;friendly: props and variants are straightforward and IDE&#8209;discoverable.</p></li></ul><p>If you develop with Mantine daily, Mantine Compare feels native&#8212;familiar props, predictable styling, and polished UX out of the box.</p><h2>Where to explore more&nbsp;&#65532;</h2><ul><li><p><a href="https://mantine.dev/getting-started">Mantine UI Library</a></p></li><li><p><a href="https://mantine-extensions.vercel.app">Mantine Extensions HUB</a></p></li></ul><p>These resources give you everything you need to scaffold, theme, and document your comparisons in production&#8209;ready apps.</p><h2>Final thoughts&nbsp;&#65532;</h2><p><a href="https://gfazioli.github.io/mantine-compare">Mantine Compare</a> turns content comparison into a premium, interactive experience. Whether you&#8217;re shipping product showcases, design diffs, or data reveals, it&#8217;s a small component that makes a big impression&#8212;and it plugs seamlessly into your Next.js + TypeScript + Mantine stack. Install it, style it, and start telling better visual stories today.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://www.undolog.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Undolog is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[Mantine BorderAnimate: Light up your UI with elegant, high‑performance animated borders ]]></title><description><![CDATA[Elegant animated borders for Mantine &#8212; customizable, performant, and effortless.]]></description><link>https://www.undolog.com/p/mantine-borderanimate-light-up-your</link><guid isPermaLink="false">https://www.undolog.com/p/mantine-borderanimate-light-up-your</guid><dc:creator><![CDATA[Giovambattista Fazioli]]></dc:creator><pubDate>Fri, 05 Dec 2025 09:29:56 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!Ncdk!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F657d9547-497e-4dfd-8d1e-5cff699a5446_2752x1536.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Ncdk!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F657d9547-497e-4dfd-8d1e-5cff699a5446_2752x1536.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Ncdk!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F657d9547-497e-4dfd-8d1e-5cff699a5446_2752x1536.png 424w, https://substackcdn.com/image/fetch/$s_!Ncdk!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F657d9547-497e-4dfd-8d1e-5cff699a5446_2752x1536.png 848w, https://substackcdn.com/image/fetch/$s_!Ncdk!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F657d9547-497e-4dfd-8d1e-5cff699a5446_2752x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!Ncdk!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F657d9547-497e-4dfd-8d1e-5cff699a5446_2752x1536.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Ncdk!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F657d9547-497e-4dfd-8d1e-5cff699a5446_2752x1536.png" width="1456" height="813" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/657d9547-497e-4dfd-8d1e-5cff699a5446_2752x1536.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:813,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:2168614,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://www.undolog.com/i/180780913?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F657d9547-497e-4dfd-8d1e-5cff699a5446_2752x1536.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!Ncdk!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F657d9547-497e-4dfd-8d1e-5cff699a5446_2752x1536.png 424w, https://substackcdn.com/image/fetch/$s_!Ncdk!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F657d9547-497e-4dfd-8d1e-5cff699a5446_2752x1536.png 848w, https://substackcdn.com/image/fetch/$s_!Ncdk!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F657d9547-497e-4dfd-8d1e-5cff699a5446_2752x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!Ncdk!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F657d9547-497e-4dfd-8d1e-5cff699a5446_2752x1536.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Elevate your React interfaces with <strong><a href="https://gfazioli.github.io/mantine-border-animate/">BorderAnimate</a></strong>, a polished Mantine extension that wraps any element&#8212;cards, buttons, inputs, alerts&#8212;and adds stunning, configurable border animations without sacrificing performance. If you&#8217;re building with Next.js and TypeScript and already rely on <strong>Mantine UI</strong>, this component feels like a natural superpower upgrade to your design system.</p><p><strong>Why BorderAnimate belongs in modern Mantine projects</strong></p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://www.undolog.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Undolog is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p>Visual feedback and motion are essential to contemporary UI. <a href="https://gfazioli.github.io/mantine-border-animate/">BorderAnimate</a> delivers a carefully engineered set of animation variants that draw attention tastefully&#8212;perfect for highlights, CTAs, focus states, and premium surfaces&#8212;while keeping your app buttery smooth via <strong>CSS animations at 60fps</strong>. </p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!7BUy!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa42243ba-693c-46db-8c5a-37e95bc02604_1388x120.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!7BUy!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa42243ba-693c-46db-8c5a-37e95bc02604_1388x120.png 424w, https://substackcdn.com/image/fetch/$s_!7BUy!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa42243ba-693c-46db-8c5a-37e95bc02604_1388x120.png 848w, https://substackcdn.com/image/fetch/$s_!7BUy!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa42243ba-693c-46db-8c5a-37e95bc02604_1388x120.png 1272w, https://substackcdn.com/image/fetch/$s_!7BUy!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa42243ba-693c-46db-8c5a-37e95bc02604_1388x120.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!7BUy!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa42243ba-693c-46db-8c5a-37e95bc02604_1388x120.png" width="1388" height="120" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/a42243ba-693c-46db-8c5a-37e95bc02604_1388x120.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:120,&quot;width&quot;:1388,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:33721,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.undolog.com/i/180780913?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa42243ba-693c-46db-8c5a-37e95bc02604_1388x120.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!7BUy!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa42243ba-693c-46db-8c5a-37e95bc02604_1388x120.png 424w, https://substackcdn.com/image/fetch/$s_!7BUy!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa42243ba-693c-46db-8c5a-37e95bc02604_1388x120.png 848w, https://substackcdn.com/image/fetch/$s_!7BUy!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa42243ba-693c-46db-8c5a-37e95bc02604_1388x120.png 1272w, https://substackcdn.com/image/fetch/$s_!7BUy!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa42243ba-693c-46db-8c5a-37e95bc02604_1388x120.png 1456w" sizes="100vw"></picture><div></div></div></a></figure></div><p>Installation is straightforward, theming is intuitive, and the API aligns perfectly with Mantine&#8217;s props-driven ergonomics.</p><p><strong>Quick start</strong></p><p>Add styles at your app root:</p><pre><code>import &#8216;@gfazioli/mantine-border-animate/styles.css&#8217;;
// or scoped layer version:
import &#8216;@gfazioli/mantine-border-animate/styles.layer.css&#8217;;</code></pre><p>Wrap any content:</p><pre><code>import { BorderAnimate } from &#8216;@gfazioli/mantine-border-animate&#8217;;
import { Flex, Title, Text } from &#8216;@mantine/core&#8217;;

export function Demo() {
  return (
    &lt;BorderAnimate w={500} h={400}&gt;
      &lt;Flex
        flex={1}
        direction=&#8221;column&#8221;
        align=&#8221;center&#8221;
        justify=&#8221;center&#8221;
        h=&#8221;100%&#8221;
        style={{ borderRadius: &#8216;inherit&#8217;, backgroundColor: &#8216;var(--mantine-color-default)&#8217; }}
      &gt;
        &lt;Title&gt;Animate Border&lt;/Title&gt;
        &lt;Text&gt;This is an example of BorderAnimate component&lt;/Text&gt;
      &lt;/Flex&gt;
    &lt;/BorderAnimate&gt;
  );
}</code></pre><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!M0vt!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb9b98396-1ac1-440e-9601-364e62627ad4_1632x1342.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!M0vt!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb9b98396-1ac1-440e-9601-364e62627ad4_1632x1342.png 424w, https://substackcdn.com/image/fetch/$s_!M0vt!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb9b98396-1ac1-440e-9601-364e62627ad4_1632x1342.png 848w, https://substackcdn.com/image/fetch/$s_!M0vt!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb9b98396-1ac1-440e-9601-364e62627ad4_1632x1342.png 1272w, https://substackcdn.com/image/fetch/$s_!M0vt!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb9b98396-1ac1-440e-9601-364e62627ad4_1632x1342.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!M0vt!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb9b98396-1ac1-440e-9601-364e62627ad4_1632x1342.png" width="1456" height="1197" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/b9b98396-1ac1-440e-9601-364e62627ad4_1632x1342.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1197,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:137012,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.undolog.com/i/180780913?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb9b98396-1ac1-440e-9601-364e62627ad4_1632x1342.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!M0vt!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb9b98396-1ac1-440e-9601-364e62627ad4_1632x1342.png 424w, https://substackcdn.com/image/fetch/$s_!M0vt!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb9b98396-1ac1-440e-9601-364e62627ad4_1632x1342.png 848w, https://substackcdn.com/image/fetch/$s_!M0vt!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb9b98396-1ac1-440e-9601-364e62627ad4_1632x1342.png 1272w, https://substackcdn.com/image/fetch/$s_!M0vt!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb9b98396-1ac1-440e-9601-364e62627ad4_1632x1342.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><strong>Four variants, endless polish</strong></p><ul><li><p>Beam: a traveling glow that traces your border radius&#8212;great for interactive emphasis. Tune with size, anchor, and duration.</p></li><li><p>Gradient: a rotating conic gradient between colorFrom and colorTo&#8212;ideal for premium visuals and loaders. Control duration and blur.</p></li><li><p>Glow: a gentle pulsation that fills the border area&#8212;perfect for notifications or attention states. Adjust duration, blur, opacity.</p></li><li><p>Pulse: subtle scale + opacity breathing for refined emphasis&#8212;minimal yet expressive.</p></li></ul><p>Switch via the variant prop and fine&#8209;tune with props like colorFrom, colorTo, size, blur, duration, border width, radius, and more.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!dfV-!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fde28bdaa-d1b0-40a4-a394-b0dd38f4739a_1632x808.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!dfV-!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fde28bdaa-d1b0-40a4-a394-b0dd38f4739a_1632x808.png 424w, https://substackcdn.com/image/fetch/$s_!dfV-!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fde28bdaa-d1b0-40a4-a394-b0dd38f4739a_1632x808.png 848w, https://substackcdn.com/image/fetch/$s_!dfV-!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fde28bdaa-d1b0-40a4-a394-b0dd38f4739a_1632x808.png 1272w, https://substackcdn.com/image/fetch/$s_!dfV-!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fde28bdaa-d1b0-40a4-a394-b0dd38f4739a_1632x808.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!dfV-!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fde28bdaa-d1b0-40a4-a394-b0dd38f4739a_1632x808.png" width="1456" height="721" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/de28bdaa-d1b0-40a4-a394-b0dd38f4739a_1632x808.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:721,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:259226,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.undolog.com/i/180780913?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fde28bdaa-d1b0-40a4-a394-b0dd38f4739a_1632x808.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!dfV-!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fde28bdaa-d1b0-40a4-a394-b0dd38f4739a_1632x808.png 424w, https://substackcdn.com/image/fetch/$s_!dfV-!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fde28bdaa-d1b0-40a4-a394-b0dd38f4739a_1632x808.png 848w, https://substackcdn.com/image/fetch/$s_!dfV-!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fde28bdaa-d1b0-40a4-a394-b0dd38f4739a_1632x808.png 1272w, https://substackcdn.com/image/fetch/$s_!dfV-!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fde28bdaa-d1b0-40a4-a394-b0dd38f4739a_1632x808.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><strong>Controlled and predictable</strong></p><p>BorderAnimate is fully controllable:</p><ul><li><p>show: toggle border visibility for conditional states.</p></li><li><p>animate: keep motion on or make it static; combine with angle for directional control.<br>This makes it effortless to integrate with app state, forms, and feature flags.</p></li></ul><p><strong>Mask and background effects</strong></p><p>For advanced visuals, pair with withMask and zIndex:</p><ul><li><p>withMask=true clips the animation to the border area.</p></li><li><p>withMask=false lets the glow soft&#8209;spill beyond the element for ambient highlights.</p></li><li><p>Combine anchor with zIndex={-1} to create tasteful background illumination behind content&#8212;beautiful for hero cards, pricing blocks, or featured widgets.</p></li></ul><p>Example:</p><pre><code>&lt;BorderAnimate w={200} h={150} withMask={false} size={200} blur={4} anchor={40}&gt;
  {/* content with var(--mantine-radius-md) to match your theme */}
&lt;/BorderAnimate&gt;</code></pre><p><strong>Built to feel Mantine&#8209;native</strong></p><p>BorderAnimate respects Mantine&#8217;s design language:</p><ul><li><p>Works with any Mantine component: Paper, Button, Card, Flex, Stack, etc.</p></li><li><p>Inherits your border radius and colors via CSS custom properties.</p></li><li><p>Plays nicely with Mantine&#8217;s <strong>Style Props</strong> and <strong>Styles API</strong>, making theming straightforward and predictable.</p></li></ul><p>Explore Mantine UI&#8217;s foundations and best practices here: <a href="https://mantine.dev/getting-started/">Mantine UI Getting Started &#8599;</a></p><p><strong>Perfect for Next.js + TypeScript teams</strong></p><ul><li><p>Type&#8209;safe props and predictable APIs.</p></li><li><p>Minimal bundle footprint; pure CSS animations for performance.</p></li><li><p>Drop&#8209;in, componentized design that fits Server Components layouts and client interactivity.</p></li></ul><p><strong>Where it shines in production</strong></p><ul><li><p>Featured product cards and pricing sections that need premium polish.</p></li><li><p>CTA buttons that warrant subtle motion without distraction.</p></li><li><p>Focus states for inputs and forms when accessibility needs visual clarity.</p></li><li><p>Status indicators and alerts that benefit from gentle animated emphasis.</p></li><li><p>Onboarding moments and spotlight panels using background glow with withMask=false.</p></li></ul><p><strong>Try it, then level up your Mantine stack</strong></p><p>Mantine BorderAnimate is part of a growing ecosystem of high&#8209;quality enhancements. </p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://www.producthunt.com/products/mantine-ui-extensions-hub?embed=true&amp;utm_source=badge-featured&amp;utm_medium=badge&amp;utm_source=badge-mantine&amp;#0045;ui&amp;#0045;extensions&amp;#0045;hub&quot;,&quot;text&quot;:&quot;Vote on Product Hunt&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://www.producthunt.com/products/mantine-ui-extensions-hub?embed=true&amp;utm_source=badge-featured&amp;utm_medium=badge&amp;utm_source=badge-mantine&amp;#0045;ui&amp;#0045;extensions&amp;#0045;hub"><span>Vote on Product Hunt</span></a></p><p>Discover more components tailored for Mantine at the community hub: <a href="https://mantine-extensions.vercel.app/">Mantine Extensions HUB &#8599;</a>. If you&#8217;re standardizing your design system in Next.js, these extensions accelerate UI innovation while staying consistent with Mantine&#8217;s principles.</p><div class="native-video-embed" data-component-name="VideoPlaceholder" data-attrs="{&quot;mediaUploadId&quot;:&quot;ec463967-b2e4-4c11-868c-2786506f9e03&quot;,&quot;duration&quot;:null}"></div><p><strong>Final thoughts</strong></p><p>BorderAnimate adds refined motion to your interfaces with the craftsmanship you expect from <strong>Mantine</strong>&#8209;aligned components. It&#8217;s simple to adopt, delightful to tune, and powerful enough to become a signature detail across your app. Wrap, configure, and let your borders come alive&#8212;responsively, accessibly, and beautifully.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://www.undolog.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Undolog is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[Mantine Window: Desktop‑style power for your React apps ]]></title><description><![CDATA[Mantine Window extends Mantine UI with draggable, resizable, and collapsible desktop-style windows, ideal for creating dynamic dashboards, admin interfaces, and interactive web applications.]]></description><link>https://www.undolog.com/p/mantine-window-desktopstyle-power</link><guid isPermaLink="false">https://www.undolog.com/p/mantine-window-desktopstyle-power</guid><dc:creator><![CDATA[Giovambattista Fazioli]]></dc:creator><pubDate>Fri, 28 Nov 2025 11:35:20 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!_oQp!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4156467e-e336-4332-b677-042db3ed2910_2752x1536.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!_oQp!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4156467e-e336-4332-b677-042db3ed2910_2752x1536.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!_oQp!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4156467e-e336-4332-b677-042db3ed2910_2752x1536.png 424w, https://substackcdn.com/image/fetch/$s_!_oQp!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4156467e-e336-4332-b677-042db3ed2910_2752x1536.png 848w, https://substackcdn.com/image/fetch/$s_!_oQp!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4156467e-e336-4332-b677-042db3ed2910_2752x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!_oQp!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4156467e-e336-4332-b677-042db3ed2910_2752x1536.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!_oQp!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4156467e-e336-4332-b677-042db3ed2910_2752x1536.png" width="1456" height="813" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/4156467e-e336-4332-b677-042db3ed2910_2752x1536.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:813,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:2346410,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://www.undolog.com/i/179999594?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4156467e-e336-4332-b677-042db3ed2910_2752x1536.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!_oQp!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4156467e-e336-4332-b677-042db3ed2910_2752x1536.png 424w, https://substackcdn.com/image/fetch/$s_!_oQp!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4156467e-e336-4332-b677-042db3ed2910_2752x1536.png 848w, https://substackcdn.com/image/fetch/$s_!_oQp!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4156467e-e336-4332-b677-042db3ed2910_2752x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!_oQp!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4156467e-e336-4332-b677-042db3ed2910_2752x1536.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2>Why Mantine Window stands out</h2><p><a href="https://gfazioli.github.io/mantine-window/">Mantine Window </a>wraps advanced interaction patterns in a straightforward API that feels native to Mantine. You get smooth dragging, resizing, collapsing, close controls, configurable shadows and radii, and a smart persistence layer&#8212;without reinventing the wheel.</p><p>If you already use Mantine components and theming, this slots in effortlessly. Start here: <a href="https://gfazioli.github.io/mantine-window/">Mantine Window &#8599;</a> and explore more extensions on the community <strong>Mantine Extensions HUB</strong>: <a href="https://mantine-extensions.vercel.app/">mantine-extensions.vercel.app &#8599;</a>.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://www.undolog.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Undolog is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><h3>Quick start</h3><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Q7FW!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6ec6d1e2-0f2f-468a-aa0c-8b4be9476967_1224x838.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Q7FW!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6ec6d1e2-0f2f-468a-aa0c-8b4be9476967_1224x838.png 424w, https://substackcdn.com/image/fetch/$s_!Q7FW!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6ec6d1e2-0f2f-468a-aa0c-8b4be9476967_1224x838.png 848w, https://substackcdn.com/image/fetch/$s_!Q7FW!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6ec6d1e2-0f2f-468a-aa0c-8b4be9476967_1224x838.png 1272w, https://substackcdn.com/image/fetch/$s_!Q7FW!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6ec6d1e2-0f2f-468a-aa0c-8b4be9476967_1224x838.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Q7FW!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6ec6d1e2-0f2f-468a-aa0c-8b4be9476967_1224x838.png" width="1224" height="838" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/6ec6d1e2-0f2f-468a-aa0c-8b4be9476967_1224x838.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:838,&quot;width&quot;:1224,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:269506,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.undolog.com/i/179999594?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6ec6d1e2-0f2f-468a-aa0c-8b4be9476967_1224x838.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!Q7FW!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6ec6d1e2-0f2f-468a-aa0c-8b4be9476967_1224x838.png 424w, https://substackcdn.com/image/fetch/$s_!Q7FW!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6ec6d1e2-0f2f-468a-aa0c-8b4be9476967_1224x838.png 848w, https://substackcdn.com/image/fetch/$s_!Q7FW!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6ec6d1e2-0f2f-468a-aa0c-8b4be9476967_1224x838.png 1272w, https://substackcdn.com/image/fetch/$s_!Q7FW!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6ec6d1e2-0f2f-468a-aa0c-8b4be9476967_1224x838.png 1456w" sizes="100vw"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Install and import styles at your app root:</p><pre><code>import &#8216;@gfazioli/mantine-window/styles.css&#8217;;
// or scoped layer support:
import &#8216;@gfazioli/mantine-window/styles.layer.css&#8217;;</code></pre><p>Then drop a window into any container:</p><pre><code>import { Window } from &#8216;@gfazioli/mantine-window&#8217;;
import { Box, Title } from &#8216;@mantine/core&#8217;;

export function Demo() {
  return (
    &lt;Box pos=&#8221;relative&#8221; style={{ width: &#8216;100%&#8217;, height: 500 }}&gt;
      &lt;Window
        title=&#8221;Hello, Mantine Window&#8221;
        defaultPosition={{ x: 0, y: 0 }}
        defaultSize={{ width: 320, height: 256 }}
        withinPortal={false}
        opened
      &gt;
        &lt;Title order={4}&gt;This is a window with data&lt;/Title&gt;
      &lt;/Window&gt;
    &lt;/Box&gt;
  );
}</code></pre><h3>Portal vs. container: control positioning</h3><p>Choose exactly how windows behave:</p><ul><li><p>Fixed via portal: &#8206;<code>withinPortal={true}</code> keeps the window pinned to the viewport&#8212;ideal for overlays and modals that ignore scroll.</p></li><li><p>Relative within a container: &#8206;<code>withinPortal={false}</code> uses absolute positioning inside a &#8206;<code>position: relative</code> parent&#8212;great for embedded widgets and constrained canvases.</p></li></ul><h3>Fully controlled or fire&#8209;and&#8209;forget</h3><p>Drive visibility with &#8206;<code>opened</code> and <code>onClose</code> for modal&#8209;style flows, or keep it simple using <code>defaultPosition</code> and &#8206;<code>defaultSize</code>. It&#8217;s React&#8209;friendly either way.</p><pre><code>const [opened, { open, close }] = useDisclosure(false);
&lt;Window title=&#8221;Controlled Window&#8221; opened={opened} onClose={close} withinPortal={false} /&gt;</code></pre><h3>Precision movement: drag boundaries</h3><p>Lock window movement to specific ranges using &#8206;<code>dragBounds</code> so your UI stays tidy and intentional&#8212;for example, dashboards with hard limits:</p><pre><code>dragBounds={{ minX: 50, maxX: 500, minY: 50, maxY: 400 }}</code></pre><h3>Persistence that delights</h3><p>With &#8206;<code>persistState</code> (enabled by default), position and size are remembered via localStorage. Refresh the page, and your layout feels personal&#8212;not fragile.</p><p>Add an &#8206;<code>id</code> for multiple distinct windows:</p><pre><code>&lt;Window id=&#8221;analytics-panel&#8221; persistState defaultPosition={{ x: 50, y: 50 }} /&gt;</code></pre><h3>Callbacks for analytics and UX</h3><p>React to user intent with <code>onPositionChange</code> and &#8206;<code>onSizeChange</code>. Log telemetry, snap to a grid, or update layout state in real time:</p><pre><code>onPositionChange={(pos) =&gt; console.log(pos)}
onSizeChange={(size) =&gt; console.log(size)}</code></pre><h3>Lock it down when needed</h3><p>Create a centered, fixed window and disable interactions:</p><ul><li><p>&#8206;<code>draggable=&#8221;none&#8221;</code></p></li><li><p>&#8206;<code>resizable=&#8221;none&#8221;</code></p></li><li><p>Supply &#8206;<code>defaultPosition</code> and &#8206;<code>defaultSize</code> for a crisp, modal&#8209;like panel.</p></li></ul><p>You can also disable collapsing entirely with &#8206;<code>collapsable={false}</code> for deterministic flows.</p><h3>Styles API: theme it like Mantine</h3><p>Mantine Window embraces Mantine&#8217;s <strong>Styles API</strong>, letting you style inner parts with &#8206;<code>classNames</code> and your theme tokens. It plays nicely with color schemes, radius, shadows, borders, and any design system rules you enforce through &#8206;<code>MantineProvider</code>.</p><p><strong>Perfect use cases</strong></p><ul><li><p>Analytics dashboards with movable panels</p></li><li><p>Admin tools with modular widgets</p></li><li><p>In&#8209;app editors and inspectors</p></li><li><p>Multi&#8209;window experiences with persistence</p></li><li><p>Demo environments that need windowed layouts</p></li></ul><p><strong>Why teams adopt Mantine Window</strong></p><ul><li><p>It&#8217;s familiar: Component props and patterns mirror Mantine conventions.</p></li><li><p>It&#8217;s robust: Drag/resizing, boundaries, persistence, and callbacks cover real production needs.</p></li><li><p>It&#8217;s flexible: Portal vs container makes the right placement trivial.</p></li><li><p>It&#8217;s maintainable: The Styles API keeps design aligned across your system.</p></li></ul><p>If you&#8217;re building with Mantine, Mantine Window adds the missing desktop&#8209;style interactions your users subconsciously expect&#8212;clean, predictable, and delightful. Dive into <strong>Mantine</strong> first at <a href="https://mantine.dev/getting-started/">https://mantine.dev/getting-started/ &#8599;</a> and browse more community&#8209;maintained gems at the <strong>Mantine Extensions HUB</strong>: <a href="https://mantine-extensions.vercel.app/">https://mantine-extensions.vercel.app/ &#8599;</a>.</p><div class="native-video-embed" data-component-name="VideoPlaceholder" data-attrs="{&quot;mediaUploadId&quot;:&quot;d65e4d25-2839-4a74-bd2c-48cf18a191e5&quot;,&quot;duration&quot;:null}"></div><p></p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://www.undolog.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Undolog is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[JsonTree for Mantine UI: A Delightfully Simple Way to Inspect Any Data Structure]]></title><description><![CDATA[A lightweight, flexible tree viewer for strings, numbers, booleans, nulls, arrays, and objects&#8212;built for Mantine UI, with zero configuration and rich customization when you need it.]]></description><link>https://www.undolog.com/p/jsontree-for-mantine-ui-a-delightfully</link><guid isPermaLink="false">https://www.undolog.com/p/jsontree-for-mantine-ui-a-delightfully</guid><dc:creator><![CDATA[Giovambattista Fazioli]]></dc:creator><pubDate>Mon, 17 Nov 2025 08:01:18 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!DaPQ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6469a712-0178-451a-a795-87b6c5fd1dd3_2752x1536.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!DaPQ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6469a712-0178-451a-a795-87b6c5fd1dd3_2752x1536.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!DaPQ!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6469a712-0178-451a-a795-87b6c5fd1dd3_2752x1536.png 424w, https://substackcdn.com/image/fetch/$s_!DaPQ!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6469a712-0178-451a-a795-87b6c5fd1dd3_2752x1536.png 848w, https://substackcdn.com/image/fetch/$s_!DaPQ!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6469a712-0178-451a-a795-87b6c5fd1dd3_2752x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!DaPQ!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6469a712-0178-451a-a795-87b6c5fd1dd3_2752x1536.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!DaPQ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6469a712-0178-451a-a795-87b6c5fd1dd3_2752x1536.png" width="1456" height="813" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/6469a712-0178-451a-a795-87b6c5fd1dd3_2752x1536.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:813,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1918336,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://www.undolog.com/i/178913892?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6469a712-0178-451a-a795-87b6c5fd1dd3_2752x1536.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!DaPQ!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6469a712-0178-451a-a795-87b6c5fd1dd3_2752x1536.png 424w, https://substackcdn.com/image/fetch/$s_!DaPQ!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6469a712-0178-451a-a795-87b6c5fd1dd3_2752x1536.png 848w, https://substackcdn.com/image/fetch/$s_!DaPQ!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6469a712-0178-451a-a795-87b6c5fd1dd3_2752x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!DaPQ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6469a712-0178-451a-a795-87b6c5fd1dd3_2752x1536.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>If you build React apps with <a href="https://mantine.dev/)">Mantine UI</a>, you already value clarity, consistency, and speed. <a href="https://gfazioli.github.io/mantine-json-tree/">JsonTree</a> continues that tradition: it renders any JavaScript value&#8212;primitives, arrays, and objects&#8212;into an interactive tree with minimal effort. Drop it in, point it to data, and you&#8217;re done. When you need more, it offers clean extension points and Mantine-friendly styling.</p><p>Why JsonTree</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://www.undolog.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Undolog is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><ul><li><p>Works with any value: <code>string</code>, <code>number</code>, <code>boolean</code>, <code>null</code>, <code>array</code>, <code>object</code>.</p></li><li><p>Instant visibility: expand/collapse nodes to navigate complex payloads.</p></li><li><p>Mantine-first: designed to fit your theme, typography, spacing, and dark mode.</p></li><li><p>Zero-config by default; composable when you need control.</p></li></ul><h2>Quick start examples</h2><p>The following snippets demonstrate how <a href="https://gfazioli.github.io/mantine-json-tree/">JsonTree</a> gracefully renders different data types. Each example uses <code>defaultExpanded</code> for immediate visibility and an optional title for context.</p><pre><code>//data.js
export const data = {
  name: &#8216;John Doe&#8217;,
  age: 30,
  isAdmin: false,
  courses: [&#8217;html&#8217;, &#8216;css&#8217;, &#8216;js&#8217;],
  wife: null,
  address: {
    street: &#8216;123 Main St&#8217;,
    city: &#8216;Anytown&#8217;,
    zip: &#8216;12345&#8217;,
  },
  action: { type: &#8216;click&#8217;, payload: undefined },
  projects: [
    {
      name: &#8216;Project A&#8217;,
      status: &#8216;completed&#8217;,
    },
    {
      name: &#8216;Project B&#8217;,
      status: &#8216;in progress&#8217;,
    },
  ],
};</code></pre><pre><code><code>import { JsonTree } from &#8220;@gfazioli/mantine-json-tree&#8221;;
import { data } from &#8216;./data&#8217;;

function Demo() {
  return &lt;JsonTree showIndentGuides data={data} maxDepth={1} defaultExpanded/&gt;;
}</code>
</code></pre><h2>Full demo with Mantine layout</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!5OPZ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd13768ba-3ce8-4d16-8de7-52d25b776dd5_1676x1648.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!5OPZ!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd13768ba-3ce8-4d16-8de7-52d25b776dd5_1676x1648.png 424w, https://substackcdn.com/image/fetch/$s_!5OPZ!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd13768ba-3ce8-4d16-8de7-52d25b776dd5_1676x1648.png 848w, https://substackcdn.com/image/fetch/$s_!5OPZ!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd13768ba-3ce8-4d16-8de7-52d25b776dd5_1676x1648.png 1272w, https://substackcdn.com/image/fetch/$s_!5OPZ!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd13768ba-3ce8-4d16-8de7-52d25b776dd5_1676x1648.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!5OPZ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd13768ba-3ce8-4d16-8de7-52d25b776dd5_1676x1648.png" width="1456" height="1432" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d13768ba-3ce8-4d16-8de7-52d25b776dd5_1676x1648.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1432,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:434360,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.undolog.com/i/178913892?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd13768ba-3ce8-4d16-8de7-52d25b776dd5_1676x1648.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!5OPZ!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd13768ba-3ce8-4d16-8de7-52d25b776dd5_1676x1648.png 424w, https://substackcdn.com/image/fetch/$s_!5OPZ!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd13768ba-3ce8-4d16-8de7-52d25b776dd5_1676x1648.png 848w, https://substackcdn.com/image/fetch/$s_!5OPZ!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd13768ba-3ce8-4d16-8de7-52d25b776dd5_1676x1648.png 1272w, https://substackcdn.com/image/fetch/$s_!5OPZ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd13768ba-3ce8-4d16-8de7-52d25b776dd5_1676x1648.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Wrap <a href="https://gfazioli.github.io/mantine-json-tree/">JsonTree</a> with familiar Mantine components like <code>Stack</code> and <code>Paper</code> to get polished spacing, borders, and responsiveness out of the box:</p><pre><code>import { JsonTree } from &#8220;@gfazioli/mantine-json-tree&#8221;;
import { data } from &#8216;./data&#8217;;

function Demo() {
  return (
    &lt;Stack&gt;
      &lt;Paper withBorder&gt;
        &lt;JsonTree data=&#8221;Hello, World!&#8221; defaultExpanded title=&#8221;Simple string&#8221; /&gt;
      &lt;/Paper&gt;
      &lt;Paper withBorder&gt;
        &lt;JsonTree data={42} defaultExpanded title=&#8221;Number value&#8221; /&gt;
      &lt;/Paper&gt;
      &lt;Paper withBorder&gt;
        &lt;JsonTree data={true} defaultExpanded title=&#8221;Boolean value (true)&#8221; /&gt;
      &lt;/Paper&gt;
      &lt;Paper withBorder&gt;
        &lt;JsonTree data={false} defaultExpanded title=&#8221;Boolean value (false)&#8221; /&gt;
      &lt;/Paper&gt;
      &lt;Paper withBorder&gt;
        &lt;JsonTree data={null} defaultExpanded title=&#8221;Null value&#8221; /&gt;
      &lt;/Paper&gt;
      &lt;Paper withBorder&gt;
        &lt;JsonTree data={{ key1: &#8216;value1&#8217;, key2: 123, key3: false, key4: null }} defaultExpanded title=&#8221;Object value&#8221; /&gt;
      &lt;/Paper&gt;
      &lt;Paper withBorder&gt;
        &lt;JsonTree data={[&#8217;string&#8217;, 456, true, null, { nestedKey: &#8216;nestedValue&#8217; }]} defaultExpanded title=&#8221;Array value&#8221; /&gt;
      &lt;/Paper&gt;
    &lt;/Stack&gt;
  );
}</code></pre><p>Use cases</p><ul><li><p>API response debugging: Quickly explore fetched JSON without switching tools.</p></li><li><p>Admin interfaces: Render configuration blobs, feature flags, or audit objects.</p></li><li><p>Developer tools: Embed a live inspector during development or in internal dashboards.</p></li><li><p>Education/demo pages: Show data shapes and changes clearly for tutorials and onboarding.</p></li></ul><p>Design philosophy: simplicity first, customization later<br>JsonTree&#8217;s primary goal is effortless inspection: pass data and get a clean, browsable tree. But it&#8217;s built for Mantine, so you can compose it within <code>Paper</code>, <code>Card</code>, <code>Stack</code>, <code>ScrollArea</code>, and more, apply theme tokens, and keep your UX coherent&#8212;light/dark included.</p><h3>Tips for smooth integration</h3><ul><li><p>Combine <code>defaultExpanded</code> with a clear title to provide instant context.</p></li><li><p>Use <code>Paper</code> <code>withBorder</code> to visually separate multiple trees in a layout.</p></li><li><p>Pair with Mantine&#8217;s Code or <code>Text</code> components to annotate nodes or describe payloads.</p></li><li><p>Wrap in <code>ScrollArea</code> for very large trees to keep the page layout tidy.</p></li><li><p>Keep large payloads collapsed by default and let users progressively explore.</p></li></ul><h2>Mantine ecosystem and extensions</h2><p><a href="https://gfazioli.github.io/mantine-json-tree/">JsonTree</a> is written for <a href="https://mantine.dev/">Mantine UI</a> and plays nicely with its core components and theme system. If you&#8217;re exploring additional add-ons, you can find other extensions on <a href="https://mantine-extensions.vercel.app/">Mantine Extensions</a> to round out your toolkit.</p><div><hr></div><h4>Closing thoughts<br></h4><p><a href="https://gfazioli.github.io/mantine-json-tree/">JsonTree</a> embraces what <a href="https://mantine.dev/">Mantine</a> developers love: speed, clarity, and control. It removes friction from inspecting data while staying 100% Mantine-friendly. Whether you&#8217;re debugging, documenting, or shipping a production admin UI, <a href="https://gfazioli.github.io/mantine-json-tree/">JsonTree</a> gives you a dependable way to present complex structures with an elegant, approachable interface.</p><div id="youtube2-WtzCOpmcvpg" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;WtzCOpmcvpg&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/WtzCOpmcvpg?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div><p>You can watch <a href="https://www.youtube.com/playlist?list=PL85tTROKkZrWyqCcmNCdWajpx05-cTal4">More video</a> </p><p>Happy building!</p><p></p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://www.undolog.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Undolog is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[Visualizing Time Progress with Dynamic Clock Arcs]]></title><description><![CDATA[Mantine Clock 2.0 introduces powerful arc rendering to show elapsed time at a glance]]></description><link>https://www.undolog.com/p/visualizing-time-progress-with-dynamic</link><guid isPermaLink="false">https://www.undolog.com/p/visualizing-time-progress-with-dynamic</guid><dc:creator><![CDATA[Giovambattista Fazioli]]></dc:creator><pubDate>Sat, 30 Aug 2025 06:01:00 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!CLiN!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F34e1a203-c48c-48a4-bf5a-13abe49e6367_2752x1536.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!CLiN!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F34e1a203-c48c-48a4-bf5a-13abe49e6367_2752x1536.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!CLiN!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F34e1a203-c48c-48a4-bf5a-13abe49e6367_2752x1536.png 424w, https://substackcdn.com/image/fetch/$s_!CLiN!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F34e1a203-c48c-48a4-bf5a-13abe49e6367_2752x1536.png 848w, https://substackcdn.com/image/fetch/$s_!CLiN!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F34e1a203-c48c-48a4-bf5a-13abe49e6367_2752x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!CLiN!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F34e1a203-c48c-48a4-bf5a-13abe49e6367_2752x1536.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!CLiN!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F34e1a203-c48c-48a4-bf5a-13abe49e6367_2752x1536.png" width="1456" height="813" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/34e1a203-c48c-48a4-bf5a-13abe49e6367_2752x1536.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:813,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:2247610,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://www.undolog.com/i/171972674?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F34e1a203-c48c-48a4-bf5a-13abe49e6367_2752x1536.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!CLiN!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F34e1a203-c48c-48a4-bf5a-13abe49e6367_2752x1536.png 424w, https://substackcdn.com/image/fetch/$s_!CLiN!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F34e1a203-c48c-48a4-bf5a-13abe49e6367_2752x1536.png 848w, https://substackcdn.com/image/fetch/$s_!CLiN!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F34e1a203-c48c-48a4-bf5a-13abe49e6367_2752x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!CLiN!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F34e1a203-c48c-48a4-bf5a-13abe49e6367_2752x1536.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>We're excited to announce the release of <a href="https://gfazioli.github.io/mantine-clock/">Mantine Clock 2.0</a>, featuring a game-changing addition: <strong>dynamic arc rendering</strong>. This major update transforms how users visualize time progression by adding colorful, customizable arcs that follow clock hands and instantly communicate elapsed time.</p><h2>What Are Clock Arcs?</h2><p>Clock arcs are sector-shaped visual indicators that sweep from a defined starting point to the current position of each clock hand. Think of them as progress indicators that make it incredibly easy to see at a glance how much time has passed since a specific moment.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://www.undolog.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Undolog is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!HPh5!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4fe69e9f-8c8f-4a43-8374-a7a3ac828af1_2394x2620.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!HPh5!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4fe69e9f-8c8f-4a43-8374-a7a3ac828af1_2394x2620.png 424w, https://substackcdn.com/image/fetch/$s_!HPh5!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4fe69e9f-8c8f-4a43-8374-a7a3ac828af1_2394x2620.png 848w, https://substackcdn.com/image/fetch/$s_!HPh5!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4fe69e9f-8c8f-4a43-8374-a7a3ac828af1_2394x2620.png 1272w, https://substackcdn.com/image/fetch/$s_!HPh5!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4fe69e9f-8c8f-4a43-8374-a7a3ac828af1_2394x2620.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!HPh5!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4fe69e9f-8c8f-4a43-8374-a7a3ac828af1_2394x2620.png" width="1456" height="1593" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/4fe69e9f-8c8f-4a43-8374-a7a3ac828af1_2394x2620.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1593,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1623307,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.undolog.com/i/171972674?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4fe69e9f-8c8f-4a43-8374-a7a3ac828af1_2394x2620.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!HPh5!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4fe69e9f-8c8f-4a43-8374-a7a3ac828af1_2394x2620.png 424w, https://substackcdn.com/image/fetch/$s_!HPh5!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4fe69e9f-8c8f-4a43-8374-a7a3ac828af1_2394x2620.png 848w, https://substackcdn.com/image/fetch/$s_!HPh5!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4fe69e9f-8c8f-4a43-8374-a7a3ac828af1_2394x2620.png 1272w, https://substackcdn.com/image/fetch/$s_!HPh5!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4fe69e9f-8c8f-4a43-8374-a7a3ac828af1_2394x2620.png 1456w" sizes="100vw"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>For example:</p><ul><li><p>A <strong>seconds arc</strong> might show how many seconds have elapsed since the start of the current minute</p></li><li><p>A <strong>minutes arc</strong> could visualize how much of the current hour has passed</p></li><li><p>An <strong>hours arc</strong> might display progress through a work shift or meeting duration</p></li></ul><h2>Key Features</h2><h3>Independent Control for Each Hand</h3><p>The new arc system provides complete control over seconds, minutes, and hours arcs:</p><pre><code>&lt;Clock
  withSecondsArc
  secondsArcFrom="12:00:00"
  secondsArcColor="red.6"
  secondsArcOpacity={0.6}
  
  withMinutesArc
  minutesArcFrom="12:00"
  minutesArcColor="blue.6"
  minutesArcOpacity={0.5}
  
  withHoursArc
  hoursArcFrom="12:00"
  hoursArcDirection="counterClockwise"
  hoursArcColor="teal.6"
  hoursArcOpacity={0.4}
/&gt;</code></pre><h3>Smart Arc Positioning</h3><p>Each arc intelligently respects the corresponding hand's length, ensuring perfect visual alignment. The arc never extends beyond its hand, maintaining clean, professional aesthetics.</p><div class="native-video-embed" data-component-name="VideoPlaceholder" data-attrs="{&quot;mediaUploadId&quot;:&quot;fd5498ad-5775-42aa-8ae8-0723997dd271&quot;,&quot;duration&quot;:null}"></div><h3>Bidirectional Support</h3><p>Arcs can sweep both <strong>clockwise</strong> and <strong>counterclockwise</strong>, opening up creative possibilities for different use cases:</p><ul><li><p>Clockwise arcs for showing elapsed time</p></li><li><p>Counterclockwise arcs for displaying remaining time</p></li><li><p>Mixed directions for complex time tracking scenarios</p></li></ul><h3>Flexible Time References</h3><p>The system supports multiple time input formats:</p><ul><li><p><strong>String format</strong>: <code>"12:30:45"</code> for precise time specification</p></li><li><p><strong>Date objects</strong>: For timezone-aware calculations</p></li><li><p><strong>Fractional precision</strong>: Seconds contribute to minute positioning, minutes affect hour positioning</p></li></ul><h2>Real-World Applications</h2><h3>Meeting Duration Tracker</h3><p>Show how much of a scheduled meeting has elapsed with a subtle arc that grows as time progresses, helping participants stay aware of remaining time without checking their phones.</p><h3>Workout Timer</h3><p>Display workout intervals with color-coded arcs&#8212;red for high-intensity periods, blue for rest periods&#8212;making it easy to see progress at a glance.</p><h3>Shift Progress Indicator</h3><p>For service industries, display work shift progress with an arc that fills throughout the day, providing instant visual feedback on how much of the shift remains.</p><h3>Educational Tools</h3><p>In classroom settings, use arcs to show lesson segment progress or exam time remaining, helping students manage their time more effectively.</p><h2>Technical Excellence</h2><h3>Performance Optimized</h3><p>The arc rendering system uses optimized SVG rendering with geometric precision hints to ensure smooth performance and crisp visuals across all devices.</p><h3>Theme Integration</h3><p>Arcs seamlessly integrate with Mantine's theming system, supporting all color variants and automatically adapting to light and dark modes.</p><h3>TypeScript Ready</h3><p>Full TypeScript support with comprehensive interfaces ensures type safety and excellent developer experience:</p><pre><code>interface ClockArcsProps {
  withSecondsArc?: boolean;
  secondsArcFrom?: string | Date;
  secondsArcDirection?: 'clockwise' | 'counterClockwise';
  secondsArcColor?: MantineColor;
  secondsArcOpacity?: number;
  // ... and more for minutes and hours
}</code></pre><h2>Migration and Compatibility</h2><p><a href="https://gfazioli.github.io/mantine-clock/">Mantine Clock 2.0</a> maintains complete backward compatibility&#8212;existing implementations continue working without changes. The arc features are <strong>opt-in</strong>, activated only when you explicitly enable them with props like `withSecondsArc`.</p><h2>Interactive Demo</h2><p>We've built a comprehensive <a href="https://gfazioli.github.io/mantine-clock/">interactive demo</a> that lets you experiment with all arc parameters in real-time. Adjust colors, directions, start times, and opacities to see how arcs can enhance your time visualization needs.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://www.undolog.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Undolog is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[Supercharge Your UI with Mantine Clock: The Ultimate Timekeeping Extension for Mantine]]></title><description><![CDATA[Real-Time Clocks, Timezone Support, and Customization&#8212;All Seamlessly Integrated with Mantine UI]]></description><link>https://www.undolog.com/p/supercharge-your-ui-with-mantine</link><guid isPermaLink="false">https://www.undolog.com/p/supercharge-your-ui-with-mantine</guid><dc:creator><![CDATA[Giovambattista Fazioli]]></dc:creator><pubDate>Mon, 04 Aug 2025 06:01:23 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!dDke!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff9105864-50df-4eef-828b-ca174bb3f906_2752x1536.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!dDke!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff9105864-50df-4eef-828b-ca174bb3f906_2752x1536.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!dDke!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff9105864-50df-4eef-828b-ca174bb3f906_2752x1536.png 424w, https://substackcdn.com/image/fetch/$s_!dDke!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff9105864-50df-4eef-828b-ca174bb3f906_2752x1536.png 848w, https://substackcdn.com/image/fetch/$s_!dDke!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff9105864-50df-4eef-828b-ca174bb3f906_2752x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!dDke!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff9105864-50df-4eef-828b-ca174bb3f906_2752x1536.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!dDke!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff9105864-50df-4eef-828b-ca174bb3f906_2752x1536.png" width="1456" height="813" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f9105864-50df-4eef-828b-ca174bb3f906_2752x1536.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:813,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:5157095,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://www.undolog.com/i/169923542?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff9105864-50df-4eef-828b-ca174bb3f906_2752x1536.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!dDke!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff9105864-50df-4eef-828b-ca174bb3f906_2752x1536.png 424w, https://substackcdn.com/image/fetch/$s_!dDke!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff9105864-50df-4eef-828b-ca174bb3f906_2752x1536.png 848w, https://substackcdn.com/image/fetch/$s_!dDke!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff9105864-50df-4eef-828b-ca174bb3f906_2752x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!dDke!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff9105864-50df-4eef-828b-ca174bb3f906_2752x1536.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>In the world of React UI development, Mantine is a top choice for building beautiful, accessible, and highly customizable interfaces. But what if you need advanced timekeeping features&#8212;like real-time clocks, timezone displays, or countdown logic&#8212;without leaving the Mantine ecosystem? Enter <a href="https://gfazioli.github.io/mantine-clock/">Mantine Clock</a>, a powerful extension that brings professional-grade clock components and hooks to your NextJS and Typescript projects.</p><h2>Why Mantine Extensions?</h2><p>If you&#8217;re already using the Mantine UI Library (<a href="https://mantine.dev/getting-started/">official docs &#8599;</a>), you know it&#8217;s all about developer experience, accessibility, and design consistency. But Mantine&#8217;s real power comes from its extension ecosystem, available through the <a href="https://mantine-extensions.vercel.app/">Mantine Extensions Hub &#8599;</a>. Here, you&#8217;ll find community-driven solutions like Mantine Clock, which solve real-world UI needs while staying true to Mantine&#8217;s design language.</p><h2>What is Mantine clock?</h2><p><a href="https://gfazioli.github.io/mantine-clock/">Mantine Clock</a> is a React package that provides highly customizable clock components and hooks, designed to integrate natively with Mantine&#8217;s design system. Whether you want a classic analog clock, a digital display, or need to handle timezones and countdowns, Mantine Clock has you covered&#8212;with full support for Mantine theming and responsive layouts.</p><h3>Key Features</h3><ul><li><p><strong>Analog &amp; Digital Clocks</strong>: Render beautiful, theme-aware clocks that fit your app&#8217;s style.</p></li><li><p><strong>Timezone Support</strong>: Display accurate time for any region&#8212;ideal for global apps.</p></li><li><p><strong>Real-Time Updates</strong>: Clocks update live, so your users always see the correct time.</p></li><li><p><strong>Customization</strong>: Style hands, ticks, numbers, and colors to match your brand.</p></li><li><p><strong>Mantine Native</strong>: Seamlessly blends with Mantine&#8217;s color schemes and dark mode.</p></li><li><p><strong>Typescript Ready</strong>: Built for type safety and autocompletion.</p></li><li><p><strong>Countdown &amp; Timer Hooks</strong>: Use hooks to implement countdowns and timers with full control.</p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!txdx!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd48b163e-6cb4-46ec-a090-53371eb4d779_1642x682.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!txdx!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd48b163e-6cb4-46ec-a090-53371eb4d779_1642x682.png 424w, https://substackcdn.com/image/fetch/$s_!txdx!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd48b163e-6cb4-46ec-a090-53371eb4d779_1642x682.png 848w, https://substackcdn.com/image/fetch/$s_!txdx!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd48b163e-6cb4-46ec-a090-53371eb4d779_1642x682.png 1272w, https://substackcdn.com/image/fetch/$s_!txdx!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd48b163e-6cb4-46ec-a090-53371eb4d779_1642x682.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!txdx!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd48b163e-6cb4-46ec-a090-53371eb4d779_1642x682.png" width="1456" height="605" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d48b163e-6cb4-46ec-a090-53371eb4d779_1642x682.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:605,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:581698,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.undolog.com/i/169923542?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd48b163e-6cb4-46ec-a090-53371eb4d779_1642x682.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!txdx!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd48b163e-6cb4-46ec-a090-53371eb4d779_1642x682.png 424w, https://substackcdn.com/image/fetch/$s_!txdx!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd48b163e-6cb4-46ec-a090-53371eb4d779_1642x682.png 848w, https://substackcdn.com/image/fetch/$s_!txdx!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd48b163e-6cb4-46ec-a090-53371eb4d779_1642x682.png 1272w, https://substackcdn.com/image/fetch/$s_!txdx!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd48b163e-6cb4-46ec-a090-53371eb4d779_1642x682.png 1456w" sizes="100vw"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2>Getting Started</h2><h3>1. Install the Package</h3><p>Add Mantine Clock to your project with yarn or npm:</p><pre><code><code>yarn add @gfazioli/mantine-clock

# or

npm install @gfazioli/mantine-clock
</code></code></pre><p>After installation, import the package styles at the root of your application:</p><pre><code><code>import '@gfazioli/mantine-clock/styles.css';</code></code></pre><p>For advanced CSS layering, you can also import:</p><pre><code><code>import '@gfazioli/mantine-clock/styles.layer.css';</code></code></pre><h3>2. Basic Usage</h3><p>The main &#8206;<code>Clock</code> component is easy to use in any Mantine layout:</p><pre><code><code>import { Clock } from '@gfazioli/mantine-clock';

export function MyDashboard() {
  return (
    &lt;Clock
      size={200}
      color="blue"
      timezone="America/New_York"
      showSeconds
    /&gt;
  );
}</code></code></pre><p>You can further customize the appearance by adjusting props for hour/minute/second hands, tick marks, and number styles. The component is fully theme-aware, adapting to your Mantine color scheme and dark mode settings.</p><h3>3. <strong>Advanced: Real-Time and Countdown Logic with Hooks</strong></h3><p>Need a countdown for a product launch or event? Mantine Clock provides hooks like &#8206;<code>useCountdown</code> and &#8206;<code>useClock</code> to help you build custom logic with full Typescript support:</p><pre><code><code>import { useCountdown } from '@gfazioli/mantine-clock';

export function LaunchCountdown() {
  const { days, hours, minutes, seconds, isCompleted } = useCountdown({
    targetDate: new Date('2025-09-01T12:00:00Z'),
  });

  if (isCompleted) {
    return &lt;span&gt;Launched!&lt;/span&gt;;
  }

  return (
    &lt;span&gt;
      {days}d {hours}h {minutes}m {seconds}s
    &lt;/span&gt;
  );
}</code></code></pre><p>You can use these hooks to create your own countdown or timer components, giving you full flexibility and control.</p><h2><strong>Why Choose Mantine Clock?</strong></h2><h3><strong>Seamless Mantine Integration</strong></h3><p>Mantine Clock is designed specifically for Mantine&#8217;s design language. You get:</p><ul><li><p>Consistent look and feel across your UI</p></li><li><p>Responsive and accessible components</p></li><li><p>Effortless integration with Mantine&#8217;s theming and color modes</p></li></ul><h3><strong>Built for NextJS &amp; Typescript</strong></h3><p>Whether you&#8217;re building a static site or a dynamic dashboard, Mantine Clock is optimized for server-side rendering and type safety. Enjoy autocompletion and reliable prop types out of the box.</p><h3><strong>Community-Driven and Actively Maintained</strong></h3><p>Mantine Clock is part of the vibrant <strong>Mantine Extensions Hub</strong> (<a href="https://mantine-extensions.vercel.app/">see more extensions &#8599;</a>), ensuring you&#8217;re using components that are open-source, actively developed, and trusted by the community.</p><h3><strong>Real-World Use Cases</strong></h3><ul><li><p><strong>Dashboards</strong>: Show local and global times for distributed teams.</p></li><li><p><strong>Event Pages</strong>: Add countdowns for launches, webinars, or deadlines.</p></li><li><p><strong>Productivity Tools</strong>: Integrate timers and clocks into your workflow apps.</p></li><li><p><strong>Educational Platforms</strong>: Teach time concepts interactively.</p></li></ul><h3><strong>Final Thoughts</strong></h3><p>If you&#8217;re already leveraging Mantine for your React projects, <a href="https://gfazioli.github.io/mantine-clock/">Mantine Clock</a> is a must-have extension. It brings professional-grade timekeeping, stunning visuals, and seamless integration&#8212;all with the reliability and polish you expect from the Mantine ecosystem.</p>]]></content:encoded></item><item><title><![CDATA[Supercharge Your Tables with Finder-Style List Views: Introducing Mantine ListViewTable Extension]]></title><description><![CDATA[Transform Data Presentation in Next.js & TypeScript Apps with Advanced Table Features, Powered by Mantine UI]]></description><link>https://www.undolog.com/p/supercharge-your-tables-with-finder</link><guid isPermaLink="false">https://www.undolog.com/p/supercharge-your-tables-with-finder</guid><dc:creator><![CDATA[Giovambattista Fazioli]]></dc:creator><pubDate>Sat, 12 Jul 2025 06:00:43 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!QL-7!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Febdb7fad-18a9-41f8-9845-60ef9c47d7a0_2752x1536.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!QL-7!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Febdb7fad-18a9-41f8-9845-60ef9c47d7a0_2752x1536.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!QL-7!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Febdb7fad-18a9-41f8-9845-60ef9c47d7a0_2752x1536.png 424w, https://substackcdn.com/image/fetch/$s_!QL-7!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Febdb7fad-18a9-41f8-9845-60ef9c47d7a0_2752x1536.png 848w, https://substackcdn.com/image/fetch/$s_!QL-7!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Febdb7fad-18a9-41f8-9845-60ef9c47d7a0_2752x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!QL-7!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Febdb7fad-18a9-41f8-9845-60ef9c47d7a0_2752x1536.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!QL-7!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Febdb7fad-18a9-41f8-9845-60ef9c47d7a0_2752x1536.png" width="1456" height="813" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ebdb7fad-18a9-41f8-9845-60ef9c47d7a0_2752x1536.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:813,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:2370527,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://www.undolog.com/i/167983346?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Febdb7fad-18a9-41f8-9845-60ef9c47d7a0_2752x1536.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!QL-7!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Febdb7fad-18a9-41f8-9845-60ef9c47d7a0_2752x1536.png 424w, https://substackcdn.com/image/fetch/$s_!QL-7!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Febdb7fad-18a9-41f8-9845-60ef9c47d7a0_2752x1536.png 848w, https://substackcdn.com/image/fetch/$s_!QL-7!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Febdb7fad-18a9-41f8-9845-60ef9c47d7a0_2752x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!QL-7!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Febdb7fad-18a9-41f8-9845-60ef9c47d7a0_2752x1536.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>If you&#8217;re building modern web applications with Next.js and TypeScript, you already know the importance of clean, interactive data presentation. Tables are a staple for displaying structured information, but what if you could offer your users a familiar, macOS Finder-style list view&#8212;complete with column reordering, resizing, sticky headers, and more? Enter the <a href="https://gfazioli.github.io/mantine-list-view-table/">Mantine ListViewTable</a> extension: an innovative component that takes your tables to the next level, all while staying true to the elegance and flexibility of the <a href="https://mantine.dev/getting-started/">Mantine UI Library</a>.</p><h3>What is Mantine ListViewTable?</h3><p>Mantine ListViewTable is a powerful extension built on top of the Mantine Table component. This extension brings a feature-rich, Finder-inspired list view to your React projects. Imagine the intuitive, drag-and-drop experience of macOS&#8217;s file manager&#8212;now available in your web app, with the full power of TypeScript and Next.js.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://www.undolog.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Undolog is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><h3>Why Choose ListViewTable?</h3><ul><li><p><strong>Column Reordering &amp; Resizing:</strong> Users can drag columns to reorder them and adjust widths, just like in native desktop apps.</p></li><li><p><strong>Sticky Headers:</strong> Keep headers visible as you scroll, ensuring context is never lost&#8212;even in long tables.</p></li><li><p><strong>Customizable Borders &amp; Spacing:</strong> Fine-tune the appearance with table, row, and column borders, plus flexible spacing and alignment options.</p></li><li><p><strong>Advanced States:</strong> Built-in support for loading and empty states offers a polished, user-friendly experience.</p></li><li><p><strong>External Control:</strong> Easily integrate with your own state logic for sorting, column configuration, and more.</p></li><li><p><strong>Vertical Variant:</strong> Display key-value pairs for detailed views&#8212;perfect for user profiles or configuration panels.</p></li></ul><h3>Seamless Integration with Mantine UI</h3><p>If you&#8217;re already leveraging <a href="https://mantine.dev/getting-started/">Mantine UI</a> for its robust components and theming, ListViewTable fits right in. Installation is a breeze:</p><pre><code><code>yarn add @gfazioli/mantine-list-view-table</code></code></pre><p>Don&#8217;t forget to import the styles at your app&#8217;s root:</p><pre><code><code>import '@gfazioli/mantine-list-view-table/styles.css';</code></code></pre><p>And just like that, you&#8217;re ready to supercharge your tables with a drop-in component that feels native, fast, and fully customizable.</p><h3>Real-World Usage Example</h3><p>Here&#8217;s how easy it is to implement a dynamic, interactive list view:</p><pre><code><code>import { ListViewTable } from '@gfazioli/mantine-list-view-table';
import { Badge, Text } from '@mantine/core';
import [data, columns] from './data';

function Demo() {
  return (
    &lt;ListViewTable
      columns={columns}
      data={data}
      rowKey="id"
      onRowClick={(row) =&gt; console.log('Clicked:', row.name)}

    /&gt;
  );
}</code></code></pre><p>Want sticky headers? Just add the <code>stickyHeader</code> prop. Need to handle custom sorting or persist column state? The component exposes flexible callbacks so you can sync with Redux, Zustand, or any state management solution.</p><h3>Advanced Features for Modern Apps</h3><ul><li><p><strong>Scroll Area Integration:</strong> Pair with Mantine&#8217;s <code>ScrollArea</code> for fixed-height, scrollable tables with sticky headers that always stay in view.</p></li><li><p><strong>Width Constraints:</strong> Set minimum, maximum, or fixed column widths to maintain perfect layouts.</p></li><li><p><strong>Loading &amp; Empty States:</strong> Show spinners or custom messages when data is loading or absent.</p></li><li><p><strong>Vertical Variant:</strong> Switch to a key-value layout for detail-rich views&#8212;ideal for admin panels and dashboards.</p></li></ul><h3>Explore More on the Mantine Extensions HUB</h3><p>ListViewTable is part of the growing ecosystem of <a href="https://mantine-extensions.vercel.app/">Mantine Extensions</a>. The HUB is your one-stop shop for discovering powerful, community-driven components that extend Mantine&#8217;s core capabilities&#8212;think calendars, onboarding tours, spinners, and more. Whether you&#8217;re building dashboards, SaaS tools, or data-heavy applications, the Extensions HUB offers a treasure trove of ready-to-use solutions.</p><h3>Why Developers Love Mantine ListViewTable</h3><ul><li><p><strong>Familiar UX:</strong> Users instantly recognize the Finder-style interface, reducing friction and boosting productivity.</p></li><li><p><strong>TypeScript First:</strong> Get full type safety and autocompletion out of the box.</p></li><li><p><strong>Next.js Friendly:</strong> Works seamlessly with server-side rendering and static generation.</p></li><li><p><strong>Open Source &amp; Community-Driven:</strong> Built and maintained by passionate developers&#8212;contributions welcome!</p></li></ul><h3>Get Started Today</h3><p>Ready to give your tables a productivity boost? Head over to the <a href="https://mantine-extensions.vercel.app/extensions/list-view-table">Mantine ListViewTable documentation</a> for detailed guides, live demos, and code examples. Don&#8217;t forget to explore the <a href="https://mantine.dev/getting-started/">Mantine UI Library</a> for even more UI inspiration, and check out the <a href="https://mantine-extensions.vercel.app/">Mantine Extensions HUB</a> for the latest community innovations.</p><p>Upgrade your data tables&#8212;your users (and your codebase) will thank you!</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://www.undolog.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Undolog is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item></channel></rss>