<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
    <title>Masakazu</title>
    <subtitle>Masakazu is a professional front-end developer based in PH.</subtitle>
    <link href="https://masakazu.netlify.app/feed.xml" rel="self" type="application/atom+xml" />
    <link href="https://masakazu.netlify.app" rel="alternate" type="text/html"/>
    <author>
        <name>Masakazu</name>
    </author>
    
    <updated>2024-09-08T00:00:00Z</updated>
    
    <id>https://masakazu.netlify.app/</id>
        <entry>
            <title>Going Buildless</title>
            <link href="https://masakazu.netlify.app/blog/buildless/"/>
            <updated>2024-09-08T00:00:00Z</updated>
            <id>https://masakazu.netlify.app/blog/buildless/</id>
            <content type="html"><![CDATA[
                <p class="lead">The year is 2005. You're blasting a pirated mp3 of "Feel Good Inc" and chugging vanilla coke while updating your website.</p>
<p>It’s just a simple change, so you log on via FTP, edit your <code>style.css</code> file, hit save - and reload the page to see your changes live.</p>
<p>Did that story resonate with you? Well then congrats A) you’re a nerd and B) you’re old enough to remember a time before bundlers, pipelines and build processes.</p>
<p>Now listen, I really don’t want to go back to doing live updates in production. That can get painful real fast. But I think it’s amazing when the files you see in your code editor are exactly the same files that are delivered to the browser. No compilation, no node process, no build step. Just edit, save, boom.</p>
<p>There’s something really satisfying about a buildless workflow. Brad Frost recently wrote about it in <a href="https://bradfrost.com/blog/post/raw-dogging-websites/">“raw-dogging websites”</a>, while developing the (very groovy) site for <a href="https://frostapalooza.bradfrost.com/">Frostapalooza</a>.</p>
<p>So, how far are we away from actually working without builds in <a href="https://masakazu.netlify.app/blog/buildless/#html">HTML</a>, <a href="https://masakazu.netlify.app/blog/buildless/#css">CSS</a> and <a href="https://masakazu.netlify.app/blog/buildless/#javascript">Javascript</a>? The idea of “buildless” development isn’t new - but there have been some recent improvements that might get us closer. Let’s jump in.</p>
<div class="callout callout--warning"><span class="callout__icon"><svg class="icon icon--warning" role="img" aria-hidden="true" width="24" height="24"><use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="/assets/icons/icons.sprite.svg#svg-warning"></use></svg></span><div class="callout__content"><p>The obvious tradeoff for a buildless workflow is performance. We use bundlers mostly to concatenate files for fewer network requests, and to avoid long dependency chains that cause &quot;loading waterfalls&quot;. I think it's still worth considering, but take everything here with a grain of performance salt.</p></div></div>
<h2 id="html" tabindex="-1">HTML</h2>
<a class="heading-anchor" href="https://masakazu.netlify.app/blog/buildless/h-html"><span class="sr-only">Permalink to “HTML”</span> <span aria-hidden="true">#</span></a><p>The main reason for a build process in HTML is composition. We don’t want to repeat the markup for things like headers, footers, etc for every single page - so we need to keep these in separate files and stitch them together later.</p>
<p>Oddly enough, HTML is the one where native imports are still an unsolved problem. If you want to include a chunk of HTML in another template, your options are limited:</p>
<ul>
<li>PHP or some other preprocessor language</li>
<li>server-side includes</li>
<li>frames?</li>
</ul>
<p>There is no real standardized way to do this in just HTML, but Scott Jehl came up <a href="https://www.filamentgroup.com/lab/html-includes/">with this idea</a> of using iframes and the <code>onload</code> event to essentially achieve html imports:</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>iframe</span>
    <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>/includes/something.html<span class="token punctuation">"</span></span>
    <span class="token special-attr"><span class="token attr-name">onload</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token value javascript language-javascript"><span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">before</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>contentDocument<span class="token punctuation">.</span>body<span class="token operator">||</span><span class="token keyword">this</span><span class="token punctuation">.</span>contentDocument<span class="token punctuation">)</span><span class="token punctuation">.</span>children<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">remove</span><span class="token punctuation">(</span><span class="token punctuation">)</span></span><span class="token punctuation">"</span></span></span>
<span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>iframe</span><span class="token punctuation">></span></span></code></pre>
<p>Andy Bell then repackaged that technique as a neat <a href="https://codepen.io/andybelldesign/project/full/DyVyPG">web component</a>. Finally Justin Fagnani took it even further with <a href="https://github.com/justinfagnani/html-include-element">html-include-element</a>, a web component that uses native <code>fetch</code> and can also render content into the shadow DOM.</p>
<p>For my own buildless experiment, I built a <a href="https://github.com/maxboeck/zerobuild/blob/main/assets/js/html-include.js">simplified version</a> that replaces itself with the fetched content. It can be used like this:</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>html-include</span> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>./my-local-file.html<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>html-include</span><span class="token punctuation">></span></span></code></pre>
<p>That comes pretty close to actual native HTML imports, even though it now has a Javascript dependency 😢.</p>
<h3 id="server-side-enhancement" tabindex="-1">Server-Side Enhancement</h3>
<a class="heading-anchor" href="https://masakazu.netlify.app/blog/buildless/h-server-side-enhancement"><span class="sr-only">Permalink to “Server-Side Enhancement”</span> <span aria-hidden="true">#</span></a><p>Right, so using web components works, but if you want to nest elements (fetch a piece of content that itself contains a <code>html-include</code>), you can run into waterfall situations again, and you might see things like layout shifts when it loads. Maybe progressive enhancement can help?</p>
<p>I’m hosting my experiment on Cloudflare Pages, and they offer the ability to write a “worker” script (very similar to a service worker) to interact with the platform.</p>
<p>It’s possible to use a <a href="https://blog.cloudflare.com/introducing-htmlrewriter/">HTML Rewriter</a> in such a worker to intercept requests to the CDN and rewrite the response. So I can check if the request is for a piece of HTML and if so, look for the <code>html-include</code> element in there:</p>
<pre class="language-js"><code class="language-js"><span class="token comment">// worker.js</span>
<span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token punctuation">{</span>
    <span class="token keyword">async</span> <span class="token function">fetch</span><span class="token punctuation">(</span><span class="token parameter">request<span class="token punctuation">,</span> env</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token keyword">const</span> response <span class="token operator">=</span> <span class="token keyword">await</span> env<span class="token punctuation">.</span><span class="token constant">ASSETS</span><span class="token punctuation">.</span><span class="token function">fetch</span><span class="token punctuation">(</span>request<span class="token punctuation">)</span>
        <span class="token keyword">const</span> contentType <span class="token operator">=</span> response<span class="token punctuation">.</span>headers<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token string">'Content-Type'</span><span class="token punctuation">)</span>

        <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>contentType <span class="token operator">||</span> <span class="token operator">!</span>contentType<span class="token punctuation">.</span><span class="token function">startsWith</span><span class="token punctuation">(</span><span class="token string">'text/html'</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
            <span class="token keyword">return</span> response
        <span class="token punctuation">}</span>

        <span class="token keyword">const</span> origin <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">URL</span><span class="token punctuation">(</span>request<span class="token punctuation">.</span>url<span class="token punctuation">)</span><span class="token punctuation">.</span>origin
        <span class="token keyword">const</span> rewriter <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">HTMLRewriter</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">on</span><span class="token punctuation">(</span>
            <span class="token string">'html-include'</span><span class="token punctuation">,</span>
            <span class="token keyword">new</span> <span class="token class-name">IncludeElementHandler</span><span class="token punctuation">(</span>origin<span class="token punctuation">)</span>
        <span class="token punctuation">)</span>

        <span class="token keyword">return</span> rewriter<span class="token punctuation">.</span><span class="token function">transform</span><span class="token punctuation">(</span>response<span class="token punctuation">)</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre>
<p>You can then define a custom handler for each <code>html-include</code> element it encounters. I made one that pretty much does the same thing as the web component, but server-side: it fetches the content defined in the <code>src</code> attribute and replaces the element with it.</p>
<pre class="language-js"><code class="language-js"><span class="token comment">// worker.js</span>
<span class="token keyword">class</span> <span class="token class-name">IncludeElementHandler</span> <span class="token punctuation">{</span>
    <span class="token function">constructor</span><span class="token punctuation">(</span><span class="token parameter">origin</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token keyword">this</span><span class="token punctuation">.</span>origin <span class="token operator">=</span> origin
    <span class="token punctuation">}</span>

    <span class="token keyword">async</span> <span class="token function">element</span><span class="token punctuation">(</span><span class="token parameter">element</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token keyword">const</span> src <span class="token operator">=</span> element<span class="token punctuation">.</span><span class="token function">getAttribute</span><span class="token punctuation">(</span><span class="token string">'src'</span><span class="token punctuation">)</span>
        <span class="token keyword">if</span> <span class="token punctuation">(</span>src<span class="token punctuation">)</span> <span class="token punctuation">{</span>
            <span class="token keyword">try</span> <span class="token punctuation">{</span>
                <span class="token keyword">const</span> content <span class="token operator">=</span> <span class="token keyword">await</span> <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">fetchContents</span><span class="token punctuation">(</span>src<span class="token punctuation">)</span>
                <span class="token keyword">if</span> <span class="token punctuation">(</span>content<span class="token punctuation">)</span> <span class="token punctuation">{</span>
                    element<span class="token punctuation">.</span><span class="token function">before</span><span class="token punctuation">(</span>content<span class="token punctuation">,</span> <span class="token punctuation">{</span> <span class="token literal-property property">html</span><span class="token operator">:</span> <span class="token boolean">true</span> <span class="token punctuation">}</span><span class="token punctuation">)</span>
                    element<span class="token punctuation">.</span><span class="token function">remove</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
                <span class="token punctuation">}</span>
            <span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span>err<span class="token punctuation">)</span> <span class="token punctuation">{</span>
                console<span class="token punctuation">.</span><span class="token function">error</span><span class="token punctuation">(</span><span class="token string">'could not replace element'</span><span class="token punctuation">,</span> err<span class="token punctuation">)</span>
            <span class="token punctuation">}</span>
        <span class="token punctuation">}</span>
    <span class="token punctuation">}</span>

    <span class="token keyword">async</span> <span class="token function">fetchContents</span><span class="token punctuation">(</span><span class="token parameter">src</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token keyword">const</span> url <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">URL</span><span class="token punctuation">(</span>src<span class="token punctuation">,</span> <span class="token keyword">this</span><span class="token punctuation">.</span>origin<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">toString</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
        <span class="token keyword">const</span> response <span class="token operator">=</span> <span class="token keyword">await</span> <span class="token function">fetch</span><span class="token punctuation">(</span>url<span class="token punctuation">,</span> <span class="token punctuation">{</span>
            <span class="token literal-property property">method</span><span class="token operator">:</span> <span class="token string">'GET'</span><span class="token punctuation">,</span>
            <span class="token literal-property property">headers</span><span class="token operator">:</span> <span class="token punctuation">{</span>
                <span class="token string-property property">'user-agent'</span><span class="token operator">:</span> <span class="token string">'cloudflare'</span>
            <span class="token punctuation">}</span>
        <span class="token punctuation">}</span><span class="token punctuation">)</span>
        <span class="token keyword">const</span> content <span class="token operator">=</span> <span class="token keyword">await</span> response<span class="token punctuation">.</span><span class="token function">text</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
        <span class="token keyword">return</span> content
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre>
<p>This is a common concept known as <a href="https://en.wikipedia.org/wiki/Edge_Side_Includes">Edge Side Includes</a> (ESI), used to inject pieces of dynamic content into an otherwise static or cached response. By using it here, I can get the best of both worlds: a buildless setup in development with no layout shift in production.</p>
<div class="callout callout--info"><span class="callout__icon"><svg class="icon icon--info" role="img" aria-hidden="true" width="24" height="24"><use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="/assets/icons/icons.sprite.svg#svg-info"></use></svg></span><div class="callout__content"><p>Cloudflare Workers run at the edge, not the client. But if your site isn't hosted there - It should also be possible to use this approach in a regular service worker. When installed, the service worker could <a href="https://github.com/worker-tools/html-rewriter">rewrite responses</a> to stitch HTML imports into the content.</p><p>Maybe you could even cache pieces of HTML locally once they've been fetched? I don't know enough about service worker architecture to do this, but maybe someone else wants to give it a shot?</p></div></div>
<h2 id="css" tabindex="-1">CSS</h2>
<a class="heading-anchor" href="https://masakazu.netlify.app/blog/buildless/h-css"><span class="sr-only">Permalink to “CSS”</span> <span aria-hidden="true">#</span></a><p>Historically, we’ve used CSS preprocessors or build pipelines to do a few things the language couldn’t do:</p>
<ol>
<li>variables</li>
<li>selector nesting</li>
<li>vendor prefixing</li>
<li>bundling (combining partial files)</li>
</ol>
<p>Well good news: we now have native support for variables and nesting, and prefixing is not really necessary anymore in evergreen browsers (except for a <a href="https://elk.zone/front-end.social/@mayank/113102535466025107">few properties</a>). That leaves us with bundling again.</p>
<p>CSS has had <code>@import</code> support for a long time - it’s trivial to include stylesheets in other stylesheets. It’s just … really frowned upon. 😅</p>
<p>Why? Damn performance waterfalls again. Nested levels of <code>@import</code> statements in a render-blocking stylesheet give web developers the creeps, and for <a href="https://csswizardry.com/2023/10/the-three-c-concatenate-compress-cache/">good reason</a>.</p>
<p>But what if we had a flat structure? If you had just <em>one</em> level of imports, wouldn’t HTTP/2 multiplexing take care of that, loading all these files in parallel?</p>
<p>Chris Ferdinandi <a href="https://gomakethings.com/modular-css-and-different-ways-to-structure-your-stylesheets/#the-test-results">ran some benchmark tests</a> on precisely that and the numbers don’t look so bad.</p>
<p>So maybe we could link up a main stylesheet that contains the top-level imports of smaller files, split by concern? We could even use that approach to automatically assign cascade layers to them, like so:</p>
<pre class="language-css"><code class="language-css"><span class="token comment">/* main.css */</span>
<span class="token atrule"><span class="token rule">@layer</span> default<span class="token punctuation">,</span> layout<span class="token punctuation">,</span> components<span class="token punctuation">,</span> utils<span class="token punctuation">,</span> theme<span class="token punctuation">;</span></span>

<span class="token atrule"><span class="token rule">@import</span> <span class="token string">'reset.css'</span> <span class="token function">layer</span><span class="token punctuation">(</span>default<span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="token atrule"><span class="token rule">@import</span> <span class="token string">'base.css'</span> <span class="token function">layer</span><span class="token punctuation">(</span>default<span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="token atrule"><span class="token rule">@import</span> <span class="token string">'layout.css'</span> <span class="token function">layer</span><span class="token punctuation">(</span>layout<span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="token atrule"><span class="token rule">@import</span> <span class="token string">'components.css'</span> <span class="token function">layer</span><span class="token punctuation">(</span>components<span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="token atrule"><span class="token rule">@import</span> <span class="token string">'utils.css'</span> <span class="token function">layer</span><span class="token punctuation">(</span>utils<span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="token atrule"><span class="token rule">@import</span> <span class="token string">'theme.css'</span> <span class="token function">layer</span><span class="token punctuation">(</span>theme<span class="token punctuation">)</span><span class="token punctuation">;</span></span></code></pre>
<h3 id="design-tokens" tabindex="-1">Design Tokens</h3>
<a class="heading-anchor" href="https://masakazu.netlify.app/blog/buildless/h-design-tokens"><span class="sr-only">Permalink to “Design Tokens”</span> <span aria-hidden="true">#</span></a><p>Love your atomic styles? Instead of Tailwind, you can use something like <a href="https://open-props.style/">Open Props</a> to include a set of ready-made design tokens without a build step. They’ll be available in all other files as CSS variables.</p>
<p>You can pick-and-choose what you need (just get color tokens or easing curves) or use all of them at once. Open props is available on a CDN, so you can just do this in your main stylesheet:</p>
<pre class="language-css"><code class="language-css"><span class="token comment">/* main.css */</span>
<span class="token atrule"><span class="token rule">@import</span> <span class="token string">'https://unpkg.com/open-props'</span><span class="token punctuation">;</span></span></code></pre>
<h2 id="javascript" tabindex="-1">Javascript</h2>
<a class="heading-anchor" href="https://masakazu.netlify.app/blog/buildless/h-javascript"><span class="sr-only">Permalink to “Javascript”</span> <span aria-hidden="true">#</span></a><p>Javascript is the one where a build step usually does the most work. Stuff like:</p>
<ul>
<li>transpiling (converting modern ES6 to cross-browser supported ES5)</li>
<li>typechecking (if you’re using TypeScript)</li>
<li>compiling JSX (or other non-standard syntactic sugars)</li>
<li>minification</li>
<li>bundling (again)</li>
</ul>
<p>A buildless worflow can never replace <strong>all</strong> of that. But it may not have to! Transpiling for example is <a href="https://philipwalton.com/articles/the-state-of-es5-on-the-web/">not necessary anymore</a> in modern browsers. As for bundling: ES Modules come with a built-in composition system, so any browser that understands module syntax…</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>script</span> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>/assets/js/main.js<span class="token punctuation">"</span></span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>module<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token script"></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>script</span><span class="token punctuation">></span></span></code></pre>
<p>…allows you to import other modules, and even lazy-load them dynamically:</p>
<pre class="language-js"><code class="language-js"><span class="token comment">// main.js</span>
<span class="token keyword">import</span> <span class="token string">'./some/module.js'</span>

<span class="token keyword">if</span> <span class="token punctuation">(</span>document<span class="token punctuation">.</span><span class="token function">querySelector</span><span class="token punctuation">(</span><span class="token string">'#app'</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">import</span><span class="token punctuation">(</span><span class="token string">'./app.js'</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span></code></pre>
<p>The newest addition to the module system are <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script/type/importmap">Import Maps</a>, which essentially allow you to define a JSON object that maps dependency names to a source location. That location can be an internal path or an external CDN like <a href="https://unpkg.com/">unpkg</a>.</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>head</span><span class="token punctuation">></span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>script</span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>importmap<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token script"><span class="token language-javascript">
        <span class="token punctuation">{</span>
            <span class="token string-property property">"imports"</span><span class="token operator">:</span> <span class="token punctuation">{</span>
                <span class="token string-property property">"preact"</span><span class="token operator">:</span> <span class="token string">"https://unpkg.com/htm/preact/standalone.module.js"</span>
            <span class="token punctuation">}</span>
        <span class="token punctuation">}</span>
    </span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>script</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>head</span><span class="token punctuation">></span></span></code></pre>
<p>Any Javascript on that page can then access these dependencies as if they were bundled with it, using the standard syntax: <code>import { render } from 'preact'</code>.</p>
<h2 id="conclusion" tabindex="-1">Conclusion</h2>
<a class="heading-anchor" href="https://masakazu.netlify.app/blog/buildless/h-conclusion"><span class="sr-only">Permalink to “Conclusion”</span> <span aria-hidden="true">#</span></a><p>So, can we all ditch our build tools soon?</p>
<p>Probably not. I’d say for production-grade development, we’re not quite there yet. Performance tradeoffs are a big part of it, but there are lots of other small problems that you’d likely run into pretty soon once you hit a certain level of complexity.</p>
<p>For smaller sites or side projects though, I can imagine going the buildless route - just to see how far I can take it.</p>
<p>Funnily enough, many build tools advertise their superior “Developer Experience” (DX). For my money, there’s no better DX than shipping code straight to the browser and not having to worry about some cryptic <code>node_modules</code> error in between.</p>
<p>I’d love to see a future where we get that simplicity back.</p>
<h2 id="links" tabindex="-1">Links</h2>
<a class="heading-anchor" href="https://masakazu.netlify.app/blog/buildless/h-links"><span class="sr-only">Permalink to “Links”</span> <span aria-hidden="true">#</span></a><ul>
<li><a href="https://github.com/maxboeck/zerobuild">Repo for my demo code</a></li>
<li><a href="https://modern-web.dev/guides/going-buildless/getting-started/">Getting started with buildess</a></li>
<li><a href="https://modern-web.dev/docs/dev-server/overview/">A simple dev server for a buildless workflow</a></li>
<li><a href="https://www.spicyweb.dev/buildless-modern-development-workflows-are-this-close-to-a-reality/">“A Real “Buildless” Modern Web Development Workflow? Oh Yes!”</a> by Jared White</li>
<li><a href="https://jvns.ca/blog/2023/02/16/writing-javascript-without-a-build-system/">“Writing Javascript without a build system”</a> by Julia Evans</li>
</ul>

            ]]></content>
        </entry>
        <entry>
            <title>You&#39;re Offline</title>
            <link href="https://masakazu.netlify.app/blog/youre-offline/"/>
            <updated>2017-07-12T00:00:00Z</updated>
            <id>https://masakazu.netlify.app/blog/youre-offline/</id>
            <content type="html"><![CDATA[
                <figure>
  <img src="https://masakazu.netlify.app/blog/youre-offline/notification-sample.jpg" alt="" />
</figure>
<p class="lead">A truly responsive website should adapt to all kinds of situations. Besides different viewport sizes, there are other factors to consider. A change in connectivity is one of them.</p>
<p>Earlier this week, I was sitting in a train on my way to speak at a local meetup. InterCity trains in Austria all have WIFI now, so I was doing some last-minute work on my slides online. Train WIFI being what it is though, the network wasn’t exactly reliable. The connection kept dropping everytime we went through a tunnel or too many passengers were logged on.</p>
<p>This is quite a common scenario. People are on the move, network coverage can be poor, internet connections fail. Luckily, we can prepare our websites for this and make them more resilient by <a href="https://bitsofco.de/bitsofcode-pwa-part-1-offline-first-with-service-worker/">building them offline-first</a>.</p>
<p>Offline support is awesome, however your users might not be aware of these capabilites - and they shouldn’t have to be. In some cases they might not even know that they’ve gone offline. That’s why it’s important to communicate what’s going on.</p>
<p>Chances are not <strong>every</strong> part of your site will work offline. Certain things may not be cached, others may require server interaction. This is fine of course, but the interface should reflect that. Just like a responsive layout adapts to changes in viewport size, your offline-optimized site should adapt to changes in connectivity.</p>
<h2 id="checking-for-offline" tabindex="-1">Checking for Offline</h2>
<a class="heading-anchor" href="https://masakazu.netlify.app/blog/youre-offline/h-checking-for-offline"><span class="sr-only">Permalink to “Checking for Offline”</span> <span aria-hidden="true">#</span></a><p>The key ingredients here are the <code>offline</code> event and the <code>navigator.onLine</code> property. By combining them, we can check for network changes and react accordingly.</p>
<p>Here’s an example of a simple connectivity check:</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">let</span> isOffline <span class="token operator">=</span> <span class="token boolean">false</span><span class="token punctuation">;</span>
window<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">'load'</span><span class="token punctuation">,</span> checkConnectivity<span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">// when the page has finished loading,</span>
<span class="token comment">// listen for future changes in connection</span>
<span class="token keyword">function</span> <span class="token function">checkConnectivity</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token function">updateStatus</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  window<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">'online'</span><span class="token punctuation">,</span> updateStatus<span class="token punctuation">)</span><span class="token punctuation">;</span>
  window<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">'offline'</span><span class="token punctuation">,</span> updateStatus<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token comment">// check if we're online, set a class on &lt;html> if not</span>
<span class="token keyword">function</span> <span class="token function">updateStatus</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token keyword">typeof</span> navigator<span class="token punctuation">.</span>onLine <span class="token operator">!==</span> <span class="token string">'undefined'</span><span class="token punctuation">)</span><span class="token punctuation">{</span>
    isOffline <span class="token operator">=</span> <span class="token operator">!</span>navigator<span class="token punctuation">.</span>onLine<span class="token punctuation">;</span>
    document<span class="token punctuation">.</span>documentElement<span class="token punctuation">.</span>classList<span class="token punctuation">.</span><span class="token function">toggle</span><span class="token punctuation">(</span><span class="token string">'is-offline'</span><span class="token punctuation">,</span> isOffline<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token operator">...</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre>
<p>⚠️ Note: With the <code>online</code> event, there’s a slight possibility of false positives: A user might be connected to a network (which is interpreted as being online), but something higher up might block actual internet access. The <code>offline</code> event is a bit more reliable, in the sense that an “offline” user can be expected <strong>NOT</strong> to have access.</p>
<h2 id="get-notified" tabindex="-1">Get Notified</h2>
<a class="heading-anchor" href="https://masakazu.netlify.app/blog/youre-offline/h-get-notified"><span class="sr-only">Permalink to “Get Notified”</span> <span aria-hidden="true">#</span></a><p>Now we want to display some kind of notification to offline users, so they know what’s going on. This can be done in a number of ways; however I would recommend using <code>aria-live</code> regions to make it accessible and have screen readers announce the connection change as well.</p>
<p>Using such a notification bar is pretty straightforward. First, define an element to display messages on your page:</p>
<pre class="language-html"><code class="language-html"><span class="token comment">&lt;!-- notification container --></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> 
  <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>notification<span class="token punctuation">"</span></span> 
  <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>notification<span class="token punctuation">"</span></span> 
  <span class="token attr-name">aria-live</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>assertive<span class="token punctuation">"</span></span> 
  <span class="token attr-name">aria-relevant</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>text<span class="token punctuation">"</span></span> 
  <span class="token attr-name">hidden</span>
<span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span></code></pre>
<p>The <code>aria-live</code> attribute tells screen readers to announce changes to this element. “assertive” means it will interrupt whatever it is currently announcing at the time and prioritize the new message. The <code>aria-relevant</code> tells it to listen for changes in the text content of the element.</p>
<p>You can extend the handler function from before to populate the notification area whenever you detect that a user has gone offline:</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">function</span> <span class="token function">updateStatus</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token operator">...</span>
  <span class="token keyword">const</span> notification <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">querySelector</span><span class="token punctuation">(</span><span class="token string">'#notification'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token keyword">if</span> <span class="token punctuation">(</span>isOffline<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    notification<span class="token punctuation">.</span>textContent <span class="token operator">=</span> <span class="token string">'You appear to be offline right now.'</span><span class="token punctuation">;</span>
    notification<span class="token punctuation">.</span><span class="token function">removeAttribute</span><span class="token punctuation">(</span><span class="token string">'hidden'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span>
    notification<span class="token punctuation">.</span>textContent <span class="token operator">=</span> <span class="token string">''</span><span class="token punctuation">;</span>
    notification<span class="token punctuation">.</span><span class="token function">setAttribute</span><span class="token punctuation">(</span><span class="token string">'hidden'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre>
<p>This is a very simple implementation - you can of course always get a bit fancier with an animated notification bar (or “toast message”). There are also some nice <a href="https://getmdl.io/components/index.html#snackbar-section">pre-made components</a> for this.</p>
<p>If you’re reading this on <a href="https://mxb.at/">my site</a>, you can see a version of these notifications in action if you simply switch off your WIFI for a second.<br />
Go ahead, I’ll wait.</p>
<p>If you’re somewhere else or your browser doesn’t support service worker / offline events, here’s how this could look:</p>
<div class="extend" style="margin-top:2rem;">
  <video poster="https://masakazu.netlify.app/blog/youre-offline/offline-notification.png" width="944" height="528" alt="Offline Notification" controls="">
    <source src="https://masakazu.netlify.app/blog/youre-offline/offline-notification.webm" type="video/webm" />
    <source src="https://masakazu.netlify.app/blog/youre-offline/offline-notification.mp4" type="video/mp4" />
  </video>
</div>
<h2 id="telling-the-user-what%E2%80%99s-available" tabindex="-1">Telling the User what’s available</h2>
<a class="heading-anchor" href="https://masakazu.netlify.app/blog/youre-offline/h-telling-the-user-whate28099s-available"><span class="sr-only">Permalink to “Telling the User what’s available”</span> <span aria-hidden="true">#</span></a><p>Notifications are a good start, but it would be even nicer if we could give the user some visual indication of which parts they can actually use offline, and which not.</p>
<p>To do this, we can loop over all the links on page load and check their <code>href</code> against the cache. If they point to a cached resource (e.g. will work offline), they get a special class.</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">const</span> links <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">querySelectorAll</span><span class="token punctuation">(</span><span class="token string">'a[href]'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
Array<span class="token punctuation">.</span><span class="token function">from</span><span class="token punctuation">(</span>links<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">link</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
  caches<span class="token punctuation">.</span><span class="token function">match</span><span class="token punctuation">(</span>link<span class="token punctuation">.</span>href<span class="token punctuation">,</span> <span class="token punctuation">{</span> <span class="token literal-property property">ignoreSearch</span><span class="token operator">:</span> <span class="token boolean">true</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">response</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span>response<span class="token punctuation">)</span> <span class="token punctuation">{</span>
      link<span class="token punctuation">.</span>classList<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span><span class="token string">'is-cached'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
  <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>Once the <code>offline</code> event fires, we toggle a class on the body and visually disable all links that aren’t cached. This should only apply to URLs, so we can ignore <code>tel:</code>, <code>mailto:</code> and anchor links.</p>
<pre class="language-scss"><code class="language-scss"><span class="token selector">.is-offline </span><span class="token punctuation">{</span>
  <span class="token comment">/* disable all links to uncached pages */</span>
  <span class="token property">a</span><span class="token punctuation">:</span><span class="token function">not</span><span class="token punctuation">(</span>.is-cached<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token property">cursor</span><span class="token punctuation">:</span>not-allowed<span class="token punctuation">;</span>
    <span class="token property">pointer-events</span><span class="token punctuation">:</span> none<span class="token punctuation">;</span>
    <span class="token property">opacity</span><span class="token punctuation">:</span>.5<span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
  <span class="token comment">/* ignore anchors, email and phone links */</span>
  <span class="token selector">a[href^="#"],
  a[href^="mailto"],
  a[href^="tel"] </span><span class="token punctuation">{</span>
    <span class="token property">cursor</span><span class="token punctuation">:</span>auto<span class="token punctuation">;</span>
    <span class="token property">pointer-events</span><span class="token punctuation">:</span> auto<span class="token punctuation">;</span>
    <span class="token property">opacity</span><span class="token punctuation">:</span>1<span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre>
<h2 id="offline-forms" tabindex="-1">Offline Forms</h2>
<a class="heading-anchor" href="https://masakazu.netlify.app/blog/youre-offline/h-offline-forms"><span class="sr-only">Permalink to “Offline Forms”</span> <span aria-hidden="true">#</span></a><p>Another way we might use this is to prevent users from filling out forms. Most forms pass data to the server and require a connection to work, so they won’t be very useful when offline.</p>
<p>What’s worse is that users might not know there is a problem until it’s too late: imagine filling out a lengthy form and finally hitting the submit button, only to find a network connection error page and all your inputs gone. That’s frustrating.</p>
<pre class="language-scss"><code class="language-scss"><span class="token comment">/* Disable Forms when offline */</span>
<span class="token selector">.is-offline form </span><span class="token punctuation">{</span>
  <span class="token property">position</span><span class="token punctuation">:</span>relative<span class="token punctuation">;</span>
  <span class="token property">opacity</span><span class="token punctuation">:</span>.65<span class="token punctuation">;</span>
  <span class="token property">cursor</span><span class="token punctuation">:</span>not-allowed<span class="token punctuation">;</span>
  <span class="token property">pointer-events</span><span class="token punctuation">:</span>none<span class="token punctuation">;</span>
  
  <span class="token selector"><span class="token parent important">&amp;</span>::after </span><span class="token punctuation">{</span>
    <span class="token property">content</span><span class="token punctuation">:</span> <span class="token string">'Sorry, you\'re offline.'</span><span class="token punctuation">;</span>
    <span class="token property">display</span><span class="token punctuation">:</span>block<span class="token punctuation">;</span>
    <span class="token property">position</span><span class="token punctuation">:</span>absolute<span class="token punctuation">;</span>
    <span class="token property">top</span><span class="token punctuation">:</span>50%<span class="token punctuation">;</span>
    <span class="token property">left</span><span class="token punctuation">:</span>50%<span class="token punctuation">;</span>
    <span class="token property">transform</span><span class="token punctuation">:</span><span class="token function">translate</span><span class="token punctuation">(</span>-50%<span class="token punctuation">,</span> -50%<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token property">color</span><span class="token punctuation">:</span>#FFFFFF<span class="token punctuation">;</span>
    <span class="token property">background-color</span><span class="token punctuation">:</span>#2D2D2D<span class="token punctuation">;</span>
    <span class="token property">padding</span><span class="token punctuation">:</span>1rem 2rem<span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre>
<figure>
  <img src="https://masakazu.netlify.app/blog/youre-offline/form-offline.jpg" alt="a disabled form with the words 'sorry, youre offline' in a box on top" />
  <figcaption>No contact forms in offline country.</figcaption>
</figure>
<p>That effectively disables every form on the page, indicating that this functionality is currently not available. Depending on what your form does, you might also consider applying these styles just to the submit button - that way a user could pre-fill the form (possibly even have it validated in JS), and then submit it once they come back online.</p>
<p>If you’re doing this, remember to suppress “submit on enter” as well, and make sure the user knows why submitting won’t work at the moment.</p>
<p><strong>UPDATE:</strong> I found a better way to handle this - by storing form submissions in <code>localStorage</code> and then checking for them once the connection comes back online. Read about it in <a href="https://mxb.at/blog/offline-forms/">“Offline-Friendly Forms”</a>.</p>
<h2 id="further-reading" tabindex="-1">Further Reading</h2>
<a class="heading-anchor" href="https://masakazu.netlify.app/blog/youre-offline/h-further-reading"><span class="sr-only">Permalink to “Further Reading”</span> <span aria-hidden="true">#</span></a><ul>
<li>Intro: <a href="http://offlinefirst.org/">OfflineFirst.org</a></li>
<li>Google Developers: <a href="https://developers.google.com/web/fundamentals/instant-and-offline/web-storage/offline-for-pwa">Offline Storage</a></li>
<li>Jake Archibald at I/O 2016: <a href="https://www.youtube.com/watch?v=cmGr0RszHc8">Building offline-first PWAs</a> (Video)</li>
</ul>

            ]]></content>
        </entry>
        <entry>
            <title>Bottle Slider Wiggle Effect</title>
            <link href="https://masakazu.netlify.app/blog/bottle-slider-wiggle-effect/"/>
            <updated>2017-03-08T00:00:00Z</updated>
            <id>https://masakazu.netlify.app/blog/bottle-slider-wiggle-effect/</id>
            <content type="html"><![CDATA[
                <div class="extend">
  <video poster="https://masakazu.netlify.app/blog/bottle-slider-wiggle-effect/bottleslider-still.jpg" preload="" autoplay="autoplay" loop="loop" width="960" height="360">
    <source src="https://masakazu.netlify.app/blog/bottle-slider-wiggle-effect/bottleslider.webm" type="video/webm" />
    <source src="https://masakazu.netlify.app/blog/bottle-slider-wiggle-effect/bottleslider.mp4" type="video/mp4" />
  </video>
</div>
<p class="lead">I built this product slider as part of a wine shop I was working on in 2015, and since it's also featured in a case study here on my site, I had a couple of people asking me how the animation was done.</p>
<p>Well, it’s really quite simple – so here’s a quick rundown on how to make the bottles dance. You can see the actual live thing in action on <a href="http://www.weingut-huber.at/product/gruener-veltliner-alte-setzen-erste-lage-2015/">one of the product pages here</a>. Grab some Grüner Veltliner while you’re at it.</p>
<h2 id="the-slider" tabindex="-1">The Slider</h2>
<a class="heading-anchor" href="https://masakazu.netlify.app/blog/bottle-slider-wiggle-effect/h-the-slider"><span class="sr-only">Permalink to “The Slider”</span> <span aria-hidden="true">#</span></a><p>Markup is pretty straightforward, just your standard slider structure. A parent <code>div</code> and an <code>ul</code> with some list items. The real production version obviously has a little bit more going on, what with that fancy ratings popover and all. But for now, this should do the job:</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>slider<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>ul</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>slider__content<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>

    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>li</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>slider__item<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
      <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>link/to/product<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>img</span> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>image_of_bottle.jpg<span class="token punctuation">"</span></span> <span class="token attr-name">alt</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span>
      <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>a</span><span class="token punctuation">></span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>li</span><span class="token punctuation">></span></span>

    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>li</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>slider__item<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>...<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>li</span><span class="token punctuation">></span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>li</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>slider__item<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>...<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>li</span><span class="token punctuation">></span></span>

  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>ul</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span></code></pre>
<p>To make this into a slider widget, you will need some CSS and a bit of Javascript. I used a jQuery plugin called <a href="https://github.com/woocommerce/FlexSlider">Flexslider</a> for this one, and I like it a lot. But almost any other slider would work too. The only important part for this effect is a callback function that gets triggered <strong>before</strong> each sliding transition.</p>
<p>Flexslider does exactly that with its <code>before</code> method. You pass it the <code>$slider</code> variable (the parent element), and then apply a class to it that later controls the animation state. After the animation has finished, we need to remove that class again. My wiggle lasts about a second, so I put in a <code>setTimeout</code> for that duration (plus a little more for good measure).</p>
<pre class="language-js"><code class="language-js">$slider<span class="token punctuation">.</span><span class="token function">flexslider</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
  <span class="token comment">//animation: 'slide',</span>
  <span class="token comment">//selector: '.slider__item',</span>
  <span class="token comment">//animationLoop: false,</span>
  <span class="token comment">//slideshow: false,</span>
  <span class="token function-variable function">before</span><span class="token operator">:</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">$slider</span><span class="token punctuation">)</span><span class="token punctuation">{</span>
    $slider<span class="token punctuation">.</span><span class="token function">addClass</span><span class="token punctuation">(</span><span class="token string">'slider--shaking'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    window<span class="token punctuation">.</span><span class="token function">setTimeout</span><span class="token punctuation">(</span><span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span>
      $slider<span class="token punctuation">.</span><span class="token function">removeClass</span><span class="token punctuation">(</span><span class="token string">'slider--shaking'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token number">1200</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<h2 id="the-animation" tabindex="-1">The Animation</h2>
<a class="heading-anchor" href="https://masakazu.netlify.app/blog/bottle-slider-wiggle-effect/h-the-animation"><span class="sr-only">Permalink to “The Animation”</span> <span aria-hidden="true">#</span></a><p>Next up is the actual CSS keyframe animation that makes the bottles swing from side to side. Mine looks like this:</p>
<pre class="language-css"><code class="language-css"><span class="token atrule"><span class="token rule">@keyframes</span> wiggle</span> <span class="token punctuation">{</span>
  <span class="token selector">25%</span>  <span class="token punctuation">{</span> <span class="token property">transform</span><span class="token punctuation">:</span> <span class="token function">rotate3d</span><span class="token punctuation">(</span>0<span class="token punctuation">,</span> 0<span class="token punctuation">,</span> 1<span class="token punctuation">,</span> 6deg<span class="token punctuation">)</span>  <span class="token punctuation">}</span>
  <span class="token selector">50%</span>  <span class="token punctuation">{</span> <span class="token property">transform</span><span class="token punctuation">:</span> <span class="token function">rotate3d</span><span class="token punctuation">(</span>0<span class="token punctuation">,</span> 0<span class="token punctuation">,</span> 1<span class="token punctuation">,</span> -4deg<span class="token punctuation">)</span> <span class="token punctuation">}</span>
  <span class="token selector">75%</span>  <span class="token punctuation">{</span> <span class="token property">transform</span><span class="token punctuation">:</span> <span class="token function">rotate3d</span><span class="token punctuation">(</span>0<span class="token punctuation">,</span> 0<span class="token punctuation">,</span> 1<span class="token punctuation">,</span> 2deg<span class="token punctuation">)</span>  <span class="token punctuation">}</span>
  <span class="token selector">100%</span> <span class="token punctuation">{</span> <span class="token property">transform</span><span class="token punctuation">:</span> <span class="token function">rotate3d</span><span class="token punctuation">(</span>0<span class="token punctuation">,</span> 0<span class="token punctuation">,</span> 1<span class="token punctuation">,</span> 0deg<span class="token punctuation">)</span>  <span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre>
<p>We tilt the items first right, then left, then right again, losing momentum in each turn to simulate the inertia a real bottle would have.<br />
The <code>rotate3d</code> is to force graphic acceleration, which makes for smoother animation performance. Also, be sure to include vendor prefixes for the transform - or, if you’re lazy like me, let <a href="https://www.npmjs.com/package/gulp-autoprefixer">Autoprefixr</a> do that for you.</p>
<p>The last step is to apply the keyframe animation to your slider every time it gets triggered.<br />
Two things are important here:</p>
<ol>
<li>
<p>define the <code>transform-origin</code> for each object. This will be the fixed point that anchors the animation, it corresponds to the center of gravity in the real world. For my bottles, that would be center bottom.</p>
</li>
<li>
<p>💡__PRO TIP:__ to make it seem more realistic, apply a little delay to every other bottle, so they dont all wiggle in unison. A small offset in timing does the trick.</p>
</li>
</ol>
<pre class="language-css"><code class="language-css"><span class="token selector">.slider--shaking .slider__item</span> <span class="token punctuation">{</span>
  //disable hover effects while transitioning
  <span class="token property">pointer-events</span><span class="token punctuation">:</span>none<span class="token punctuation">;</span>

  //set up the wiggle animation
  <span class="token property">animation-name</span><span class="token punctuation">:</span> wiggle<span class="token punctuation">;</span>
  <span class="token property">animation-duration</span><span class="token punctuation">:</span> 1s<span class="token punctuation">;</span>
  <span class="token property">animation-fill-mode</span><span class="token punctuation">:</span> both<span class="token punctuation">;</span>

  //set the <span class="token string">'fixed point'</span> of the animation
  <span class="token property">transform-origin</span><span class="token punctuation">:</span>bottom center<span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token selector">.slider--shaking .slider__item:nth-child(2n)</span> <span class="token punctuation">{</span>
  //slightly offset every other item
  <span class="token property">animation-delay</span><span class="token punctuation">:</span>.1s<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>Aaand you’re done! Not much to it, but makes for a nice touch and a cool thing to show off. 🍾</p>

            ]]></content>
        </entry></feed>
