This content originally appeared on Modern Web Development with Chrome and was authored by Paul Kinlan
<p>Just after my daughter was born we needed a simple way to track "Baby Habits" (Read: Eat, Poop, Wee, Sleep, and maybe a bit of Vom). During my baby duties when the baby was asleep I built <a href="https://akachan.app/">Akachan.app</a> to help us keep on top of things.</p>
<p>I wanted this app to load instantly, and given that JavaScript is one of the biggest contributors to a slow page load, I gave myself the challenge to build Akachan as a SPA without client-side JS (more in another post), which for the most part I was able to do this with traditional POST's and a hefty amount of Service Worker logic.</p>
<p>There was one challenge, I wanted a Material Design like Floating Action Button (FAB) in my page that gave you the ability to quickly add any of the babies activities, but without adding any additional JavaScript.</p>
<figure><video src="https://paul.kinlan.me/videos/2020-09-23-fab-without-javascript-0.mp4" alt="Floating Action Button in action" controls></video></figure>
<p>All of the solutions I could find required client-side JavaScript to function and I didn't want that, so I built one without any.</p>
<p>The solution was to have a 'menu' with an id of "add-nav" and then to use two anchors (one for the open state and one for the closed state) with an href of #add-nav and #remove-nav, and then using how the browser manages anchor based navigation by managing the visibility of the anchors and menus with CSS and the ":target" selector.</p>
<h3 id="html">HTML</h3>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-HTML" data-lang="HTML"><<span style="color:#f92672">nav</span> <span style="color:#a6e22e">id</span><span style="color:#f92672">=</span><span style="color:#e6db74">"add-nav"</span>>
<<span style="color:#f92672">a</span> <span style="color:#a6e22e">href</span><span style="color:#f92672">=</span><span style="color:#e6db74">"/feeds/new"</span> <span style="color:#a6e22e">title</span><span style="color:#f92672">=</span><span style="color:#e6db74">"Add a feed"</span>>?</<span style="color:#f92672">a</span>>
<<span style="color:#f92672">a</span> <span style="color:#a6e22e">href</span><span style="color:#f92672">=</span><span style="color:#e6db74">"/sleeps/new"</span> <span style="color:#a6e22e">title</span><span style="color:#f92672">=</span><span style="color:#e6db74">"Add a Sleep"</span>>?</<span style="color:#f92672">a</span>>
<<span style="color:#f92672">a</span> <span style="color:#a6e22e">href</span><span style="color:#f92672">=</span><span style="color:#e6db74">"/poops/new"</span> <span style="color:#a6e22e">title</span><span style="color:#f92672">=</span><span style="color:#e6db74">"Add a Poop"</span>>?</<span style="color:#f92672">a</span>>
<<span style="color:#f92672">a</span> <span style="color:#a6e22e">href</span><span style="color:#f92672">=</span><span style="color:#e6db74">"/wees/new"</span> <span style="color:#a6e22e">title</span><span style="color:#f92672">=</span><span style="color:#e6db74">"Add a Wee"</span>>⛲️</<span style="color:#f92672">a</span>>
</<span style="color:#f92672">nav</span>>
<<span style="color:#f92672">a</span> <span style="color:#a6e22e">href</span><span style="color:#f92672">=</span><span style="color:#e6db74">"#remove-nav"</span>>
<<span style="color:#f92672">img</span> <span style="color:#a6e22e">src</span><span style="color:#f92672">=</span><span style="color:#e6db74">"/images/icons/ui/remove_white_18dp.svg"</span> <span style="color:#a6e22e">alt</span><span style="color:#f92672">=</span><span style="color:#e6db74">""</span> />
</<span style="color:#f92672">a</span>>
<<span style="color:#f92672">a</span> <span style="color:#a6e22e">href</span><span style="color:#f92672">=</span><span style="color:#e6db74">"#add-nav"</span> <span style="color:#a6e22e">title</span><span style="color:#f92672">=</span><span style="color:#e6db74">"Add"</span>>
<<span style="color:#f92672">img</span> <span style="color:#a6e22e">src</span><span style="color:#f92672">=</span><span style="color:#e6db74">"/images/icons/ui/add_white_18dp.svg"</span> <span style="color:#a6e22e">alt</span><span style="color:#f92672">=</span><span style="color:#e6db74">""</span> />
</<span style="color:#f92672">a</span>>
</code></pre></div><h3 id="css">CSS</h3>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-CSS" data-lang="CSS"><span style="color:#f92672">footer</span> <span style="color:#f92672">></span> <span style="color:#f92672">a</span><span style="color:#f92672">[</span><span style="color:#f92672">href</span><span style="color:#f92672">~=</span><span style="color:#e6db74">"#add-nav"</span><span style="color:#f92672">]</span> {
<span style="color:#66d9ef">display</span>: <span style="color:#66d9ef">flex</span>;
<span style="color:#66d9ef">box-shadow</span>: <span style="color:#66d9ef">none</span>;
}
<span style="color:#f92672">footer</span> <span style="color:#f92672">></span> <span style="color:#f92672">a</span><span style="color:#f92672">[</span><span style="color:#f92672">href</span><span style="color:#f92672">~=</span><span style="color:#e6db74">"#remove-nav"</span><span style="color:#f92672">]</span> {
<span style="color:#66d9ef">display</span>: <span style="color:#66d9ef">flex</span>;
}
<span style="color:#f92672">footer</span> <span style="color:#f92672">nav</span>:<span style="color:#a6e22e">target</span> <span style="color:#f92672">~</span> <span style="color:#f92672">a</span><span style="color:#f92672">[</span><span style="color:#f92672">href</span><span style="color:#f92672">~=</span><span style="color:#e6db74">"#add-nav"</span><span style="color:#f92672">]</span> {
<span style="color:#66d9ef">display</span>: <span style="color:#66d9ef">none</span>;
}
<span style="color:#f92672">footer</span> <span style="color:#f92672">nav</span> {
<span style="color:#66d9ef">display</span>: <span style="color:#66d9ef">none</span>;
}
<span style="color:#f92672">footer</span> <span style="color:#f92672">nav</span>:<span style="color:#a6e22e">target</span> {
<span style="color:#66d9ef">display</span>: <span style="color:#66d9ef">flex</span>;
<span style="color:#66d9ef">flex-direction</span>: <span style="color:#66d9ef">column</span>;
<span style="color:#66d9ef">position</span>: <span style="color:#66d9ef">fixed</span>;
<span style="color:#66d9ef">bottom</span>: <span style="color:#ae81ff">3.5</span><span style="color:#66d9ef">em</span>;
<span style="color:#66d9ef">right</span>: <span style="color:#ae81ff">1</span><span style="color:#66d9ef">em</span>;
<span style="color:#66d9ef">font-size</span>: <span style="color:#ae81ff">2</span><span style="color:#66d9ef">em</span>;
<span style="color:#66d9ef">padding</span>: <span style="color:#ae81ff">0.1</span><span style="color:#66d9ef">em</span> <span style="color:#ae81ff">0.3</span><span style="color:#66d9ef">em</span>;
}
<span style="color:#f92672">footer</span> <span style="color:#f92672">nav</span>:<span style="color:#a6e22e">target</span> <span style="color:#f92672">a</span> {
<span style="color:#66d9ef">display</span>: <span style="color:#66d9ef">flex</span>;
<span style="color:#66d9ef">text-shadow</span>: hsla(<span style="color:#ae81ff">218</span>, <span style="color:#ae81ff">20</span><span style="color:#66d9ef">%</span>, <span style="color:#ae81ff">63</span><span style="color:#66d9ef">%</span>, <span style="color:#ae81ff">1</span>) <span style="color:#ae81ff">0</span><span style="color:#66d9ef">px</span> <span style="color:#ae81ff">0</span><span style="color:#66d9ef">px</span> <span style="color:#ae81ff">0</span><span style="color:#66d9ef">px</span>;
<span style="color:#66d9ef">text-decoration</span>: <span style="color:#66d9ef">none</span>;
<span style="color:#66d9ef">background-color</span>: <span style="color:#66d9ef">white</span>;
<span style="color:#66d9ef">color</span>: <span style="color:#66d9ef">transparent</span>;
<span style="color:#66d9ef">border-radius</span>: <span style="color:#ae81ff">50</span><span style="color:#66d9ef">%</span>;
<span style="color:#66d9ef">box-shadow</span>: <span style="color:#ae81ff">0</span> <span style="color:#ae81ff">5</span><span style="color:#66d9ef">px</span> <span style="color:#ae81ff">5</span><span style="color:#66d9ef">px</span> <span style="color:#ae81ff">-3</span><span style="color:#66d9ef">px</span> rgba(<span style="color:#ae81ff">0</span>, <span style="color:#ae81ff">0</span>, <span style="color:#ae81ff">0</span>, <span style="color:#ae81ff">0.2</span>),
<span style="color:#ae81ff">0</span> <span style="color:#ae81ff">8</span><span style="color:#66d9ef">px</span> <span style="color:#ae81ff">10</span><span style="color:#66d9ef">px</span> <span style="color:#ae81ff">1</span><span style="color:#66d9ef">px</span> rgba(<span style="color:#ae81ff">0</span>, <span style="color:#ae81ff">0</span>, <span style="color:#ae81ff">0</span>, <span style="color:#ae81ff">0.14</span>), <span style="color:#ae81ff">0</span> <span style="color:#ae81ff">3</span><span style="color:#66d9ef">px</span> <span style="color:#ae81ff">14</span><span style="color:#66d9ef">px</span> <span style="color:#ae81ff">2</span><span style="color:#66d9ef">px</span> rgba(<span style="color:#ae81ff">0</span>, <span style="color:#ae81ff">0</span>, <span style="color:#ae81ff">0</span>, <span style="color:#ae81ff">0.12</span>);
<span style="color:#66d9ef">width</span>: <span style="color:#ae81ff">1.3</span><span style="color:#66d9ef">em</span>;
<span style="color:#66d9ef">height</span>: <span style="color:#ae81ff">1.3</span><span style="color:#66d9ef">em</span>;
<span style="color:#66d9ef">justify-content</span>: <span style="color:#66d9ef">center</span>;
<span style="color:#66d9ef">align-content</span>: <span style="color:#66d9ef">center</span>;
<span style="color:#66d9ef">margin-top</span>: <span style="color:#ae81ff">0.5</span><span style="color:#66d9ef">em</span>;
<span style="color:#66d9ef">padding</span>: <span style="color:#ae81ff">0.1</span><span style="color:#66d9ef">em</span>;
}
</code></pre></div><h3 id="how-this-works">How this works</h3>
<p>It was fun getting this to work, and it's nice to see that it's just plain HTML and CSS combined with the browsers state.</p>
<p>Both anchors are positioned on top of each other, with an initial "add-nav" element positioned visible, and remove-nav hidden.</p>
<ol>
<li>When the nav element is the target (after the user clicks the add-nav anchor) the menu is displayed (nav:target selectors). We also then hide the "add-nav" anchor and display "remove-nav" anchor.</li>
<li>When the user clicks the "remove-nav" anchor there is no matching target element, so the DOM state is reverted and the CSS hides the menu and re-displays the "add-nav" anchor.</li>
</ol>
<h3 id="trade-offs">Trade offs</h3>
<p>It's not without issues, because we are using the browser navigation, each time you click the FAB it will add a navigation to the history stack. On one hand this is good, it means you can dismiss the FAB with a backwards navigation, but on the other hand if you spam the FAB, it will create a lot of navigations. There's no solution without adding JS, I would love a <code>a rel="replace"</code> option on anchors so that we could navigate to an id in the page without creating a history item.</p>
<h3 id="conclusion">Conclusion</h3>
<p>There are some things that I need to clear up, when the menu opens it would be nice to have some animations, but it was a lot fun creating this and it's great to see how far you can get with just pure HTML and CSS in building rich and interactive applications.</p>
<p>If you have any suggestions, please let me know what can be done to improve this.</p>
<p>I would also love to see what people can do without layering more and more JS. If you have any other HTML and CSS only UI interactions, then I would love to see them.</p>
<p>FAB.</p>
This content originally appeared on Modern Web Development with Chrome and was authored by Paul Kinlan