<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>tailwind &amp;mdash; Zumi&#39;s Blog</title>
    <link>https://blog.dtth.ch/nki/tag:tailwind</link>
    <description>Just random Zumi Zoom things</description>
    <pubDate>Sun, 10 May 2026 16:46:28 +0200</pubDate>
    <item>
      <title>CSS Sticky is Fun</title>
      <link>https://blog.dtth.ch/nki/css-sticky-is-fun</link>
      <description>&lt;![CDATA[#css #tailwind&#xA;&#xA;I am working on an experimental Codefun frontend, codenamed Mashiro, written completely in Elm using the Tailwind.css &#34;framework&#34;.&#xA;&#xA;The UI&#39;s main idea is to capture most content into card-like objects floating on a greeny background. Something like this:&#xA;&#xA;Screen Shot 2021-08-20 at 0.15.26&#xA;&#xA;You can see that the main &#34;Problems&#34; represent a floating card, while the left side navbar represents some more floating cards.&#xA;&#xA;!--more--&#xA;&#xA;Let&#39;s make a Sticky card header!&#xA;&#xA;In a moment I had a bright idea of keeping that Problems header on top of the card, inside the view while it is being scrolled. &#xA;&#xA;This made sense: the navigation bar is right to the right (!) of the header, so it should be accessible from any scrolling. OK, so how about making it sticky?&#xA;&#xA;Here&#39;s the basic layout of the page:&#xA;&#xA;body:&#xA;&#x9;nav#topBar.sticky.top-0.h-14:&#xA;&#x9;&#x9;... # the basic top bar&#xA;&#x9;main.mt-14.relative:&#xA;&#x9;&#x9;nav#sideNav.fixed: The side navigations&#xA;&#x9;&#x9;content:&#xA;&#x9;&#x9;&#x9;# The actual body&#xA;&#x9;&#x9;&#x9;card:&#xA;&#x9;&#x9;&#x9;&#x9;card-title.sticky.top-0&#xA;&#xA;Now as some of you might have already figured out, this layout actually does not provide what we want at all. &#xA;&#xA;To see why, let&#39;s look at the description of position: sticky on MDN:&#xA;&#xA;  The element is positioned according to the normal flow of the document, and then offset relative to its nearest scrolling ancestor and containing block (nearest block-level ancestor), including table-related elements, based on the values of top, right, bottom, and left. The offset does not affect the position of any other elements.&#xA;&#xA;Note that the offset is relative to the &#34;nearest scrolling ancestor&#34;. What is the nearest scrolling ancestor? It&#39;s basically anything that gives a scrolling bar. At the base level, it&#39;s the body element itself, when its height exceeds the screen (this is why the top bar sticks to the top).&#xA;&#xA;So what we&#39;ve done here is just sticking the card header to under the top bar. Not what we wanted.&#xA;&#xA;We want to make a &#34;scrolling ancestor&#34; out of the main element, since it&#39;s what we want the card header to stick to. OK, let&#39;s do it.&#xA;Note that the simplest way to make something a &#34;scrolling ancestor&#34; is just to stick an overflow value to it:&#xA;&#xA;main.mt-14.relative:&#xA;main.mt-14.relative.overflow-auto:&#xA;&#xA;Is it working yet? &#xA;&#xA;Why wouldn&#39;t you scroll?&#xA;&#xA;Unfortunately we&#39;ve come closer, but not yet done. Now that we&#39;ve locked the card header to the top of main, we&#39;ve actually made the behavior worse: the header now scrolls away with the main element. &#xA;&#xA;Why is this happening? Note that, when we scroll the page, it&#39;s not the main element that was scrolling: it&#39;s actually body. This does not interact with the logic of sticky, so we are just scrolling over the element now.&#xA;&#xA;To fix this, we will need to completely move the scrolling from body to main. This can be done by limiting the height of main:&#xA;&#xA;main.mt-14.relative.overflow-auto:&#xA;main.mt-14.relative.overflow-auto.max-h-screen&#xA;&#xA;Is it working yet?&#xA;&#xA;video width=&#34;240&#34; type=&#34;video/mov&#34; controls src=&#34;https://cdn.discordapp.com/attachments/676817846617243658/1108888105248903278/css-sticky-works.mov&#34;/&#xA;&#xA;🎉]]&gt;</description>
      <content:encoded><![CDATA[<p><a href="/nki/tag:css" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">css</span></a> <a href="/nki/tag:tailwind" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">tailwind</span></a></p>

<p>I am working on an experimental Codefun frontend, codenamed <em>Mashiro</em>, written completely in Elm using the Tailwind.css “framework”.</p>

<p>The UI&#39;s main idea is to capture most content into card-like objects floating on a greeny background. Something like this:</p>

<p><img src="https://cdn.discordapp.com/attachments/676817846617243658/1108887953830334574/css-sticky-layout.png" alt="Screen Shot 2021-08-20 at 0.15.26"></p>

<p>You can see that the main “Problems” represent a floating card, while the left side navbar represents some more floating cards.</p>



<h2 id="let-s-make-a-sticky-card-header">Let&#39;s make a Sticky card header!</h2>

<p>In a moment I had a bright idea of keeping that <code>Problems</code> header on top of the card, inside the view while it is being scrolled.</p>

<p>This made sense: the navigation bar is right to the right (!) of the header, so it should be accessible from any scrolling. OK, so how about making it sticky?</p>

<p>Here&#39;s the basic layout of the page:</p>

<pre><code class="language-yaml">body:
	nav#topBar.sticky.top-0.h-14:
		... # the basic top bar
	main.mt-14.relative:
		nav#sideNav.fixed: The side navigations
		content:
			# The actual body
			card:
				card-title.sticky.top-0
</code></pre>

<p>Now as some of you might have already figured out, this layout actually does not provide what we want at all.</p>

<p>To see why, let&#39;s look at the description of <code>position: sticky</code> on <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/position" rel="nofollow">MDN</a>:</p>

<blockquote><p>The element is positioned according to the normal flow of the document, and then offset relative to its <em>nearest scrolling ancestor</em> and <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Containing_block" rel="nofollow">containing block</a> (nearest block-level ancestor), including table-related elements, based on the values of <code>top</code>, <code>right</code>, <code>bottom</code>, and <code>left</code>. The offset does not affect the position of any other elements.</p></blockquote>

<p>Note that the offset is relative to the “nearest scrolling ancestor”. What is the nearest scrolling ancestor? It&#39;s basically anything that gives a scrolling bar. At the base level, it&#39;s the <code>body</code> element itself, when its height exceeds the screen (this is why the top bar sticks to the top).</p>

<p>So what we&#39;ve done here is just sticking the card header to under the top bar. Not what we wanted.</p>

<p>We want to make a “scrolling ancestor” out of the <code>main</code> element, since it&#39;s what we want the card header to stick to. OK, let&#39;s do it.
Note that the simplest way to make something a “scrolling ancestor” is just to stick an overflow value to it:</p>

<pre><code class="language-diff">- main.mt-14.relative:
+ main.mt-14.relative.overflow-auto:
</code></pre>

<p>Is it working yet?</p>

<h2 id="why-wouldn-t-you-scroll">Why wouldn&#39;t you scroll?</h2>

<p>Unfortunately we&#39;ve come closer, but not yet done. Now that we&#39;ve locked the card header to the top of <code>main</code>, we&#39;ve actually made the behavior worse: <em>the header now scrolls away with the <code>main</code> element</em>.</p>

<p>Why is this happening? Note that, when we scroll the page, it&#39;s not the <code>main</code> element that was scrolling: it&#39;s actually <code>body</code>. This does not interact with the logic of <code>sticky</code>, so we are just scrolling over the element now.</p>

<p>To fix this, we will need to completely <strong>move the scrolling from <code>body</code> to <code>main</code></strong>. This can be done by limiting the height of <code>main</code>:</p>

<pre><code class="language-diff">- main.mt-14.relative.overflow-auto:
+ main.mt-14.relative.overflow-auto.max-h-screen
</code></pre>

<p>Is it working yet?</p>

<p><video controls="" src="https://cdn.discordapp.com/attachments/676817846617243658/1108888105248903278/css-sticky-works.mov"/></p>

<p>🎉</p>
]]></content:encoded>
      <guid>https://blog.dtth.ch/nki/css-sticky-is-fun</guid>
      <pubDate>Thu, 19 Aug 2021 22:30:00 +0000</pubDate>
    </item>
  </channel>
</rss>