<?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"><channel><title><![CDATA[Abhishek Ghosh]]></title><description><![CDATA[Blog by Abhishek Ghosh]]></description><link>https://www.ghosh.dev</link><generator>GatsbyJS</generator><lastBuildDate>Tue, 29 Mar 2022 08:46:06 GMT</lastBuildDate><item><title><![CDATA[Playing with video scrubbing animations on the web]]></title><link>https://www.ghosh.dev/posts/playing-with-video-scrubbing-animations-on-the-web/</link><guid isPermaLink="false">https://www.ghosh.dev/posts/playing-with-video-scrubbing-animations-on-the-web/</guid><pubDate>Sat, 28 Dec 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Videos are but a sequence of consecutive images (or frames) with small differences being painted in
rapid succession to provide the illusion of motion. Before folks can chase me with pitchforks
angered at the gross oversimplification of what goes into storing and playing digital videos of this
age - the keyframes, the deltas, the interpolation and all the intelligent algorithms that allow us
to encode every required bit of information into a much more compressed format as opposed to a naive
sequence of full frames images - allow me to capture the intent of my conversation: all animation for
that matter, digital or otherwise, is built on this basic founding premise.&lt;/p&gt;
&lt;p&gt;For normal video playback, the primary input variable is nothing but a synthesized numerical value
that is repeatedly updated in accordance to how we human beings perceive the passage of “time”.
Given a specific value, we know which frame to display. Done repeatedly, we have motion picture.&lt;/p&gt;
&lt;p&gt;It’s not hard to imagine that this input variable can be fed in by other sources apart from the so
customary time-axis. What about space co-ordinates? Say the user’s &lt;strong&gt;scroll position&lt;/strong&gt; on a page? Or
any action that the user takes which can be crunched through a mathematical function and reduced to
a value on a &lt;a href=&quot;https://en.wikipedia.org/wiki/Number_line&quot;&gt;number line&lt;/a&gt;? Such patterns are fairly well
established and sometimes commonplace. Occasionally, they help build quite the creative user
experience. Apple Inc., for one, has time and again exhibited their affinity for such patterns,
most recently with their &lt;a href=&quot;https://www.apple.com/airpods-pro/&quot;&gt;Airpods Pro&lt;/a&gt; website.&lt;/p&gt;
&lt;p&gt;So far every time, almost to a fault, &lt;a href=&quot;https://twitter.com/rauchg/status/1189327508442943488&quot;&gt;implementation
details&lt;/a&gt; have revealed that to present us with
such animations, a large set of images representing individual frames are downloaded and selectively
displayed in rapid succession on the screen in response to an input signal such as a scroll event.
That’s downloading a lot of image files whose content vary very little incrementally from one frame
image to the next by design. In the process of doing so, are we throwing all the advancements we’ve made
together as a tech community in the world of video compression, out of the window?&lt;/p&gt;
&lt;p&gt;From my understanding, this is mostly because of the limitations of Web API (or a lack thereof) that
would allow us to efficiently go back and forth to paint a specific frame from a video loaded on
a web page in a manner that is fast and responsive. The sentiment is perhaps shared and the
limitation is &lt;a href=&quot;https://twitter.com/jaffathecake/status/1189653092117155842&quot;&gt;acknowledged&lt;/a&gt; too.&lt;/p&gt;
&lt;p&gt;With all that being said, this article is an attempt to proverbially dip my feet into the water of
how we build such experiences and hopefully be able to share some learnings from a bunch of quick
prototypes of potential web video frame extraction and scrubbing techniques within the confines of
existing limitations of today. The overarching theme is trying to extract necessary frames out
of a video either on the client (in-browser) or aided by a server (as in the example above), such
that they can later be used to provide a video scrubbing experience based on page scrolling.&lt;/p&gt;
&lt;p&gt;All of this comes available with &lt;a href=&quot;https://video-scrub.playground.ghosh.dev/&quot;&gt;live demos&lt;/a&gt; and &lt;a href=&quot;https://github.com/abhishekcghosh/experiment-video-scrub&quot;&gt;source
code&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The final video used for the
purpose of these demos is taken from a &lt;a href=&quot;https://gist.github.com/jsturgis/3b19447b304616f18657&quot;&gt;public list of
samples&lt;/a&gt; that I found and is a 1280x720p
resolution 15-second-duration video with a download size of ~2.5MB. My tests were run on Chrome 78
on 2015 15” Macbook Pro (desktop) and Chrome 78 for Android on a Oneplus 5 (Snapdragon 835 SoC
with 8GB RAM) mobile phone, all over a &lt;a href=&quot;https://www.speedtest.net/result/8895140476&quot;&gt;fairly good&lt;/a&gt; WiFi
connection.&lt;/p&gt;
&lt;h1 id=&quot;Approaches&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#Approaches&quot; aria-label=&quot;Approaches permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Approaches&lt;/h1&gt;
&lt;h2 id=&quot;1-video-current-time-demo&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#1-video-current-time-demo&quot; aria-label=&quot;1 video current time demo permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;#1: video-current-time (&lt;a href=&quot;https://video-scrub.playground.ghosh.dev/video-current-time/&quot;&gt;demo&lt;/a&gt;)&lt;/h2&gt;
&lt;p&gt;This mechanism simply loads the video in an HTML5 &lt;code class=&quot;language-text&quot;&gt;video&lt;/code&gt; tag and sets the &lt;code class=&quot;language-text&quot;&gt;currentTime&lt;/code&gt; property of
the loaded video to scrub it when scrolling. We do not specifically extract out frames from the
video, instead, we just let the normal video playing experience on the web take care of it and see
how it does.&lt;/p&gt;
&lt;figure class=&quot;float-right&quot; style=&quot;width: 240px&quot;&gt;
    &lt;blockquote&gt;
        &lt;p&gt;The frame drops I observed were stupendous; drastically falling apart as I went about increasing the resolution of the video.&lt;/p&gt;
    &lt;/blockquote&gt;
&lt;/figure&gt;
&lt;p&gt;This somewhat worked out on high-end devices (such as my 15” Macbook Pro) especially with a
not-too-high quality video, or perhaps as long as the browser is fast and powerful enough to be able
to quickly seek back and forth and paint the frames out of the provided video. But it can’t be
trusted beyond that. As expected, on mobile devices (even on a decently well-to-do phone such as a
Oneplus 5 which I use as my primary mobile device), this was quite miserable with no frame updates
happening when the scrolling is in motion, till the UI thread has had the breathing room to update
pixels on the page. I also have a hunch that the browser (tested on Chrome 78 for Android) may be
purposefully doing things (mobile optimisations?) that it doesn’t do on the desktop version that
makes this mechanism not work well on the mobile browser.&lt;/p&gt;
&lt;p&gt;It’s important to realise that browsers internally do a lot of magic to understand and optimise
what’s the best way to display a video and update it on a page… and unless we’re making the
browser’s life easy, it’s going to leave us feeling stupid.&lt;/p&gt;
&lt;p&gt;I’ll admit that the videos I had been playing around with we’re not per se additionally optimised
and specifically encoded in a way to facilitate extremely fast seeking - and we may
&lt;a href=&quot;https://twitter.com/thespite/status/1189328760266510339&quot;&gt;anecdotally&lt;/a&gt; know that it may have been
possible achieve a better experience if we were to do so - but the frame drops I observed were
stupendous; drastically falling apart as I went about increasing the resolution of the video (even
at 720p) which with the intent of the type of experience we’re trying to build here, will probably
be quite hard to sacrifice if we want to build a great experience.&lt;/p&gt;
&lt;h2 id=&quot;2-video-play-unpack-frames-canvas-demo&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#2-video-play-unpack-frames-canvas-demo&quot; aria-label=&quot;2 video play unpack frames canvas demo permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;#2: video-play-unpack-frames-canvas (&lt;a href=&quot;https://video-scrub.playground.ghosh.dev/video-play-unpack-frames-canvas/&quot;&gt;demo&lt;/a&gt;)&lt;/h2&gt;
&lt;p&gt;So the two-line tactic did not work out. Great. Let’s evolve from there.&lt;/p&gt;
&lt;p&gt;What we do here is load the video in a hidden HTML5 &lt;code class=&quot;language-text&quot;&gt;video&lt;/code&gt; tag and &lt;em&gt;unpack&lt;/em&gt; video frames from it by
starting to &lt;code class=&quot;language-text&quot;&gt;play&lt;/code&gt; the video and then listening to &lt;code class=&quot;language-text&quot;&gt;timeupdate&lt;/code&gt; events at regular intervals on the
&lt;code class=&quot;language-text&quot;&gt;video&lt;/code&gt; element being fired as it’s being played, at which point we &lt;code class=&quot;language-text&quot;&gt;pause&lt;/code&gt; the video and grab the
current frame by painting the outcome on an &lt;code class=&quot;language-text&quot;&gt;OffscreenCanvas&lt;/code&gt; element and collecting the frame’s
image bitmap from its 2D context. When done, we start playing the video again, looping through
the process until the video has come to an end.&lt;/p&gt;
&lt;p&gt;The basic idea is to generate a set of static images from the source video by the end of this
exercise. We use an &lt;code class=&quot;language-text&quot;&gt;OffscreenCanvas&lt;/code&gt; for possible &lt;a href=&quot;https://developers.google.com/web/updates/2018/08/offscreen-canvas&quot;&gt;performance
benefits&lt;/a&gt; on top of a normal
&lt;code class=&quot;language-text&quot;&gt;canvas&lt;/code&gt; element, but that would work as well.&lt;/p&gt;
&lt;figure class=&quot;float-left&quot; style=&quot;width: 240px&quot;&gt;
    &lt;blockquote&gt;
        &lt;p&gt;The basic idea is to generate a set of static images from the source video at the end of
this exercise.&lt;/p&gt;
    &lt;/blockquote&gt;
&lt;/figure&gt;
&lt;p&gt;This mechanism works in principle but is not a very smart thing to do for an obvious reason: the
time to extract out the frames is bound to &lt;em&gt;at least&lt;/em&gt; the duration of playback of the video. If you
need to extract out some frames from a 15-second video, be prepared to wait for at least those 15
seconds, no matter how fast your video is downloaded or even cached! On top of that, it would also
take some additional time for all the amount of javascript work that’s happening. On my test setup, our
15-second 1280x720p video took a bit more than 18 seconds to extract out 244 frames on my 15”
Macbook Pro on Chrome 78 whether the video was cached or not on the browser. That’s a &lt;em&gt;lot&lt;/em&gt; of time!&lt;/p&gt;
&lt;p&gt;Once the extraction of frames is done (a set of
&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/ImageBitmap&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;ImageBitmap&lt;/code&gt;&lt;/a&gt; objects representing
the frames is retained in memory), for scrubbing we figure out the correct frame to paint based on
the input signal (scroll position) and then draw the correct frame on a &lt;em&gt;visible&lt;/em&gt; &lt;code class=&quot;language-text&quot;&gt;canvas&lt;/code&gt;
element on the page.&lt;/p&gt;
&lt;p&gt;The scrubbing part itself worked out fairly well - it was fast enough to scroll and scrub around
without any visible lag on pretty much all devices (desktop and mobile) I tested on. Retaining a
representation of frames in a set of image bitmaps in memory which can be painted rapidly on a
&lt;code class=&quot;language-text&quot;&gt;canvas&lt;/code&gt; (as opposed to trying to encode and put them into &lt;code class=&quot;language-text&quot;&gt;img&lt;/code&gt; elements that are then chosen to be
displayed or hidden in quick succession) must have contributed significantly in making the scrubbing
experience smooth by making the browser do less work.&lt;/p&gt;
&lt;h2 id=&quot;3-video-seek-unpack-frames-canvas-demo&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#3-video-seek-unpack-frames-canvas-demo&quot; aria-label=&quot;3 video seek unpack frames canvas demo permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;#3: video-seek-unpack-frames-canvas (&lt;a href=&quot;https://video-scrub.playground.ghosh.dev/video-seek-unpack-frames-canvas/&quot;&gt;demo&lt;/a&gt;)&lt;/h2&gt;
&lt;p&gt;This is quite similar to approach #2 above but it tries to eliminate the glaring video playback
duration wait problem by performing &lt;code class=&quot;language-text&quot;&gt;seek&lt;/code&gt; instead of &lt;code class=&quot;language-text&quot;&gt;play&lt;/code&gt; while extracting out frames. Quite
obvious really when you think about it.&lt;/p&gt;
&lt;p&gt;In the current prototype, a predefined number of frames are unpacked, but this can also be easily
changed to a frame-rate based approach rather than overall count.&lt;/p&gt;
&lt;p&gt;Once frames are extracted, the scrubbing experience works the same.&lt;/p&gt;
&lt;p&gt;Turns out, this is indeed much faster! On the same test setup, the same 15-second 1280x720p video
took about 9 seconds to extract out 244 frames (first hit) and 6 seconds when the video was
cached (subsequent hits). That’s a &lt;strong&gt;2x-3x&lt;/strong&gt; improvement for the same number of frames.&lt;/p&gt;
&lt;p&gt;But yeah. I’d agree that 6 seconds in itself is not a number to proudly strive for.&lt;/p&gt;
&lt;h2 id=&quot;4-video-seek-media-stream-image-capture-demo&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#4-video-seek-media-stream-image-capture-demo&quot; aria-label=&quot;4 video seek media stream image capture demo permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;#4: video-seek-media-stream-image-capture (&lt;a href=&quot;https://video-scrub.playground.ghosh.dev/video-seek-media-stream-image-capture/&quot;&gt;demo&lt;/a&gt;)&lt;/h2&gt;
&lt;p&gt;Again, this is largely similar to the above approaches #2 and #3 in terms of seeking through the
video using an HTML5 &lt;code class=&quot;language-text&quot;&gt;video&lt;/code&gt; tag. But instead of pausing and drawing it on a canvas context to
extract out the frame’s image bitmap data, I wanted to check if we could use
&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/captureStream&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;captureStream()&lt;/code&gt;&lt;/a&gt;
on the &lt;code class=&quot;language-text&quot;&gt;video&lt;/code&gt; element to capture the video stream and then we use the captured stream’s
&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/ImageCapture&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;ImageCapture&lt;/code&gt;&lt;/a&gt; interface to grab
the image bitmap data of a frame at the desired point in time. Well, it works.&lt;/p&gt;
&lt;p&gt;For scrubbing, the same approach is followed.&lt;/p&gt;
&lt;p&gt;I’d be honest - while the approach to use
&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/MediaStream/MediaStream&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;MediaStream&lt;/code&gt;&lt;/a&gt; APIs had
originally somehow struck me as more elegant in concept, in reality, this turned out to be a bit of
a bummer! It was slower than approach #3 performance-wise, taking as much as 12 seconds (first hit)
and 9 seconds (subsequent hits when the video was cached) which is about a &lt;strong&gt;1.3-1.5x&lt;/strong&gt; degradation
compared to directly drawing the video element in an &lt;code class=&quot;language-text&quot;&gt;OffscreenCanvas&lt;/code&gt; and extracting out the image
bitmap from it, on the same test setup. Now I am not 100% certain that I’ve not made any fundamental
mistakes in terms of best practices for using these streaming APIs (I believe I haven’t goofed up),
in retrospect, this was perhaps to be expected due to all the internal complexity that browser has
to take care of to open a media stream and then do things with it. That’s okay - I don’t quite
believe this use-case is something that the MediaStream APIs are intended to solve anyway.&lt;/p&gt;
&lt;h2 id=&quot;5-video-server-frames-demo&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#5-video-server-frames-demo&quot; aria-label=&quot;5 video server frames demo permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;#5: video-server-frames (&lt;a href=&quot;https://video-scrub.playground.ghosh.dev/video-server-frames/&quot;&gt;demo&lt;/a&gt;)&lt;/h2&gt;
&lt;p&gt;This is basically what we discussed in the beginning. Take the &lt;a href=&quot;https://www.apple.com/in/airpods-pro/&quot;&gt;Apple Airpods
Pro&lt;/a&gt; example above. Other people have tried to
&lt;a href=&quot;https://canvas.narative.now.sh/&quot;&gt;replicate&lt;/a&gt; it as well.&lt;/p&gt;
&lt;p&gt;Perhaps the simplest mechanism of all, it
relies on the server to provide a bunch of video frames as images that are downloaded and scrubbed
through.&lt;/p&gt;
&lt;p&gt;This works out really well when you know upfront what exact content (the video and hence the image
frames) you’re going to load and scrub through exactly, which is legitimately a fair assumption to
make in the use-case we’ve been discussing here. You can pre-generate and store a set of frames
easily at build time on your server or CDNs and serve them when required by the client. Within the
context of discussed use-cases it goes along well with another great software design principle I
love and quote from time to time: &lt;a href=&quot;https://www.freecodecamp.org/news/dont-do-it-at-runtime-do-it-at-design-time-c4f59d1775e4/&quot;&gt;Avoid doing at runtime what you can do at design
time.&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;For the same number of frames (244) which were pre-computed and delivered from the server, the
network bytes transferred was about 20% larger (~3MB as opposed to ~2.5MB video), but getting the
frames ready for scrubbing took about 2.5 seconds (first hit) and 1.3 seconds (subsequent hits when
the frame images were cached) which is &lt;strong&gt;3x-4.5x&lt;/strong&gt; faster than having to download the video and then
extract frames from it as fast as we can (approach #3). I should mention though that all of this
happened over a HTTP/2 connection (which is today’s reality) to the same CDN (which surely worked
out in favour of having to make those 244 requests).&lt;/p&gt;
&lt;p&gt;Initially, it seemed that downloading an image sprite with a bunch of frames as opposed to
individual requests for every frame would a good idea, but it turned out to be very tricky. Based on
the actual frame images and parameters like how many frames to fetch, sprites can actually degrade
the performance by visibly increasing size of downloads or at least reduce flexibility. In a world
with HTTP/2, distinct images fare better - we could even prioritise certain frames and bootstrap the
scrubbing experience faster.&lt;/p&gt;
&lt;h2 id=&quot;6-video-wasm-ffmpeg-extract&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#6-video-wasm-ffmpeg-extract&quot; aria-label=&quot;6 video wasm ffmpeg extract permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;#6: video-wasm-ffmpeg-extract&lt;/h2&gt;
&lt;p&gt;Definitely an idea to pursue, although I haven’t yet been able to test this in action.&lt;/p&gt;
&lt;p&gt;The idea is to exploit &lt;a href=&quot;https://webassembly.github.io/&quot;&gt;WebAssembly&lt;/a&gt; to have an in-browser
&lt;a href=&quot;https://www.ffmpeg.org/&quot;&gt;ffmpeg&lt;/a&gt; module loaded which can then be invoked to extract out frames
pretty fast. This should be possible today in theory with projects like
&lt;a href=&quot;https://github.com/Kagami/ffmpeg.js&quot;&gt;ffmpeg.js&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Honestly, I tried going through this but have so far given up having faced several difficulties with
compiling low-level modules into a build of ffmpeg.js that would be necessary for this experiment -
somehow, the default ffpmeg.js builds are not built with the required options needed for performing
frame extracts. Oops!&lt;/p&gt;
&lt;p&gt;I do hope to try again in the future and write another blog post on how that goes.&lt;/p&gt;
&lt;p&gt;One sure shot thing to consider though - for typical small-sized videos or when the actual content
in question is known not to be very dynamic in nature, this sounds like a fairly over-engineered
idea. For one, the WASM library build for ffmpeg.js itself is humongous in size (~14MB) to have it
downloaded and instantiated before any actual work can happen, which is fairly cost-prohibitive for
what I had been trying to achieve here. This might, however, breakeven for other frame extraction
use-cases which fit the bill better - say we’re dynamically changing a lot of video content,
scrubbing through them, saving them back and so on (for eg. in an in-browser video frame extractor and
editor).&lt;/p&gt;
&lt;h1 id=&quot;The-Verdict&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#The-Verdict&quot; aria-label=&quot;The Verdict permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;The Verdict&lt;/h1&gt;
&lt;p&gt;From the numbers, sending out pre-computed frames from the server (approach #5) turned out to be the
most efficient for practical network and device conditions that such use-cases be exposed to in
terms of &lt;strong&gt;overall cost-benfit, complexity and user experience&lt;/strong&gt;. So, looks like Apple’s approach
was right given the circumstances. Otherwise, if I &lt;em&gt;have&lt;/em&gt; to compute it on the client though, I’d go
with approach #3.&lt;/p&gt;
&lt;p&gt;As for users with constrained network connection and device power, I strongly think that such
experiences shouldn’t even go out to such users. Probably find alternate experiences for them that
provide more value. For the sake of completeness, I did try out on slower network connections, #5
still worked more reliably than trying to pull a video which somehow got stuck or kept buffering.&lt;/p&gt;
&lt;figure class=&quot;float-right&quot; style=&quot;width: 240px&quot;&gt;
    &lt;blockquote&gt;
        &lt;p&gt;One of the major costs we&apos;re trading off here is the network consumption vs.
device compute. Given the state of Web APIs, and use-case in question, pre-computed frames from the server is probably the best way to go about it now for production scenarios.&lt;/p&gt;
    &lt;/blockquote&gt;
&lt;/figure&gt;
&lt;p&gt;On a high-level, one of the major costs we’re trading off here is the &lt;strong&gt;network consumption vs.
device compute&lt;/strong&gt;. From the observations, it clearly seems that unless the total download time
(factor of size and round-trips) of our image frames is not massively larger than the video (so much
as to reach a point of inflex), it distinctly works out in favour of downloading pre-computed image
frames rather than the video and then compute out the frames from it. A progressive enhancement to
our approaches #2 through #4 could definitely be that we store the computed frames in a cache
locally and avoid having to generate them every time the page is loaded, but still, the initial costs
far outweigh the benefits when we know what content (the video and hence the frames) is to be
scrubbed. The other obvious trade-off is the choice of the &lt;strong&gt;flexibility of the content&lt;/strong&gt; itself - but
that’s not really a problem if our content is not truly dynamic.&lt;/p&gt;
&lt;p&gt;Given the state of Web APIs, and use-case in question, pre-computed frames from the server is probably
the best way to go about it now for production scenarios. That’s the opinion I’m going to stick with
for now.&lt;/p&gt;
&lt;p&gt;As a bonus, this also opens up pathways for adapting experience parameters such as with the number
of frames to download (animation frame-rate), image format or compression level etc. which can be
easily negotiated with the server to only download what will be used for an optimal experience on
that specific device, based on information on client-side capabilities (device computation power,
memory, network speed, data-saver modes and so on) as compared to having to download one of few
pre-defined video and then extract usable pieces (some frames) from it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Do you have other approaches in mind? Do share in the comment below - I’d be excited to give them
a try!&lt;/strong&gt;&lt;/p&gt;
&lt;h1 id=&quot;Future&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#Future&quot; aria-label=&quot;Future permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Future&lt;/h1&gt;
&lt;p&gt;In a future where native browser support to unpack frames from a video fast and efficiently, or at
least some native API on the browser that provides the capability to write custom logic to do perform
efficient processing on video streams (think codecs) become a reality, this is to hoping that we’ll
not have to be limited to the current antics. But it’s perhaps a bit too early to clearly say.&lt;/p&gt;
&lt;p&gt;Perhaps there is hope with &lt;a href=&quot;https://github.com/WICG/web-codecs/blob/master/explainer.md&quot;&gt;WebCodecs&lt;/a&gt;?&lt;/p&gt;
&lt;h1 id=&quot;Bonus&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#Bonus&quot; aria-label=&quot;Bonus permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Bonus&lt;/h1&gt;
&lt;p&gt;While playing around with these experiments, I decided to quickly hack up together a &lt;a href=&quot;https://video-scrub.playground.ghosh.dev/frame-extract-tool/&quot;&gt;video frame
extract tool&lt;/a&gt; that can take any video
that is uploaded as input and extract out frames from it, conveniently downloaded as a bunch of
JPEG images within a single ZIP file.&lt;/p&gt;
&lt;p&gt;It isn’t an extremely powerful tool as such but is a little bit configurable, such as how many
frames to extract or at what frame rate and gets the job done simply and fairly well.&lt;/p&gt;
&lt;p&gt;Be sure to check it out! I’m also eager to listen to any feedback there is.&lt;/p&gt;
&lt;br&gt;
&lt;br&gt;</content:encoded></item><item><title><![CDATA[Chrome Dev Summit 2019: Everything you need to know]]></title><link>https://www.ghosh.dev/posts/chrome-dev-summit-2019-everything-you-need-to-know/</link><guid isPermaLink="false">https://www.ghosh.dev/posts/chrome-dev-summit-2019-everything-you-need-to-know/</guid><pubDate>Sat, 14 Dec 2019 00:00:00 GMT</pubDate><content:encoded>&lt;figure&gt;
    &lt;blockquote&gt;
        &lt;p&gt;&quot;As the &lt;strong&gt;largest open ecosystem&lt;/strong&gt; in history, the Web is a tremendous utility, with more than 1.5B active websites on the Internet today, serving nearly 4.5B web users across the world. This kind of diversity (geography, device, content, and more) can only be facilitated by the &lt;strong&gt;open web platform&lt;/strong&gt;.&quot;&lt;/p&gt;
        &lt;footer&gt;
            &lt;cite&gt;
                - from &lt;a href=&quot;https://blog.chromium.org/2019/11/chrome-dev-summit-2019-elevating-web.html&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;
                blog.chromium.org&lt;/a&gt;
            &lt;/cite&gt;
        &lt;/footer&gt;
    &lt;/blockquote&gt;
&lt;/figure&gt;
&lt;p&gt;This would be the uber pitch for &lt;a href=&quot;https://developer.chrome.com/devsummit/&quot;&gt;Chrome Dev Summit&lt;/a&gt; this year: &lt;strong&gt;elevating the web platform&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Last month I was in San Francisco for CDS 2019, my first time attending the conference in person. For those of you who couldn’t attend CDS this year or haven’t yet gotten around watching all the sessions on their &lt;a href=&quot;https://www.youtube.com/watch?v=F1UP7wRCPH8&amp;#x26;list=PLNYkxOF6rcIDA1uGhqy45bqlul0VcvKMr&quot;&gt;Youtube channel&lt;/a&gt;, here are my notes and thoughts about almost everything that was announced that you need to know!&lt;/p&gt;
&lt;p&gt;Almost everything you say? Well then, buckle up, for this is going to be a long article!&lt;/p&gt;
&lt;h1 id=&quot;Bridging-the-app-gap&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#Bridging-the-app-gap&quot; aria-label=&quot;Bridging the app gap permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Bridging the “app gap”&lt;/h1&gt;
&lt;p&gt;The web is surely a powerful platform. But there’s still so much that native apps can do that web apps today can not. Think about even some simple things that naturally come to your mind when picturing installed native applications on your phones or computers: accessing contacts or working directly on files on your device, background syncs at regular intervals, system-level cross-app sharing capabilities and such. This is what we call the “native app gap”. And we’re betting that’s there a real need for closing it.&lt;/p&gt;
&lt;figure class=&quot;float-left&quot; style=&quot;width: 240px&quot;&gt;
    &lt;blockquote&gt;
        &lt;p&gt;In their natural progression, browsers have become viable, full-fledged application runtimes.&lt;/p&gt;
    &lt;/blockquote&gt;
&lt;/figure&gt;
&lt;p&gt;Over the last decade, “the browser” has become so much more than a simple tool for accessing information on the web. It is perhaps the most widely-known publicly-used piece of software that there is, yet so heavily underrated at its sophistication. In their natural progression, browsers have become viable, full-fledged application runtimes - containers for not only delivering but also capable of deploying and running a very large variety of cross-platform applications. &lt;a href=&quot;https://en.wikipedia.org/wiki/Node.js&quot;&gt;Technologies&lt;/a&gt; &lt;a href=&quot;https://en.wikipedia.org/wiki/Blink_layout_engine&quot;&gt;arising&lt;/a&gt; out of browser engines have been used for a while to build &lt;a href=&quot;https://code.visualstudio.com/&quot;&gt;massively successful desktop applications&lt;/a&gt;; browsers themselves have been used as the basis of building &lt;a href=&quot;https://en.wikipedia.org/wiki/Chrome_OS&quot;&gt;entire operating system UI&lt;/a&gt; and even run &lt;a href=&quot;https://blog.mozilla.org/blog/2014/03/12/mozilla-and-epic-preview-unreal-engine-4-running-in-firefox/&quot;&gt;complex real-time 3D games&lt;/a&gt; at a performance that is getting closer and closer to native speeds every day. With that train of thought, it seems almost inevitable that the &lt;em&gt;browserverse&lt;/em&gt; would sooner or later tackle the problem of being able to impart the average-joe web app the power to do things native apps can.&lt;/p&gt;
&lt;figure class=&quot;float-right&quot; style=&quot;width: 300px&quot;&gt;
	&lt;img src=&quot;/static/media/thanos-1.jpg&quot; alt=&quot;I am inevitable&quot;&gt;
    &lt;figcaption&gt;
        Source: &lt;a href=&quot;https://imgflip.com/i/3j3vbb&quot;&gt;imgflip.com&lt;/a&gt;
    &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;If I had to bet, the Web combined with its reach and ubiquitous platform support is probably going to become one of the best and perhaps most popular mechanisms for software delivery for a very large subset of applications use-cases in the coming years.&lt;/p&gt;
&lt;h2 id=&quot;Project-Fugu&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#Project-Fugu&quot; aria-label=&quot;Project Fugu permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Project Fugu&lt;/h2&gt;
&lt;p&gt;The goal of &lt;a href=&quot;https://www.chromium.org/teams/web-capabilities-fugu&quot;&gt;Project Fugu&lt;/a&gt; is to make the “app gap” go away. Simply put, the idea is to bake a set of right APIs into the browser, such that over time web apps become capable of doing almost everything that native apps can - with the right levers of permissions and access-control of course, for all your rightly-raised potential privacy and security concerns!&lt;/p&gt;
&lt;figure class=&quot;float-left&quot; style=&quot;width: 300px&quot;&gt;
	&lt;img src=&quot;/static/media/thanos-2.jpg&quot; alt=&quot;They called me a madman&quot;&gt;
    &lt;figcaption&gt;
        Source: &lt;a href=&quot;https://imgflip.com/i/3j3vtu&quot;&gt;imgflip.com&lt;/a&gt;
    &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Enough with the Thanos references, but I wouldn’t be very surprised if someone in the web community called this crazy. And if I absolutely had to, I am going to admit that even if so, this is definitely &lt;em&gt;my&lt;/em&gt; kind of crazy. I like to think that I somehow saw this coming, and I wanted this to happen. For years, browsers &lt;em&gt;have&lt;/em&gt; in some capacity been trying to bring bits and pieces of native power to the web with &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API&quot;&gt;experimental APIs&lt;/a&gt; for things like hardware sensors and such. With Fugu, all this endeavour to impart native-level power to the web has at least been formally unified under one banner and picked up like an umbrella initiative for building and standardising into the open web by the most popular browser project there is. And I’m really excited to see this succeed for the long term! So I’d love to see this turn out like IronMan (the winning, &lt;em&gt;not&lt;/em&gt; the dying part) rather than Thanos in the end!&lt;/p&gt;
&lt;p&gt;The advent and success of &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/Progressive_web_apps&quot;&gt;Progressive Web Apps&lt;/a&gt; (PWA) would have definitely acted as a catalyst at exhibiting that with the right pedigree, web apps can really triumph. Making web apps easily &lt;a href=&quot;https://developers.google.com/web/fundamentals/app-install-banners&quot;&gt;installable&lt;/a&gt; or bringing &lt;a href=&quot;https://developers.google.com/web/fundamentals/push-notifications&quot;&gt;push notifications&lt;/a&gt; to the web was perhaps just the first few pieces of this bigger puzzle we have been staring at for a while. Keeping aside whatever Google as a company’s strategies and business motivations may be to so heavily be investing in everything web; if at the end of the day it benefits the entire web user and developer community by making the web platform more capable, and I choose to believe that it will, I am surely going to sleep happier at night.&lt;/p&gt;
&lt;figure class=&quot;float-right&quot; style=&quot;width: 240px&quot;&gt;
	&lt;img src=&quot;/static/media/fugu-logo-1.png&quot; alt=&quot;Project Fugu&quot;&gt;
    &lt;figcaption&gt;
        Source: &lt;a href=&quot;https://slides.com/mhadaily/hardware-connectivity-on-pwa#/0/7&quot;&gt;slides.com&lt;/a&gt;
    &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;In case you’ve been wondering, the word “Fugu” is Japanese for &lt;a href=&quot;https://en.wikipedia.org/wiki/Tetraodontidae&quot;&gt;pufferfish&lt;/a&gt;, one of the most toxic and poisonous species of vertebrates in the world, incidentally also prepared and consumed as a delicacy, which as you can guess, can be extremely dangerous from the poison if not prepared right. &lt;em&gt;See what they did there?&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Some of the upcoming and interesting capabilities that were announced as part of Project Fugu are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/native-file-system/&quot;&gt;Native Filesystem API&lt;/a&gt; which enables developers to build web apps that interact with files on the users’ local device, like IDEs, photo and video editors or text editors.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/contact-picker/&quot;&gt;Contact Picker API&lt;/a&gt;, an on-demand picker that allows users to select entries from their contact list and share limited details of the selected entries with a website.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/web-share/&quot;&gt;Web Share&lt;/a&gt; and &lt;a href=&quot;https://web.dev/web-share-target/&quot;&gt;Web Share Target&lt;/a&gt; APIs which together allow web apps to use the same system-provided share capabilities as native apps.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/sms-receiver-api-announcement/&quot;&gt;SMS Receiver API&lt;/a&gt;, using which web apps can now use to auto-verify phone numbers with SMS.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developers.google.com/web/updates/2018/05/webauthn&quot;&gt;WebAuthN&lt;/a&gt; to let web apps access hardware tokens (eg. &lt;a href=&quot;https://en.wikipedia.org/wiki/YubiKey&quot;&gt;YubiKey&lt;/a&gt;) or perform biometrics (like a fingerprint or facial recognition) based identification and recognition of users on the web.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/get-installed-related-apps/&quot;&gt;getInstalledRelatedApps() API&lt;/a&gt; that allows your web app to check whether &lt;em&gt;your&lt;/em&gt; native app is installed on a user’s device, and vice versa.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/periodic-background-sync/&quot;&gt;Periodic Background Sync API&lt;/a&gt; for syncing your web app’s data periodically in the background and possibly providing more powerful and creative offline use-cases for a more native-app-like experience.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/shape-detection/&quot;&gt;Shape Detection API&lt;/a&gt; to easily detect faces, barcodes, and text in images.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/badging-api/&quot;&gt;Badging API&lt;/a&gt; that allows installed web apps to set an application-wide badge on the app icon.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/wakelock/&quot;&gt;Wake Lock API&lt;/a&gt; for providing a mechanism to prevent devices from dimming or locking the screen when a web app needs to keep running.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.chromestatus.com/feature/5133150283890688&quot;&gt;Notification Triggers&lt;/a&gt; for triggering notifications using timers or events apart from a server push.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The list goes on. These features are either in early experimentation through &lt;a href=&quot;https://github.com/GoogleChrome/OriginTrials/blob/gh-pages/explainer.md&quot;&gt;Origin Trials&lt;/a&gt; or targeted to be built in the future. Here is an open &lt;a href=&quot;https://goo.gle/fugu-api-tracker&quot;&gt;tracker&lt;/a&gt; for the list of APIs and their progress that have been captured so far under the banner of Project Fugu.&lt;/p&gt;
&lt;figure&gt;
	&lt;img src=&quot;/static/media/fugu-process-1.jpg&quot; alt=&quot;Project Fugu process&quot;&gt;
    &lt;figcaption&gt;
        Source: &lt;a href=&quot;https://developers.google.com/web/updates/capabilities#process&quot;&gt;developer.google.com&lt;/a&gt;
    &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Also worth noting, &lt;a href=&quot;https://webwewant.fyi/&quot;&gt;webwewant.fyi&lt;/a&gt; was as announced as well, which is a great place for anyone in the web community to go and provide feedback about the state of the web and things that we want the web to do!&lt;/p&gt;
&lt;h2 id=&quot;On-PWAs-wait-now-we-have-TWAs&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#On-PWAs-wait-now-we-have-TWAs&quot; aria-label=&quot;On PWAs wait now we have TWAs permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;On PWAs… wait, now we have TWAs?&lt;/h2&gt;
&lt;p&gt;Progressive Web Apps with their installability and native-like fullscreen immersive experiences have been Google’s &lt;a href=&quot;https://developers.google.com/web/showcase/tags/progressive-web-apps&quot;&gt;showcase fodder&lt;/a&gt; for over multiple years of Google I/O and Chrome Dev Summit. Major consumer facing brands like Flipkart, Twitter, Spotify, Pinterest, Starbucks, Airbnb, Alibaba, BookMyShow, MakeMyTrip, Housing, Ola, OYO have all built and shown how great PWA based experiences can be made which are hard to distinguish from native apps by the average user. By this point in time, I think as a community, we generally understand and agree that PWAs can be awesome when done right. So what next?&lt;/p&gt;
&lt;figure class=&quot;float-left&quot; style=&quot;width: 300px&quot;&gt;
	&lt;img src=&quot;/static/media/twa-logo-1.jpg&quot; alt=&quot;TWA&quot;&gt;
    &lt;figcaption&gt;
        Source: &lt;a href=&quot;https://medium.com/@firt/google-play-store-now-open-for-progressive-web-apps-ec6f3c6ff3cc&quot;&gt;medium.com&lt;/a&gt;
    &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;A key development for installable web apps has been the emergence of &lt;a href=&quot;https://developers.google.com/web/updates/2019/02/using-twa&quot;&gt;Trusted Web Activities&lt;/a&gt; (TWA) which provide a way to integrate full-screen web content into Android apps using a protocol called Custom Tabs: in our case, &lt;a href=&quot;https://developer.chrome.com/multidevice/android/customtabs&quot;&gt;Chrome Custom Tabs&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;To quote from the &lt;a href=&quot;https://blog.chromium.org/2019/02/introducing-trusted-web-activity-for.html&quot;&gt;Chromium Blog&lt;/a&gt;, TWAs have access to all Chrome features and functionalities including many which are not available to a standard Android WebView, such as &lt;a href=&quot;https://developers.google.com/web/fundamentals/push-notifications/&quot;&gt;web push notifications&lt;/a&gt;, &lt;a href=&quot;https://developers.google.com/web/updates/2015/12/background-sync&quot;&gt;background sync&lt;/a&gt;, &lt;a href=&quot;https://support.google.com/chrome/answer/142893?co=GENIE.Platform%3DDesktop&amp;#x26;hl=en&quot;&gt;form autofill&lt;/a&gt;, &lt;a href=&quot;https://www.w3.org/TR/media-source/&quot;&gt;media source extensions&lt;/a&gt; and the &lt;a href=&quot;https://developers.google.com/web/updates/2016/09/navigator-share&quot;&gt;sharing API&lt;/a&gt;. A website loaded in a TWA shares stored data with the Chrome browser, including cookies. This implies shared session state, which for most sites means that if a user has previously signed into your website in Chrome they will also be signed into the TWA.&lt;/p&gt;
&lt;p&gt;Basically think of how PWAs work today, except that you can install it from the Play Store, with possibly some other added benefits of app-like privileges. It was briefly mentioned that with TWAs the general permission model of certain web app capabilities (such as having to ask for push notification permissions) may go away and become as simple and elevated as true native Android apps (considering they &lt;em&gt;are&lt;/em&gt;, in fact, so).&lt;/p&gt;
&lt;p&gt;As an application of all this, TWAs are now Google’s recommended for web app developers to surface their &lt;em&gt;PWA listings on the Play Store&lt;/em&gt;. Here’s their &lt;a href=&quot;https://youtu.be/Hp_dQvQyYEI?list=PLNYkxOF6rcIDA1uGhqy45bqlul0VcvKMr&amp;#x26;t=1516&quot;&gt;showcase&lt;/a&gt; on OYO.&lt;/p&gt;
&lt;figure class=&quot;float-right&quot; style=&quot;width: 240px&quot;&gt;
    &lt;blockquote&gt;
        &lt;p&gt;Imagine having native apps that almost never need to go through painful or slow update cycles and consume very little disk space.&lt;/p&gt;
    &lt;/blockquote&gt;
&lt;/figure&gt;
&lt;p&gt;I believe TWAs can open up some really interesting avenues. Imagine having native apps that almost never need to go through painful or slow update cycles (or hence have to deal with all the typical complexities of releasing and maintaining native apps and their codebases), because like everything web, the actual UI and content always updates on the fly! These apps get installed on a user’s device, consume very little disk space compared to full-blown native apps because of an effectively shared runtime host (the browser) and work at full capacity of that browser, say Chrome. This is quite different from embedding WebViews into hybrid native applications for a number of reasons where the main browser on a user’s device can do more powerful things or have access to information that WebView components embedded into individual native apps can not.&lt;/p&gt;
&lt;p&gt;If today you maintain a native app, a &lt;em&gt;lite&lt;/em&gt; version of your native app, &lt;em&gt;and&lt;/em&gt; a mobile website (like Facebook does); now, if you want, your lite app can be simply your mobile website distributed via the Play Store wrapped in a TWA. One less codebase to maintain. Phew.&lt;/p&gt;
&lt;p&gt;I haven’t yet played around with deploying a TWA first-hand myself, so some of the low-level processes are still unclear to me, but from what I could gather talking to Google engineers at CDS who have been working on TWAs, because of several Play Store policies and mechanisms based on how it operates today by design, looks like as developers we still need to handcraft a TWA from a PWA, and manually upload and release it onto the Play Store. What would be amazing is being able to hook up some sort of a pipeline onto the Play Store itself that auto-vends a PWA as a TWA given a right, validated config.&lt;/p&gt;
&lt;p&gt;Other improvements coming to PWAs are the &lt;a href=&quot;https://www.w3.org/TR/appmanifest/#shortcuts-member&quot;&gt;shortcuts&lt;/a&gt; member in Web App Manifest which will allow us to register a list of static shortcuts to key URLs within the PWA where the browser exposes these shortcuts via interactions that are consistent with exposure of an application icon’s context menu in the host operating system (e.g., right-click, long press), similar to how native apps do.&lt;/p&gt;
&lt;p&gt;Also, Chrome intends to play with the &lt;a href=&quot;https://www.youtube.com/watch?v=Hp_dQvQyYEI&amp;#x26;feature=youtu.be&amp;#x26;list=PLNYkxOF6rcIDA1uGhqy45bqlul0VcvKMr&amp;#x26;t=82&quot;&gt;“Add to Homescreen” verbiage&lt;/a&gt; and it might probably simply be called “Install” in the future.&lt;/p&gt;
&lt;h1 id=&quot;Because-Performance-Obviously&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#Because-Performance-Obviously&quot; aria-label=&quot;Because Performance Obviously permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Because Performance. Obviously!&lt;/h1&gt;
&lt;h2 id=&quot;Not-all-devices-are-made-equal&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#Not-all-devices-are-made-equal&quot; aria-label=&quot;Not all devices are made equal permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Not all devices &lt;em&gt;are&lt;/em&gt; made equal.&lt;/h2&gt;
&lt;p&gt;And neither the experience of your web app.&lt;/p&gt;
&lt;p&gt;Users access web experiences from a large variety of network conditions (WiFi, LTE, 3G, 2G, optional data-saver modes) and device capabilities (CPU, memory, screen size and resolution) which causes a large performance gap to exist across the spectrum of network types and devices. Multiple Web APIs are available today that can collectively inform us with network and device information which could be used to understand, classify and target users with an adaptive experience for providing them with an optimal journey through our website, given their state of network and device performance. Some APIs that can help us here are the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Network_Information_API&quot;&gt;Network Information API&lt;/a&gt; that informs effective connection type, downlink speed, RTT or data-saver information, &lt;a href=&quot;https://developers.google.com/web/updates/2017/12/device-memory&quot;&gt;DeviceMemory API&lt;/a&gt;, CPU &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/NavigatorConcurrentHardware/hardwareConcurrency&quot;&gt;HardwareConcurrency API&lt;/a&gt; and a mechanism to communicate such information as &lt;a href=&quot;https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/client-hints&quot;&gt;Client-Hints&lt;/a&gt;.&lt;/p&gt;
&lt;figure&gt;
	&lt;img src=&quot;/static/media/adaptive-loading-1.jpg&quot; alt=&quot;Adaptive Loading&quot;&gt;
    &lt;figcaption&gt;
        Source: &lt;a href=&quot;https://youtu.be/puUPpVrIRkc?list=PLNYkxOF6rcIDA1uGhqy45bqlul0VcvKMr&amp;t=454&quot;&gt;Adaptive Loading - improving web performance on slow devices&lt;/a&gt;
    &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Facebook &lt;a href=&quot;https://youtu.be/puUPpVrIRkc?list=PLNYkxOF6rcIDA1uGhqy45bqlul0VcvKMr&amp;#x26;t=1438&quot;&gt;described&lt;/a&gt; on a very high-level their classification models for devices for both mobiles and desktops (which are relatively harder to do) and how they have been effectively using this information to derive the best user experience for targeted segments.&lt;/p&gt;
&lt;p&gt;All this comes paired with the release of &lt;a href=&quot;https://github.com/GoogleChromeLabs/react-adaptive-hooks&quot;&gt;Adaptive Hooks&lt;/a&gt;, which makes it easy to target device and network specs for patterns around resource loading, data-fetching, code-splitting or disabling certain features for your React web app.&lt;/p&gt;
&lt;h2 id=&quot;Better-to-hang-by-more-than-a-thread&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#Better-to-hang-by-more-than-a-thread&quot; aria-label=&quot;Better to hang by more than a thread permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Better to hang by more than a thread.&lt;/h2&gt;
&lt;figure class=&quot;float-right&quot; style=&quot;width: 240px&quot;&gt;
    &lt;blockquote&gt;
        &lt;p&gt;The more work is required to be done during JS execution, it queues up, blocks and slows everything down effectively causing the web app to suffer from jank and feel sluggish.&lt;/p&gt;
    &lt;/blockquote&gt;
&lt;/figure&gt;
&lt;p&gt;To deliver web experiences that feel smooth, a lot of work needs to be done by the browser, ranging from the initial downloading, parsing and execution of HTML, CSS &amp;#x26; Javascript; and all the successive work required to be able to eventually &lt;a href=&quot;https://developers.google.com/web/fundamentals/performance/rendering&quot;&gt;paint pixels&lt;/a&gt; on the screen which includes styling, layouting, painting and compositing; and then to make things interactive, handling multiple events and actions; and in response to that, frequently &lt;em&gt;redoing&lt;/em&gt; most of these above tasks to update content on the page.&lt;/p&gt;
&lt;p&gt;A lot of these tasks need to happen in sequence every time, as frequently as required, within a cumulative span of a &lt;em&gt;few milliseconds&lt;/em&gt; to keep the experience smooth and responsive: about 16ms to deliver 60 frames per second, while some new devices support even higher refresh rates like 90Hz or 120Hz which push the available time to paint a frame to even smaller if you have to keep up with the refresh rate.&lt;/p&gt;
&lt;p&gt;With the combined truth that all devices are not made equal and that JavaScript is single-threaded by nature which means the more work is required to be done during JS execution, it queues up, blocks and slows everything down effectively causing your web app to suffer from &lt;a href=&quot;https://www.afasterweb.com/2015/08/29/what-the-jank/&quot;&gt;jank&lt;/a&gt; and feel sluggish.&lt;/p&gt;
&lt;figure&gt;
	&lt;img src=&quot;/static/media/main-thread-1.png&quot; alt=&quot;Pixel pipeline&quot;&gt;
    &lt;figcaption&gt;
        Source: &lt;a href=&quot;https://youtu.be/7Rrv9qFMWNM?list=PLNYkxOF6rcIDA1uGhqy45bqlul0VcvKMr&amp;t=485&quot;&gt;The main thread is overworked &amp; underpaid&lt;/a&gt;
    &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Patterns to effectively distribute the amount of client-side computational work across multiple threads by leveraging &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers&quot;&gt;web workers&lt;/a&gt; to perform &lt;strong&gt;Off-Main-Thread&lt;/strong&gt; (OMT) work and keep the main (UI) thread reserved for doing only the amount of work that absolutely needs to be done by it (DOM &amp;#x26; UI work) can help significantly to alleviate this problem. Such patterns are about &lt;em&gt;reducing risks&lt;/em&gt; of delivering poor user experiences, where although the entire time of overall work completion maybe, in fact, slowed marginally my message-passing overheads across multiple worker threads, the main (UI) thread is instead free to do any UI work required in the interim (even new user interactions like handling touch or scrolls) and keep delivering a smooth experience throughout. This works out great since the margin of error of dropping a frame is in the order of milliseconds while the making the user wait for an overall task to complete can go into the order of 100s of milliseconds.&lt;/p&gt;
&lt;p&gt;Web workers have existed for a while but somehow for probable reasons around their wonkiness, they haven’t really seen great adoption. Libraries like &lt;a href=&quot;http://npm.im/comlink&quot;&gt;Comlink&lt;/a&gt; can help a lot to that end.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://proxx.app/&quot;&gt;Proxx.app&lt;/a&gt; is a great example to look at all of this in action.&lt;/p&gt;
&lt;h2 id=&quot;Lighthouse-shines-brighter&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#Lighthouse-shines-brighter&quot; aria-label=&quot;Lighthouse shines brighter permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Lighthouse shines brighter!&lt;/h2&gt;
&lt;figure class=&quot;float-right&quot; style=&quot;width: 300px&quot;&gt;
	&lt;img src=&quot;/static/media/lighthouse-logo-1.jpg&quot; alt=&quot;Lighthouse&quot;&gt;
    &lt;figcaption&gt;
        Source: &lt;a href=&quot;https://developers.google.com/web/tools/lighthouse/&quot;&gt;google.com&lt;/a&gt;
    &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Not to throw any shade at &lt;a href=&quot;https://developers.google.com/web/tools/lighthouse&quot;&gt;Lighthouse&lt;/a&gt; &lt;em&gt;(all puns intended)&lt;/em&gt;, so far this web performance auditing tool had always fallen short of my expectations for any practical large-scale use-case. It seemed a little too simple and superficial to be of much use to drive meaningful insights in complex, real-world production applications. Don’t get me wrong, Lighthouse has always been a decent product on its own, but building web performance test tooling that is robust, powerful &lt;em&gt;and&lt;/em&gt; predictable, is inherently a hard problem to solve. Lighthouse had always been somewhat useful to me, but mostly as a basic in-browser audit panel thing that could give me some generic “Performance 101” insights, rather than something I would be excited to hook up as a powerful-enough synthetic performance testing tool in a production pipeline for catering to deeper performance auditing needs that I may have.&lt;/p&gt;
&lt;p&gt;Hopefully, that changes now. With the release of &lt;a href=&quot;https://github.com/GoogleChrome/lighthouse-ci&quot;&gt;Lighthouse CI&lt;/a&gt;, an extension of the toolset for automated assertion, saving and retrieval of historical data and actionable insights for improving web app performance though continuous integrations, the future seems a bit more brighter.&lt;/p&gt;
&lt;p&gt;What’s even more exciting, is the introduction of &lt;a href=&quot;https://github.com/GoogleChrome/lighthouse-stack-packs&quot;&gt;stack-packs&lt;/a&gt; which detect what platform a site is built on (such as Wordpress) and displays specific stack-based recommendations, and &lt;a href=&quot;https://github.com/GoogleChrome/lighthouse/blob/master/docs/plugins.md&quot;&gt;plugins&lt;/a&gt; which provide mechanisms to extend the functionality of Lighthouse with things such as domain-specific insights and scoring, for example, to cater to the bespoke needs of an e-commerce website.&lt;/p&gt;
&lt;h2 id=&quot;-and-comes-with-new-Performance-Metrics&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#-and-comes-with-new-Performance-Metrics&quot; aria-label=&quot; and comes with new Performance Metrics permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;… and comes with new Performance Metrics.&lt;/h2&gt;
&lt;figure&gt;
	&lt;img src=&quot;/static/media/lighthouse-scores-1.jpg&quot; alt=&quot;Lighthouse scores&quot;&gt;
    &lt;figcaption&gt;
        Source: &lt;a href=&quot;https://youtu.be/iaWLXf1FgI0?list=PLNYkxOF6rcIDA1uGhqy45bqlul0VcvKMr&amp;t=509&quot;&gt;Speed tooling evolutions: 2019 and beyond&lt;/a&gt;
    &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;With Lighthouse 6, some important changes are coming to how it scores web page performance, focusing on some of the &lt;em&gt;new&lt;/em&gt; metrics that are being introduced:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/lcp/&quot;&gt;Largest Contentful Paint&lt;/a&gt;(LCP), which measures the render time of the largest content element visible in the viewport.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/lighthouse-total-blocking-time/&quot;&gt;Total Blocking Time&lt;/a&gt; (TBT), a measure of the total amount of time that a page is blocked from responding to user input, such as mouse clicks, screen taps, or keyboard presses. The sum is calculated by adding the &lt;em&gt;blocking portion&lt;/em&gt; of all &lt;a href=&quot;https://web.dev/long-tasks-devtools&quot;&gt;long tasks&lt;/a&gt; between &lt;a href=&quot;https://web.dev/first-contentful-paint/&quot;&gt;First Contentful Paint&lt;/a&gt; and &lt;a href=&quot;https://web.dev/interactive/&quot;&gt;Time to Interactive&lt;/a&gt;. Any task that executes for more than 50ms is a long task. The amount of time after 50ms is the blocking portion. For example, if Chrome detects a 70ms long task, the blocking portion would be 20ms.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/cls/&quot;&gt;Cumulative Layout Shift&lt;/a&gt; (CLS), which measures the sum of the individual &lt;em&gt;layout shift scores&lt;/em&gt; for each &lt;em&gt;unexpected layout shift&lt;/em&gt; that occurs between when the page starts loading and when its &lt;a href=&quot;https://developers.google.com/web/updates/2018/07/page-lifecycle-api&quot;&gt;lifecycle state&lt;/a&gt; changes to hidden. Layout shifts are defined by the &lt;a href=&quot;https://github.com/WICG/layout-instability&quot;&gt;Layout Instability API&lt;/a&gt; and they occur any time an element that is visible in the viewport changes its start position (for example, it’s top and left position in the default &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/writing-mode&quot;&gt;writing mode&lt;/a&gt;) changes between two frames. Such elements are considered &lt;em&gt;unstable elements&lt;/em&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Metrics that are being deprecated are &lt;a href=&quot;https://web.dev/first-meaningful-paint/&quot;&gt;First Meaningful Paint&lt;/a&gt;(FMP) and &lt;a href=&quot;https://web.dev/first-cpu-idle/&quot;&gt;First CPU Idle&lt;/a&gt; (FCI).&lt;/p&gt;
&lt;h2 id=&quot;Visually-marking-slow-vs-fast-websites&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#Visually-marking-slow-vs-fast-websites&quot; aria-label=&quot;Visually marking slow vs fast websites permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Visually marking slow vs. fast websites&lt;/h2&gt;
&lt;p&gt;Chrome has expressed plans to visually indicate what it thinks is a slow vs. a fast loading website. The exact form of how the UI is going to look like is still uncertain, but we can expect a bunch of trials and experiments from Chrome on this.&lt;/p&gt;
&lt;figure&gt;
	&lt;img src=&quot;/static/media/visual-slow-fast-1.png&quot; alt=&quot;Visual indicators for slow vs. fast websites&quot;&gt;
    &lt;figcaption&gt;
        Source: &lt;a href=&quot;https://blog.chromium.org/2019/11/moving-towards-faster-web.html&quot;&gt;Moving towards a faster web&lt;/a&gt;
    &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;I guess that this is going to be mired with some controversy when this happens. How does the browser judge what is right for my website? Why does it get to decide where exactly to draw the line? And how exactly does it do it for the type of website, target audience and content I have? Surely there are a lot of difficult questions with no easy answers yet, but there’s one thing for sure, that if this does happen, it will force the average website to take some web performance considerations more seriously. Somewhat like when browsers together decided to start enforcing HTTPS by visually penalizing non-secure websites and it worked out well eventually. Honestly, with so much that is unclear, for now, I am still going lean towards the side of more free will, but as a self-appointed advocate of web performance, I am very tempted to think that if executed right, this might just be a good thing.&lt;/p&gt;
&lt;h2 id=&quot;Web-Framework-ecosystem-improvements&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#Web-Framework-ecosystem-improvements&quot; aria-label=&quot;Web Framework ecosystem improvements permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Web Framework ecosystem improvements&lt;/h2&gt;
&lt;p&gt;Google has been partnering up with popular web framework developers to make under-the-hood improvements to those frameworks such that sites that are built and run on top of these frameworks get visible performance improvements without having to lift a finger so to speak.&lt;/p&gt;
&lt;figure class=&quot;float-left&quot; style=&quot;width: 240px&quot;&gt;
    &lt;blockquote&gt;
        &lt;p&gt;Choosing to deliver modern Javascript code to modern browsers could visibly improve performance.&lt;/p&gt;
    &lt;/blockquote&gt;
&lt;/figure&gt;
&lt;p&gt;The prime example of this was a partnership with &lt;a href=&quot;https://nextjs.org/&quot;&gt;Next.js&lt;/a&gt;, a popular web framework based on &lt;a href=&quot;https://reactjs.org/&quot;&gt;React&lt;/a&gt;, where a lot of development has happened around improved chunking, differential loading, JS optimisations and capturing better performance metrics.&lt;/p&gt;
&lt;p&gt;An interesting (though quite seemingly obvious in retrospect) takeaway from this was choosing to deliver &lt;em&gt;modern&lt;/em&gt; Javascript code to &lt;em&gt;modern&lt;/em&gt; browsers (as opposed to lengthy transpiled or polyfilled code based on some lowest common denominator of browsers you need to support) could visibly improve performance by drastically reducing the amount of code that is shipped. This was coupled with the announcement of &lt;a href=&quot;https://github.com/babel/preset-modules&quot;&gt;Babel preset-modules&lt;/a&gt; which can help you achieve this.&lt;/p&gt;
&lt;p&gt;If you are interested, the &lt;a href=&quot;https://opencollective.com/chrome&quot;&gt;Framework Fund&lt;/a&gt; run by Chrome has also been announced multiple times across this CDS.&lt;/p&gt;
&lt;h2 id=&quot;How-awesome-is-WebAssembly-now&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#How-awesome-is-WebAssembly-now&quot; aria-label=&quot;How awesome is WebAssembly now permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;How awesome is WebAssembly now?&lt;/h2&gt;
&lt;p&gt;Spoiler alert: pretty awesome! 💯&lt;/p&gt;
&lt;figure class=&quot;float-right&quot; style=&quot;width: 240px&quot;&gt;
	&lt;img src=&quot;/static/media/wasm-logo-1.svg&quot; alt=&quot;WebAssembly&quot;&gt;
    &lt;figcaption&gt;
        Source: &lt;a href=&quot;https://commons.wikimedia.org/wiki/File:Web_Assembly_Logo.svg&quot;&gt;Wikimedia Commons&lt;/a&gt;
    &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/WebAssembly&quot;&gt;WebAssembly&lt;/a&gt; (WASM) is a new language for the web that is designed to run alongside Javascript and as a compilation target from other languages (such as C, C++, Rust, …) to enable performance-intensive software to run within the browser at near-native speeds that were not possible to attain with JS.&lt;/p&gt;
&lt;p&gt;While WebAssembly has been out there being developed and improved upon for a few years, it saw a major announcements this time on performance improvements that brings it closer to being at par with high-performance native code execution, being enabled through the browser’s WASM engine improvements such as &lt;a href=&quot;https://dzone.com/articles/webassembly-caching-when-using-emscripten&quot;&gt;implicit caching&lt;/a&gt;, introduction of support for &lt;a href=&quot;https://developers.google.com/web/updates/2018/10/wasm-threads&quot;&gt;threads&lt;/a&gt;, and &lt;a href=&quot;https://www.chromestatus.com/feature/6533147810332672&quot;&gt;SIMD&lt;/a&gt; (Single Instruction Multiple Data) which is a core capability of modern CPU architectures that enables instructions to be executed multiple times faster.&lt;/p&gt;
&lt;p&gt;Multiple OpenCV-based &lt;a href=&quot;https://riju.github.io/WebCamera/samples/&quot;&gt;demos&lt;/a&gt; that illustrated real-time high-FPS feature recognition, card reading and image information extraction, facial expression detection or replacement within the web browser, all of which have been made possible by the recent WASM improvements, were showcased.&lt;/p&gt;
&lt;p&gt;Check them out, some of them are really cool!&lt;/p&gt;
&lt;h2 id=&quot;The-cake-is-still-a-lie-but-a-delicious-one-at-that&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#The-cake-is-still-a-lie-but-a-delicious-one-at-that&quot; aria-label=&quot;The cake is still a lie but a delicious one at that permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;The cake is still a lie, but a delicious one at that!&lt;/h2&gt;
&lt;p&gt;&lt;em&gt;Here, if you didn’t get that &lt;a href=&quot;https://knowyourmeme.com/memes/the-cake-is-a-lie&quot;&gt;reference&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;figure class=&quot;float-left&quot; style=&quot;width: 240px&quot;&gt;
	&lt;img src=&quot;/static/media/portals-logo-1.png&quot; alt=&quot;Portals&quot;&gt;
    &lt;figcaption&gt;
        Source: &lt;a href=&quot;https://web.dev/hands-on-portals/&quot;&gt;web.dev&lt;/a&gt;
    &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;One of the unique new web platform capabilities that garnered a lot of interest was &lt;a href=&quot;https://web.dev/hands-on-portals/&quot;&gt;Portals&lt;/a&gt;, which aims to enable seamless and animation-capable transitions across &lt;em&gt;page navigations&lt;/em&gt; for multi-page architecture (MPA) based web applications, effectively affording such MPAs a &lt;em&gt;creative lever&lt;/em&gt; to provide smooth and potentially “instantaneous” browsing experiences like native apps or single-page applications (SPA) could provide. They can be used to deliver great app or SPA-like behaviour without the complexity of doing a SPA which often becomes cumbersome to scale and maintain for large websites and with complex and dynamic use-cases.&lt;/p&gt;
&lt;p&gt;Portals are essentially a new type of HTML element that can be instantiated and injected in a page to load another page inside it (in some form similar to IFrames but also different in a lot of ways), keep it hidden or animate it in any form using CSS animations, and when required, navigate &lt;em&gt;into&lt;/em&gt; it - thus performing page navigations in an instant when used effectively.&lt;/p&gt;
&lt;p&gt;An interesting pattern with Portals is that it can also be made to load the page structure (skeleton or stencil, as you may also call it) preemptively even if the exact data is not prefetched and perform a lot of the major tasks of computation (parsing, execution, styling, layout, painting, compositing) for most of the document structure that contributes to web page performance, in effect moving the performance costs of these tasks out-of-band of critical page load latencies, such that when a portal navigation happens, only the data is fetched and filled in onto the page. This would also require some rendering costs but typically much lesser than the entire page’s worth of work.&lt;/p&gt;
&lt;p&gt;Several &lt;a href=&quot;https://www.youtube.com/watch?v=X2zqwMBBvIs&amp;#x26;feature=youtu.be&amp;#x26;list=PLNYkxOF6rcIDA1uGhqy45bqlul0VcvKMr&amp;#x26;t=956&quot;&gt;demos&lt;/a&gt; are available and this API can be seen behind experimental flags in Chrome at the moment.&lt;/p&gt;
&lt;h1 id=&quot;Web-Bundles&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#Web-Bundles&quot; aria-label=&quot;Web Bundles permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Web Bundles&lt;/h1&gt;
&lt;figure class=&quot;float-right&quot; style=&quot;width: 240px&quot;&gt;
	&lt;img src=&quot;/static/media/web-bundles-logo-1.jpg&quot; alt=&quot;Web Bundles&quot;&gt;
    &lt;figcaption&gt;
        Source: &lt;a href=&quot;https://web.dev/web-bundles/&quot;&gt;web.dev&lt;/a&gt;
    &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;In the simplest terms, a &lt;a href=&quot;https://web.dev/web-bundles/&quot;&gt;Web Bundle&lt;/a&gt; is a file format for encapsulating one or more HTTP resources in a single file. It can include one or more HTML files, Javascript files, images, or Stylesheets. Also known as &lt;a href=&quot;https://wicg.github.io/webpackage/draft-yasskin-wpack-bundled-exchanges.html&quot;&gt;Bundled HTTP Exchanges&lt;/a&gt;, it’s part of the &lt;a href=&quot;https://github.com/WICG/webpackage&quot;&gt;Web Packaging&lt;/a&gt; proposal (and as someone wise would say, not to be confused with &lt;a href=&quot;https://webpack.js.org/&quot;&gt;webpack&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;The idea is to enable offline distribution and usage of web apps. Imagine sharing web apps as a single &lt;code class=&quot;language-text&quot;&gt;.wbn&lt;/code&gt; file over anything like Bluetooth, Wi-Fi Direct or USB flash drives and then being able to run them offline on another device in the web application’s origin’s context! In a way, this sort of again veers into the territory of imparting the web more powers like native apps which are easily distributable and executable offline.&lt;/p&gt;
&lt;figure class=&quot;float-left&quot; style=&quot;width: 240px&quot;&gt;
    &lt;blockquote&gt;
        &lt;p&gt;In such countries, a large portion of apps that exist on people’s phones get side-loaded over peer-to-peer mechanisms rather than from over a first-party distribution source like the Play Store.&lt;/p&gt;
    &lt;/blockquote&gt;
&lt;/figure&gt;
&lt;p&gt;If you researched on different modes in how native apps get distributed in countries with emerging markets such as in India, Mid-East or Africa which are heavy on mobile users but generally deprived on public Wi-Fi availability or have predominantly poor, patchy or congested cellular networks, peer-to-peer file-sharing apps like &lt;a href=&quot;https://play.google.com/store/apps/details?id=com.lenovo.anyshare.gps&amp;#x26;hl=en_IN&quot;&gt;Share-It&lt;/a&gt; or &lt;a href=&quot;https://play.google.com/store/apps/details?id=cn.xender&amp;#x26;hl=en_IN&quot;&gt;Xender&lt;/a&gt; are extremely popular in terms of how people share software and a large portion of apps that exist on people’s phones get side-loaded over peer-to-peer mechanisms rather than from over a first-party distribution source like the Play Store. Seems only natural that this would be a place to catch on for web apps as well!&lt;/p&gt;
&lt;p&gt;I need to confess that the premise of Web Bundles does sort of remind me of the age-old &lt;a href=&quot;https://en.wikipedia.org/wiki/MHTML&quot;&gt;MHTML&lt;/a&gt; file format (&lt;code class=&quot;language-text&quot;&gt;.mhtml&lt;/code&gt; or &lt;code class=&quot;language-text&quot;&gt;.mht&lt;/code&gt; files if remember those) that used to be popular a decade back and in fact, is still &lt;a href=&quot;https://en.wikipedia.org/wiki/MHTML#Browser_support&quot;&gt;supported&lt;/a&gt; by all major browsers. MHTML is a web page archive format that could contain HTML and associated assets like stylesheets, javascript, audio, video and even Flash and Java applets of the day, with their content-encoding inspired from the MIME email protocol (leading to the name MIME HTML) to combine stuff into a single file.&lt;/p&gt;
&lt;p&gt;For what it’s worth though, from the limited knowledge that I have so far, I do believe that what we’ll have with Web Packaging is going to be much more complex and powerful and catering to the needs of the Web of &lt;em&gt;this&lt;/em&gt; generation with key differences like being able to run in the browser using the web application’s origin’s context (by being verified using signatures similar to how &lt;a href=&quot;https://developers.google.com/web/updates/2018/11/signed-exchanges&quot;&gt;Signed HTTP Exchanges&lt;/a&gt; may work) rather than being treated as locally saved content like with MTHML. But yeah… can’t deny that it does feel a bit like listening to &lt;a href=&quot;https://www.youtube.com/watch?v=4fndeDfaWCg&quot;&gt;Backstreet Boys&lt;/a&gt; again from those days! Hashtag Nostalgia.&lt;/p&gt;
&lt;h1 id=&quot;More-cool-stuff-with-CSS-Yeah&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#More-cool-stuff-with-CSS-Yeah&quot; aria-label=&quot;More cool stuff with CSS Yeah permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;More cool stuff with CSS! Yeah!&lt;/h1&gt;
&lt;p&gt;I didn’t even try to come up with a better headline for this section, because this is simply how I genuinely feel.&lt;/p&gt;
&lt;h2 id=&quot;New-capabilities&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#New-capabilities&quot; aria-label=&quot;New capabilities permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;New capabilities&lt;/h2&gt;
&lt;p&gt;A quick list of some of the new coolness that’s landing on browsers (or even better, have already landed) are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Scroll_Snap&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;scroll-snap&lt;/code&gt;&lt;/a&gt; that introduces scroll snap positions, which enforce the scroll positions that a &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Glossary/scroll_container&quot;&gt;scroll container’s&lt;/a&gt; &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Glossary/scrollport&quot;&gt;scrollport&lt;/a&gt; may end at after a scrolling operation has completed.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/:focus-within&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;:focus-within&lt;/code&gt;&lt;/a&gt; to represent an element that has received focus or &lt;em&gt;contains&lt;/em&gt; an element that has received focus.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;@media (prefers-*)&lt;/code&gt; queries, namely the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;prefers-color-scheme&lt;/code&gt;&lt;/a&gt;, &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-contrast&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;prefers-contrast&lt;/code&gt;&lt;/a&gt;, &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-reduced-motion&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;prefers-reduced-motion&lt;/code&gt;&lt;/a&gt; and &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-reduced-transparency&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;prefers-reduced-transparency&lt;/code&gt;&lt;/a&gt;, which in &lt;a href=&quot;https://youtu.be/-oyeaIirVC0?list=PLNYkxOF6rcIDA1uGhqy45bqlul0VcvKMr&amp;#x26;t=396&quot;&gt;Adam Argyle’s words&lt;/a&gt;, can together enable you to serve user preferences like, &lt;em&gt;“I prefer a high contrast dark mode motion when in dim-lit environments!”&lt;/em&gt; 😎&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://css-tricks.com/position-sticky-2/&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;position: sticky&lt;/code&gt;&lt;/a&gt; which is a hybrid of relative and fixed positioning, where the element is treated as &lt;code class=&quot;language-text&quot;&gt;relative&lt;/code&gt; positioned until it crosses a specified threshold, at which point it is treated as &lt;code class=&quot;language-text&quot;&gt;fixed&lt;/code&gt; positioned.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Logical_Properties&quot;&gt;CSS logical properties&lt;/a&gt; to provide the ability to control layout through logical, rather than physical, direction and dimension mappings. Think of dynamic directionality based on whether you’re serving &lt;code class=&quot;language-text&quot;&gt;ltr&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;rtl&lt;/code&gt; or &lt;code class=&quot;language-text&quot;&gt;vertical-rl&lt;/code&gt; content. Here’s a &lt;a href=&quot;https://webdesign.tutsplus.com/tutorials/how-to-use-css-logical-properties--cms-33024&quot;&gt;nice article&lt;/a&gt; that talks about it.&lt;/li&gt;
&lt;/ul&gt;
&lt;figure&gt;
	&lt;img src=&quot;/static/media/css-logical-properties-1.jpg&quot; alt=&quot;CSS Logical Properties&quot;&gt;
    &lt;figcaption&gt;
        Source: &lt;a href=&quot;https://www.youtube.com/watch?v=-oyeaIirVC0&amp;list=PLNYkxOF6rcIDA1uGhqy45bqlul0VcvKMr&amp;index=2&quot;&gt;Next-generation web styling&lt;/a&gt;
    &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://css-tricks.com/almanac/selectors/i/is/&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;:is()&lt;/code&gt; selector&lt;/a&gt;, the new name for the &lt;a href=&quot;https://www.w3.org/TR/selectors-4/#matches&quot;&gt;Matches-Any Pseudo-class&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/backdrop-filter&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;backdrop-filter&lt;/code&gt;&lt;/a&gt; which lets you apply graphical effects such as blurring or colour shifting to &lt;em&gt;anything&lt;/em&gt; in the area behind an element!&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;Houdini&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#Houdini&quot; aria-label=&quot;Houdini permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Houdini&lt;/h2&gt;
&lt;figure class=&quot;float-right&quot; style=&quot;width: 240px&quot;&gt;
    &lt;blockquote&gt;
        &lt;p&gt;...creating new CSS features without waiting for them to be implemented natively in browsers.&lt;/p&gt;
    &lt;/blockquote&gt;
&lt;/figure&gt;
&lt;p&gt;CSS Houdini has perhaps for some time been at the top of my &lt;em&gt;hot-new-things&lt;/em&gt; list when thinking about the exciting future of the web. As &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/Houdini&quot;&gt;MDN&lt;/a&gt; describes it, Houdini is a group of APIs being built to give developers direct access to the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/CSS_Object_Model&quot;&gt;CSS Object Model&lt;/a&gt; (CSSOM) and enabling them to write code the browser can parse as CSS, thereby creating new CSS features &lt;em&gt;without waiting for them to be implemented&lt;/em&gt; natively in browsers.&lt;/p&gt;
&lt;p&gt;In other words, it’s an initiative to open up the browser in a way that gives web developers more direct access to be able to hook into styling and layout process of the browser’s rendering engine. And let creativity (with performance) run amok!&lt;/p&gt;
&lt;p&gt;If this is the first time you are hearing about CSS Houdini, take a few moments to let that sink in. Believe me. Then if you like, read a few &lt;a href=&quot;https://www.smashingmagazine.com/2016/03/houdini-maybe-the-most-exciting-development-in-css-youve-never-heard-of/&quot;&gt;other&lt;/a&gt; &lt;a href=&quot;https://www.qed42.com/blog/building-powerful-custom-properties-CSS-houdini&quot;&gt;great&lt;/a&gt; &lt;a href=&quot;https://medium.com/front-end-field-guide/how-to-be-houdini-and-escape-the-limits-of-css-460af7307d41&quot;&gt;articles&lt;/a&gt; on it.&lt;/p&gt;
&lt;p&gt;It’s relatively still quite early in the world of Houdini in terms of cross-browser support, but a few announcements on some good progress were made. &lt;a href=&quot;https://developers.google.com/web/updates/2018/01/paintapi&quot;&gt;Paint API&lt;/a&gt;, &lt;a href=&quot;https://developers.google.com/web/updates/2018/03/cssom&quot;&gt;Typed OM&lt;/a&gt; and &lt;a href=&quot;https://web.dev/css-props-and-vals/&quot;&gt;Properties and Values API&lt;/a&gt; have shipped to Chrome. &lt;a href=&quot;https://github.com/w3c/css-houdini-drafts/blob/master/css-layout-api/EXPLAINER.md&quot;&gt;Layout API&lt;/a&gt; is in Canary.&lt;/p&gt;
&lt;figure&gt;
	&lt;img src=&quot;/static/media/css-houdini-1.jpg&quot; alt=&quot;CSS Houdini&quot;&gt;
    &lt;figcaption&gt;
        Source: &lt;a href=&quot;https://youtu.be/-oyeaIirVC0?list=PLNYkxOF6rcIDA1uGhqy45bqlul0VcvKMr&amp;t=1258&quot;&gt;Next-generation web styling&lt;/a&gt;
    &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Check out the cool &lt;a href=&quot;https://houdini.glitch.me/&quot;&gt;Houdini Spellbook&lt;/a&gt; or the &lt;a href=&quot;https://ishoudinireadyyet.com/&quot;&gt;ishoudinireadyyet.com&lt;/a&gt; website for more.&lt;/p&gt;
&lt;h1 id=&quot;Showing-some--to-HTML&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#Showing-some--to-HTML&quot; aria-label=&quot;Showing some  to HTML permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Showing some ❤ to HTML&lt;/h1&gt;
&lt;h2 id=&quot;Form-elements&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#Form-elements&quot; aria-label=&quot;Form elements permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Form elements&lt;/h2&gt;
&lt;p&gt;In HTML, the visual redesign, accessibility upgrade and extensibility of &lt;a href=&quot;https://www.youtube.com/watch?v=ZFvPLrKZywA&amp;#x26;list=PLNYkxOF6rcIDA1uGhqy45bqlul0VcvKMr&amp;#x26;index=5&quot;&gt;form elements&lt;/a&gt; such as &lt;code class=&quot;language-text&quot;&gt;&amp;lt;select&amp;gt;&lt;/code&gt; or &lt;code class=&quot;language-text&quot;&gt;&amp;lt;date&amp;gt;&lt;/code&gt; is a welcome development! A joint session by Google and Microsoft described what to expect with the customizability of form elements in the future, or even the possibility of new elements that don’t exist yet, like table-views or toggle-switches.&lt;/p&gt;
&lt;h2 id=&quot;Display-Locking&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#Display-Locking&quot; aria-label=&quot;Display Locking permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Display Locking&lt;/h2&gt;
&lt;p&gt;Web content is predominantly large amounts of text and images, and if you think about it, often just a collection of &lt;em&gt;list views&lt;/em&gt; of a few common types of components repeating over and over again. Think about your Facebook or Twitter feed, your Google or Amazon search results, a lengthy blog (like this one?) or a Wikipedia article you’re reading, or pretty much every other popular website that you use. There is a lot of scrolling involved!&lt;/p&gt;
&lt;p&gt;This demands the notion of &lt;a href=&quot;https://github.com/WICG/virtual-scroller&quot;&gt;Virtual Scrolling&lt;/a&gt;, and one of the possible prototypes is a new web platform primitive called &lt;a href=&quot;https://github.com/WICG/display-locking&quot;&gt;Display Locking&lt;/a&gt;, which is a set of API changes that aim to make it straightforward for developers and browsers to easily scale to a large amount of content and control when rendering work happens.&lt;/p&gt;
&lt;figure&gt;
	&lt;img src=&quot;/static/media/display-locking-1.jpg&quot; alt=&quot;Display Locking&quot;&gt;
    &lt;figcaption&gt;
        Source: &lt;a href=&quot;https://www.youtube.com/watch?v=ZFvPLrKZywA&amp;list=PLNYkxOF6rcIDA1uGhqy45bqlul0VcvKMr&amp;index=5&quot;&gt;HTML isn’t done!&lt;/a&gt;
    &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;To quote from Nicole Sullivan from the &lt;a href=&quot;https://youtu.be/ZFvPLrKZywA?list=PLNYkxOF6rcIDA1uGhqy45bqlul0VcvKMr&amp;#x26;t=1100&quot;&gt;session&lt;/a&gt; on this, Display Locking allows you to do batch rendering updates to avoid paying the performance costs when handling large amounts of DOM; it also means that locked sub-trees are not rendered immediately, which means you can keep more stuff in the DOM; and that’s great because it becomes searchable both by Find in Page as well as by assistive technologies.&lt;/p&gt;
&lt;h1 id=&quot;Safety-and-Privacy&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#Safety-and-Privacy&quot; aria-label=&quot;Safety and Privacy permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Safety and Privacy&lt;/h1&gt;
&lt;h2 id=&quot;Visual-updates-to-URL-display&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#Visual-updates-to-URL-display&quot; aria-label=&quot;Visual updates to URL display permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Visual updates to URL display&lt;/h2&gt;
&lt;figure  class=&quot;float-left&quot; style=&quot;width: 300px&quot;&gt;
	&lt;img src=&quot;/static/media/privacy-url-1.jpg&quot; alt=&quot;URL security&quot;&gt;
    &lt;figcaption&gt;
        Source: &lt;a href=&quot;https://youtu.be/WnCKlNE52tc?list=PLNYkxOF6rcIDA1uGhqy45bqlul0VcvKMr&amp;t=146&quot;&gt;Protecting users on a thriving web&lt;/a&gt;
    &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Let’s talk about important stuff. In the dimension of safety and privacy, some notable callouts include Chrome’s experiments around the general web consumer’s &lt;a href=&quot;https://ai.google/research/pubs/pub48199&quot;&gt;perception of security models&lt;/a&gt; related to how URL information is displayed by the browser.&lt;/p&gt;
&lt;p&gt;Chrome’s past attempts on using &lt;a href=&quot;https://en.wikipedia.org/wiki/Extended_Validation_Certificate&quot;&gt;extended validation indicator&lt;/a&gt; which informs the legal entity tied to a domain name did not turn out to be successful, simply because people don’t notice when it’s missing.&lt;/p&gt;
&lt;figure class=&quot;float-right&quot; style=&quot;width: 300px&quot;&gt;
	&lt;img src=&quot;/static/media/privacy-url-2.jpg&quot; alt=&quot;URL security&quot;&gt;
    &lt;figcaption&gt;
        Source: &lt;a href=&quot;https://youtu.be/WnCKlNE52tc?list=PLNYkxOF6rcIDA1uGhqy45bqlul0VcvKMr&amp;t=637&quot;&gt;Protecting users on a thriving web&lt;/a&gt;
    &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;This also calls out studies where the average web user has been found out to not necessarily clearly understand visual cues like &lt;a href=&quot;https://publications.sba-research.org/publications/2019-Pfeffer-HTTPS_Mental_Models.pdf&quot;&gt;what the HTTPS padlock icon on the URL bar means&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;All of this comes with a degree of potential controversy though, where Chrome as a browser will start hiding complete URLs and show origin information that it feels is the most relevant for the average web user. This is probably not going to make more experienced users very happy. In its defence, Chrome announced the &lt;a href=&quot;https://chrome.google.com/webstore/detail/suspicious-site-reporter/jknemblkbdhdcpllfgbfekkdciegfboi&quot;&gt;Suspicious Site Reporter&lt;/a&gt; extension that will allow the power user to see the full unedited URL, as well as report malicious sites to Google’s Safe Browsing service. While the intentions are great, this still feels somewhat stop-gap and a little unfulfilling to me. Simply because mobile has been always so hugely important and non-desktop versions of Chrome have so far, to the best of my knowledge, have had literally zero history of supporting extensions, this sounds like a hasty half-measure and an uncanny oversight. Wouldn’t something as simple as a browser setting have made things easier? Perhaps there are motivations here that I don’t fully understand.&lt;/p&gt;
&lt;h2 id=&quot;Combating-sophisticated-spoofing-attacks&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#Combating-sophisticated-spoofing-attacks&quot; aria-label=&quot;Combating sophisticated spoofing attacks permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Combating sophisticated spoofing attacks&lt;/h2&gt;
&lt;p&gt;Sophisticated spoofing attacks like &lt;a href=&quot;https://en.wikipedia.org/wiki/IDN_homograph_attack&quot;&gt;IDN spoofing&lt;/a&gt; is extremely difficult for the average web user to detect, and for that matter, often likely to easily defeat the technically experienced user as well unless they are specifically looking for it.&lt;/p&gt;
&lt;figure&gt;
	&lt;img src=&quot;/static/media/idn-attack-1.png&quot; alt=&quot;IDN spoofing&quot;&gt;
    &lt;figcaption&gt;
        Source: &lt;a href=&quot;https://youtu.be/WnCKlNE52tc?list=PLNYkxOF6rcIDA1uGhqy45bqlul0VcvKMr&amp;t=274&quot;&gt;Protecting users on a thriving web&lt;/a&gt;
    &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Chrome is introducing what it calls &lt;a href=&quot;https://www.zdnet.com/article/google-chrome-to-get-warnings-for-lookalike-urls/&quot;&gt;lookalike warnings&lt;/a&gt; to inform the user of potential attacks and then try to redirect them to the possibly intended website instead.&lt;/p&gt;
&lt;h2 id=&quot;Committed-to-privacy-but-with-ads-🤷♀️&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#Committed-to-privacy-but-with-ads-%F0%9F%A4%B7%E2%99%80%EF%B8%8F&quot; aria-label=&quot;Committed to privacy but with ads 🤷♀️ permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Committed to privacy, but with ads? 🤷‍♀️&lt;/h2&gt;
&lt;p&gt;Following a brief stint on &lt;a href=&quot;https://youtu.be/WnCKlNE52tc?list=PLNYkxOF6rcIDA1uGhqy45bqlul0VcvKMr&amp;#x26;t=1175&quot;&gt;why we should all look at ads as a necessary evil&lt;/a&gt;, it was announced that there are [sic] restrictions coming to third-party cookies in Chrome. New cookie classification spec that’s landing enforces &lt;code class=&quot;language-text&quot;&gt;SameSite=None&lt;/code&gt; to be explicitly set to designate cookies intended for cross-site access, without which they will be by default accessible only in first-party contexts. Coupled with the &lt;code class=&quot;language-text&quot;&gt;Secure&lt;/code&gt; attribute, this makes cookies only accessible over HTTPS connections. Read more about it on &lt;a href=&quot;https://blog.chromium.org/2019/10/developers-get-ready-for-new.html&quot;&gt;blog.chromium.com&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In a world where other more privacy-focused browsers like Firefox or Safari have been blocking third-party cookies by default and have shown &lt;a href=&quot;https://www.zdnet.com/article/firefox-to-add-tor-browser-anti-fingerprinting-technique-called-letterboxing/&quot;&gt;committed&lt;/a&gt; &lt;a href=&quot;https://gizmodo.com/apple-declares-war-on-browser-fingerprinting-the-sneak-1826549108&quot;&gt;interest&lt;/a&gt; in fighting against fingerprinting, all this seems arguably feeble. Then again, &lt;a href=&quot;https://www.eff.org/deeplinks/2019/08/dont-play-googles-privacy-sandbox-1&quot;&gt;not that&lt;/a&gt; it has had a great track record.&lt;/p&gt;
&lt;h1 id=&quot;Chrome-Extensions&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#Chrome-Extensions&quot; aria-label=&quot;Chrome Extensions permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Chrome Extensions&lt;/h1&gt;
&lt;h2 id=&quot;making-progress-towards-the-way-they-should-have-ideally-always-been&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#making-progress-towards-the-way-they-should-have-ideally-always-been&quot; aria-label=&quot;making progress towards the way they should have ideally always been permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;…making progress towards the way they should have ideally always been.&lt;/h2&gt;
&lt;p&gt;Themes of privacy and safety continued into the presentation of the newer mechanisms for building Chrome extensions that are being designed to be more protective about security and access control.&lt;/p&gt;
&lt;p&gt;The introduction of Extension &lt;a href=&quot;https://developer.chrome.com/extensions/migrating_to_manifest_v3&quot;&gt;Manifest V3&lt;/a&gt; along with other overhauls to the extension ecosystem architecture would hopefully deprecate currently existing permission models and background code executions which traditionally have been overtly open and also known to cause performance bottlenecks. Stricter models for both permissions and execution limits through &lt;a href=&quot;https://developer.chrome.com/extensions/migrating_to_service_workers&quot;&gt;background service workers&lt;/a&gt; are being introduced.&lt;/p&gt;
&lt;p&gt;A notable mention here was the addition of the &lt;a href=&quot;https://blog.chromium.org/2019/06/web-request-and-declarative-net-request.html&quot;&gt;declarative net request&lt;/a&gt; model that will drastically change the behaviour of request interception and blocking as done today by Chrome extensions (such as &lt;a href=&quot;https://chrome.google.com/webstore/detail/adblock-%E2%80%94-best-ad-blocker/gighmmpiobklfepjocnamgkkbiglidom&quot;&gt;AdBlock&lt;/a&gt;) and provide more restrictive and performant models to achieve the same.&lt;/p&gt;
&lt;figure&gt;
	&lt;img src=&quot;/static/media/chrome-ext-1.jpg&quot; alt=&quot;Chrome Extensions: declarative net request&quot;&gt;
    &lt;figcaption&gt;
        Source: &lt;a href=&quot;https://youtu.be/7-ALJiZCI6w?list=PLNYkxOF6rcIDA1uGhqy45bqlul0VcvKMr&amp;t=1630&quot;&gt;Chrome extensions and the world of tomorrow&lt;/a&gt;
    &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h1 id=&quot;SEO&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#SEO&quot; aria-label=&quot;SEO permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;SEO&lt;/h1&gt;
&lt;p&gt;Google &lt;a href=&quot;https://www.youtube.com/watch?v=4pOH8Smd0Xs&amp;#x26;list=PLNYkxOF6rcIDA1uGhqy45bqlul0VcvKMr&amp;#x26;index=6&quot;&gt;announced&lt;/a&gt; that the Googlebot crawler user-agent would be updated soon to reflect the &lt;a href=&quot;https://webmasters.googleblog.com/2019/05/the-new-evergreen-googlebot.html&quot;&gt;new evergreen Googlebot&lt;/a&gt;. A much-needed improvement over the previous rendering engine used by Googlebot that was based on Chrome 41 and had been probably facing a lot of difficulties keeping up with new Javascript heavy websites of today.&lt;/p&gt;
&lt;figure&gt;
	&lt;img src=&quot;/static/media/googlebot-1.png&quot; alt=&quot;Googlebot user-agent&quot;&gt;
    &lt;figcaption&gt;
        Source: &lt;a href=&quot;https://www.youtube.com/watch?v=4pOH8Smd0Xs&amp;list=PLNYkxOF6rcIDA1uGhqy45bqlul0VcvKMr&amp;index=6&quot;&gt;How to make your content shine on Google Search&lt;/a&gt;
    &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;What this basically means is that with every release of Chrome, the Googlebot rendering engine will also keep up and get updated. Consequentially, this also gets coupled with the update of the Googlebot user-agent string which will now not always continue to match with an exact version of Chrome anymore, as used to be the case before.&lt;/p&gt;
&lt;p&gt;Benefits include better support for modern ways of building websites using such as with Javascript rendered pages, lazy-loading and the support for &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/Web_Components&quot;&gt;web components&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Also, going ahead, images set as &lt;code class=&quot;language-text&quot;&gt;background-image&lt;/code&gt; URLs may not be indexed.&lt;/p&gt;
&lt;h1 id=&quot;Other-stuff-to-call-out&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#Other-stuff-to-call-out&quot; aria-label=&quot;Other stuff to call out permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Other stuff to call out…&lt;/h1&gt;
&lt;h2 id=&quot;The-Blink-shipping-process&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#The-Blink-shipping-process&quot; aria-label=&quot;The Blink shipping process permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;The Blink shipping process&lt;/h2&gt;
&lt;p&gt;I wouldn’t really bother trying to explain this considering there’s a great &lt;a href=&quot;https://www.youtube.com/watch?v=y3EZx_b-7tk&amp;#x26;list=PLNYkxOF6rcIDA1uGhqy45bqlul0VcvKMr&amp;#x26;index=17&quot;&gt;video&lt;/a&gt; and &lt;a href=&quot;https://blog.chromium.org/2019/11/intent-to-explain-demystifying-blink.html&quot;&gt;write-up&lt;/a&gt; that already does a much better job at it. If you are interested in learning how the web platform works in terms of new feature proposals, addition and review of web standards and how features are shipped to &lt;a href=&quot;http://www.chromium.org/blink&quot;&gt;Blink&lt;/a&gt; (Chromium’s rendering engine) and how it complements the web standards process, check them out.&lt;/p&gt;
&lt;h2 id=&quot;Origin-Trials&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#Origin-Trials&quot; aria-label=&quot;Origin Trials permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Origin Trials&lt;/h2&gt;
&lt;p&gt;This isn’t really news, but to the uninitiated, “Origin trials” are Chrome’s way to quickly experiment and gather feedback on new and potentially upcoming web features by providing some partner websites early access to try out early implementations and give feedback on usability, practicality, and effectiveness that eventually goes on its way to the web standards community.&lt;/p&gt;
&lt;p&gt;APIs exposed through Origin Trials are unstable by intent (prone to changes) and at one point before the end of the trials, go out of availability briefly. They also have inbuilt safeguarding mechanisms to get disabled if 0.5% of Chrome’s page loads start accessing the API, to prevent such experimental APIs from being exposed or baked too much into the open web before they are done right and become standards. For more information, see the &lt;a href=&quot;https://github.com/GoogleChrome/OriginTrials/blob/gh-pages/explainer.md&quot;&gt;explainer&lt;/a&gt;, &lt;a href=&quot;https://github.com/GoogleChrome/OriginTrials/blob/gh-pages/developer-guide.md&quot;&gt;developer guide&lt;/a&gt; or &lt;a href=&quot;https://www.chromium.org/blink/origin-trials/running-an-origin-trial&quot;&gt;blink documentation&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If you are interested in playing around with a hot new API that’s landing on Chrome but far from general availability, this is your way to go!&lt;/p&gt;
&lt;h2 id=&quot;Context-Indexing-API&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#Context-Indexing-API&quot; aria-label=&quot;Context Indexing API permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Context Indexing API&lt;/h2&gt;
&lt;p&gt;Here comes that one-off API that I didn’t really quite know where to put. The idea is to enable indexing of offline content that you can present to a user, but to be able to effectively do that, request the browser to index them such that they can be surfaced in prominent places like Chrome’s landing page or other places, collectively known as “Discovery” experience. Check out the &lt;a href=&quot;https://github.com/rayankans/content-index&quot;&gt;proposal&lt;/a&gt; to learn more.&lt;/p&gt;
&lt;br&gt;
&lt;p&gt;&lt;em&gt;That’s all folks!&lt;/em&gt;&lt;/p&gt;
&lt;br&gt;</content:encoded></item><item><title><![CDATA[Creating a multi-level hierarchical flyout navigation menu using only HTML and CSS]]></title><link>https://www.ghosh.dev/posts/creating-a-multi-level-hierarchical-flyout-navigation-menu-using-only-html-and-css/</link><guid isPermaLink="false">https://www.ghosh.dev/posts/creating-a-multi-level-hierarchical-flyout-navigation-menu-using-only-html-and-css/</guid><pubDate>Sun, 07 Jul 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Today I am going to give you a quick tutorial on how to create a hierarchical navigation flyout menu that can go nested deep down across multiple levels.&lt;/p&gt;
&lt;p&gt;As an inspiration, we’ll start off with a concrete practical use-case of an example menu bar for a desktop application. I’ll pick a subset of the Chrome browser’s menu bar to illustrate this.&lt;/p&gt;
&lt;p&gt;We’ll begin with a simple quite look-and-feel, something that goes back to the classic Windows™ theme. Here’s a short video on how that would look like:&lt;/p&gt;
&lt;!--
![Css nav menu](/static/media/css-nav-menu-3.gif) --&gt;
&lt;figure&gt;
    &lt;video controls loop muted style=&quot;width: 100%&quot;&gt;
        &lt;source src=&quot;/static/media/css-nav-menu-3.mp4&quot; type=&quot;video/mp4&quot;&gt;
    &lt;/video&gt;
&lt;/figure&gt;
&lt;p&gt;Towards the end, we’ll make it a bit fancier by adding some more styling to give it a MacOS™ like feel.&lt;/p&gt;
&lt;h3 id=&quot;The-Basics&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#The-Basics&quot; aria-label=&quot;The Basics permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;The Basics&lt;/h3&gt;
&lt;p&gt;Let’s start off by understanding what our menu items would typically constitute of. They should have the following properties:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Label&lt;/strong&gt;: (&lt;em&gt;required&lt;/em&gt;) which is basically the name of the menu item that is displayed&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Target&lt;/strong&gt;: (&lt;em&gt;optional&lt;/em&gt;) a hyperlink that takes the user to a page as a response to clicking on the menu item. We’ll stick to just links right now. Adding more dynamic in-page features would require JavaScript which we’ll stay away from at the moment. It’s something you can always go and easily add later.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Shortcut&lt;/strong&gt;: (&lt;em&gt;optional&lt;/em&gt;) in our case, displays a keyboard shortcut that could be used for this menu item. For example, “File &gt; New” would be “Cmd + N” (⌘N) on Mac.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Children&lt;/strong&gt;: (&lt;em&gt;optional&lt;/em&gt;) which refers to the sub-menu for this menu item. Think of our menus and sub-menus in the form of a &lt;strong&gt;recursive structure&lt;/strong&gt;. Visually, a menu item having a sub-menu should also have an arrow icon on it (▶) to indicate that it can expand when hovered.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Disabled&lt;/strong&gt;: (optiona), a state indicating if the menu item can be interacted with.&lt;/li&gt;
&lt;li&gt;A conceptual &lt;strong&gt;Type&lt;/strong&gt; parameter? (&lt;em&gt;optional&lt;/em&gt;) that could emulate different types of menu items with this. Say, some entries in the list of menus should act as just a &lt;strong&gt;separator&lt;/strong&gt; line.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Note that we could go ahead and add more complex behaviours to our menus. For example, a certain menu could be a &lt;strong&gt;Toggle&lt;/strong&gt; item, and so, would need to have some form of a tick mark (✔) or checkbox associated with it to indicate its on/off state.&lt;/p&gt;
&lt;p&gt;We’ll use &lt;strong&gt;CSS classes&lt;/strong&gt; on our HTML markup to indicate such properties and write some clever styling to impart all the corresponding behaviours.&lt;/p&gt;
&lt;h3 id=&quot;Structuring-the-HTML&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#Structuring-the-HTML&quot; aria-label=&quot;Structuring the HTML permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Structuring the HTML&lt;/h3&gt;
&lt;p&gt;Based on the above, this is how our basic menu HTML should look like:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;A list of menus is defined by an HTML &lt;code class=&quot;language-text&quot;&gt;ul&lt;/code&gt; element, with individual items being the obvious &lt;code class=&quot;language-text&quot;&gt;li&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;label&lt;/strong&gt; and &lt;strong&gt;shortcut&lt;/strong&gt; will be placed as &lt;code class=&quot;language-text&quot;&gt;span&lt;/code&gt; elements with their corresponding CSS classes (&lt;code class=&quot;language-text&quot;&gt;label&lt;/code&gt; or &lt;code class=&quot;language-text&quot;&gt;shortcut&lt;/code&gt;) inside an anchor (&lt;code class=&quot;language-text&quot;&gt;a&lt;/code&gt;) tag inside the &lt;code class=&quot;language-text&quot;&gt;li&lt;/code&gt;, so that clicking on it causes the navigation action, as well as be able to provide some UI feedback such as highlighting the menu item on &lt;strong&gt;hover&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;When a menu item contains a list of &lt;strong&gt;sub-menu&lt;/strong&gt; (children), we’ll put that sub-menu in another &lt;code class=&quot;language-text&quot;&gt;ul&lt;/code&gt; element inside the current menu &lt;code class=&quot;language-text&quot;&gt;li&lt;/code&gt; element (parent) and so on. To describe that this particular menu item contains a sub-menu and also be able to add some specific styling to make it functional (as well as visual elements like the ▶ indicator), we’ll add the &lt;code class=&quot;language-text&quot;&gt;has-children&lt;/code&gt; CSS class to this parent &lt;code class=&quot;language-text&quot;&gt;li&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;For items like the &lt;strong&gt;separator&lt;/strong&gt;, we’ll add a corresponding CSS class called &lt;code class=&quot;language-text&quot;&gt;separator&lt;/code&gt; to the &lt;code class=&quot;language-text&quot;&gt;li&lt;/code&gt; item denoting it.&lt;/li&gt;
&lt;li&gt;A menu item can be &lt;strong&gt;disabled&lt;/strong&gt;, in which case we’ll add the corresponding &lt;code class=&quot;language-text&quot;&gt;disabled&lt;/code&gt; CSS class. It’s job is to make this item non-responsive to pointer events like hover or clicks.&lt;/li&gt;
&lt;li&gt;We’ll wrap everything off inside a container HTML &lt;code class=&quot;language-text&quot;&gt;nav&lt;/code&gt; element (it’s good to be &lt;a href=&quot;https://en.wikipedia.org/wiki/Semantic_HTML&quot;&gt;semantic&lt;/a&gt;) and add the &lt;code class=&quot;language-text&quot;&gt;flyout-nav&lt;/code&gt; class to it for some basic namespacing of the CSS styles that we’ll add.&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;nav&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;flyout-nav&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;ul&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;li&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
            &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;a&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;#&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;span&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;label&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;File&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;span&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
            &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;ul&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
                &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;li&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
                    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;a&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;#&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
                        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;span&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;label&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;New Tab&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;span&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
                        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;span&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;shortcut&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;⌘T&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;span&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
                    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
                &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;li&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
                &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;li&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
                    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;a&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;#&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
                        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;span&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;label&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;New Window&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;span&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
                        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;span&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;shortcut&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;⌘N&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;span&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
                    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
                &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;li&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
                &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;li&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;separator&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;li&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
                &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;li&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;has-children&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
                    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;a&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;#&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
                        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;span&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;label&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Share...&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;span&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
                    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
                    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;ul&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
                        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;li&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
                            &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;a&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;#&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
                                &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;span&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;label&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;✉️ Email&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;span&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
                            &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
                        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;li&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
                        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;li&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
                            &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;a&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;#&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
                                &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;span&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;label&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;💬 Messages&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;span&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
                            &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
                        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;li&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
                    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;ul&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
                &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;li&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
            &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;ul&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;li&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;ul&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;nav&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;Adding-behaviours-in-CSS&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#Adding-behaviours-in-CSS&quot; aria-label=&quot;Adding behaviours in CSS permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Adding behaviours in CSS&lt;/h3&gt;
&lt;p&gt;I lied. We’ll use &lt;a href=&quot;https://sass-lang.com/&quot;&gt;SCSS&lt;/a&gt; instead.&lt;/p&gt;
&lt;p&gt;Jokes aside, here comes the interesting part!&lt;/p&gt;
&lt;p&gt;The menu (except the first-level “horizontal bar”), should be &lt;em&gt;hidden&lt;/em&gt; by default.&lt;/p&gt;
&lt;p&gt;Anything below the first level should only be displayed when the corresponding menu item is hovered upon using the mouse pointer. As you may have already guessed, we’ll heavily rely on the CSS &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/:hover&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;hover&lt;/code&gt; pseudo-class&lt;/a&gt; for this.&lt;/p&gt;
&lt;h5 id=&quot;Arranging-menu-and-sub-menu-elements&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#Arranging-menu-and-sub-menu-elements&quot; aria-label=&quot;Arranging menu and sub menu elements permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Arranging menu and sub-menu elements&lt;/h5&gt;
&lt;p&gt;Perhaps the trickiest bit in this whole puzzle is to understand how we make the sub-menu position and align itself with respect to their parent menu item correctly. This is where some knowledge of CSS &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/position&quot;&gt;positioning&lt;/a&gt; comes in. Let’s look at that.&lt;/p&gt;
&lt;p&gt;There was a reason why we chose to put the sub-menu &lt;code class=&quot;language-text&quot;&gt;ul&lt;/code&gt; element inside a “parent” &lt;code class=&quot;language-text&quot;&gt;li&lt;/code&gt; element. Of course, it helps us to logically appropriately put together the markup for our hierarchical content, but it also serves another purpose of allowing us to easily write some CSS to position a child element &lt;em&gt;relative&lt;/em&gt; to the position of a parent element. Then we take this concept all the way to the root &lt;code class=&quot;language-text&quot;&gt;ul&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;li&lt;/code&gt; elements.&lt;/p&gt;
&lt;p&gt;For doing this, we’ll use a combination of &lt;code class=&quot;language-text&quot;&gt;absolute&lt;/code&gt; positioning and &lt;code class=&quot;language-text&quot;&gt;top&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;left&lt;/code&gt; CSS properties that will help us to position a child element relative to its &lt;em&gt;closest non-static positioned ancestor&lt;/em&gt; defining the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/Containing_block&quot;&gt;containing block&lt;/a&gt;. By non-static, we mean that the CSS position property for an element is not &lt;code class=&quot;language-text&quot;&gt;static&lt;/code&gt; (which happens by default in the HTML document flow), but instead is one of &lt;code class=&quot;language-text&quot;&gt;relative&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;absolute&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;fixed&lt;/code&gt; or &lt;code class=&quot;language-text&quot;&gt;sticky&lt;/code&gt;. To make sure of that, we’ll assign the position &lt;code class=&quot;language-text&quot;&gt;relative&lt;/code&gt; to the &lt;code class=&quot;language-text&quot;&gt;li&lt;/code&gt; elements with their child &lt;code class=&quot;language-text&quot;&gt;ul&lt;/code&gt; elements positioned &lt;code class=&quot;language-text&quot;&gt;absolute&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;scss&quot;&gt;&lt;pre class=&quot;language-scss&quot;&gt;&lt;code class=&quot;language-scss&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.flyout-nav &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// list of menu items at any level&lt;/span&gt;
    &lt;span class=&quot;token selector&quot;&gt;ul &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;margin&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;position&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; absolute&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; none&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;list-style-type&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; none&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// a menu item&lt;/span&gt;
    &lt;span class=&quot;token selector&quot;&gt;li &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;position&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; relative&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; block&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token comment&quot;&gt;// show the next level drop-down on&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// the right at the same height&lt;/span&gt;
        &lt;span class=&quot;token selector&quot;&gt;&lt;span class=&quot;token parent important&quot;&gt;&amp;amp;&lt;/span&gt;:hover &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token selector&quot;&gt;&lt;span class=&quot;token parent important&quot;&gt;&amp;amp;&lt;/span&gt; &gt; ul &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token property&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; block&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;token property&quot;&gt;top&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;token property&quot;&gt;left&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 100%&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The effect of this is shown in the image below, highlighted in the red box for illustration. Some additional CSS for visual styling has been done in the image to make it look all nice, but the core behaviour is defined by what we have above. This keeps working great to N-levels deep (within limits of practicality).&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/static/media/css-nav-menu-4.jpg&quot; alt=&quot;Sub-menu positioning&quot;&gt;&lt;/p&gt;
&lt;p&gt;There’s one exception to this though, which is the first-level list of menu items (File, Edit, View… in our example), whose children menu items need to be positioned &lt;em&gt;below&lt;/em&gt; instead of right. To handle that, we add some style overrides to our previous CSS.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;scss&quot;&gt;&lt;pre class=&quot;language-scss&quot;&gt;&lt;code class=&quot;language-scss&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.flyout-nav &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// ... other stuff&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// overrides for first-level behaviour (horizontal bar)&lt;/span&gt;
    &lt;span class=&quot;token selector&quot;&gt;&lt;span class=&quot;token parent important&quot;&gt;&amp;amp;&lt;/span&gt; &gt; ul &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; flex&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;flex-flow&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; row nowrap&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;justify-content&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; flex-start&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;align-items&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; stretch&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token comment&quot;&gt;// first-level drop-down should appear&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// below at the same left position&lt;/span&gt;
        &lt;span class=&quot;token selector&quot;&gt;&lt;span class=&quot;token parent important&quot;&gt;&amp;amp;&lt;/span&gt; &gt; li:hover &gt; ul &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token property&quot;&gt;top&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 100%&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token property&quot;&gt;left&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Note that using a flex-box here was not imperative, rather just something I did out of choice. You could achieve similar behaviour using other approaches such as a combination of &lt;code class=&quot;language-text&quot;&gt;display: block&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;display: inline-block&lt;/code&gt; on the &lt;code class=&quot;language-text&quot;&gt;ul&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;li&lt;/code&gt; items as well.&lt;/p&gt;
&lt;h5 id=&quot;UI-polishing&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#UI-polishing&quot; aria-label=&quot;UI polishing permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;UI polishing&lt;/h5&gt;
&lt;p&gt;Once we’re done handling the basics of positioning the menu items, we’ll go on about writing some additional styles such as fonts, sizes, colours, backgrounds, shadow and such for making the UI feel all nice and better.&lt;/p&gt;
&lt;p&gt;For consistency and reuse, let’s also assume that we have such values defined and shared using a bunch of SCSS variables. Something like…&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;scss&quot;&gt;&lt;pre class=&quot;language-scss&quot;&gt;&lt;code class=&quot;language-scss&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// variables&lt;/span&gt;
&lt;span class=&quot;token property&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$page-bg&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; #607d8b&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token property&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$base-font-size&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 16px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// becomes 1rem&lt;/span&gt;
&lt;span class=&quot;token property&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$menu-silver&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; #eee&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token property&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$menu-border&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; #dedede&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token property&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$menu-focused&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; #1e88e5&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token property&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$menu-separator&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; #ccc&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token property&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$menu-text-color&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; #333&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token property&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$menu-shortcut-color&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; #999&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token property&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$menu-focused-text-color&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; #fff&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token property&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$menu-text-color-disabled&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; #999&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token property&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$menu-border-width&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 1px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token property&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$menu-shadow&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 2px 2px 3px -3px &lt;span class=&quot;token variable&quot;&gt;$menu-text-color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token property&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$menu-content-padding&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0.5rem 1rem 0.5rem 1.75rem&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token property&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$menu-border-radius&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0.5rem&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token property&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$menu-top-padding&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0.25rem&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;There are some pieces that we’re left adding the appropriate styles and behaviours for. We’ll go over them quickly now.&lt;/p&gt;
&lt;h5 id=&quot;Anchors-Labels-and-Shortcuts---the-actual-visual-elements&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#Anchors-Labels-and-Shortcuts---the-actual-visual-elements&quot; aria-label=&quot;Anchors Labels and Shortcuts   the actual visual elements permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Anchors, Labels and Shortcuts - the actual visual elements&lt;/h5&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;scss&quot;&gt;&lt;pre class=&quot;language-scss&quot;&gt;&lt;code class=&quot;language-scss&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.flyout-nav &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// ... other stuff&lt;/span&gt;

    &lt;span class=&quot;token selector&quot;&gt;li &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// ... other stuff&lt;/span&gt;

        &lt;span class=&quot;token comment&quot;&gt;// the menu items - text, shortcut info and hover effect (blue bg)&lt;/span&gt;
        &lt;span class=&quot;token selector&quot;&gt;a &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token property&quot;&gt;text-decoration&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; none&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token property&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$menu-text-color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token property&quot;&gt;position&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; relative&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token property&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; table&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token property&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 100%&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

            &lt;span class=&quot;token selector&quot;&gt;.label,
            .shortcut &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token property&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; table-cell&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;token property&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$menu-content-padding&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

            &lt;span class=&quot;token selector&quot;&gt;.shortcut &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token property&quot;&gt;text-align&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; right&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;token property&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$menu-shortcut-color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

            &lt;span class=&quot;token selector&quot;&gt;label &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token property&quot;&gt;cursor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; pointer&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

            &lt;span class=&quot;token comment&quot;&gt;// for menu items that are toggles&lt;/span&gt;
            &lt;span class=&quot;token selector&quot;&gt;input[type=&apos;checkbox&apos;] &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token property&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; none&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

            &lt;span class=&quot;token selector&quot;&gt;input[type=&apos;checkbox&apos;]:checked + .label &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token selector&quot;&gt;&lt;span class=&quot;token parent important&quot;&gt;&amp;amp;&lt;/span&gt;::before &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;token property&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;✔️&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
                    &lt;span class=&quot;token property&quot;&gt;position&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; absolute&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
                    &lt;span class=&quot;token property&quot;&gt;top&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
                    &lt;span class=&quot;token property&quot;&gt;left&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0.25rem&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
                    &lt;span class=&quot;token property&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0.25rem&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

            &lt;span class=&quot;token selector&quot;&gt;&lt;span class=&quot;token parent important&quot;&gt;&amp;amp;&lt;/span&gt;:hover &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token property&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$menu-focused&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;token selector&quot;&gt;.label,
                .shortcut &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;token property&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$menu-focused-text-color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Most of this code is pretty self-explanatory. However, did you notice anything interesting? The bit about &lt;code class=&quot;language-text&quot;&gt;input[type=&amp;#39;checkbox&amp;#39;]&lt;/code&gt;?&lt;/p&gt;
&lt;h5 id=&quot;Toggle-Items&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#Toggle-Items&quot; aria-label=&quot;Toggle Items permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Toggle Items&lt;/h5&gt;
&lt;p&gt;For toggles, we use a hidden HTML &lt;code class=&quot;language-text&quot;&gt;checkbox&lt;/code&gt; element to maintain state (on or off) and style the &lt;code class=&quot;language-text&quot;&gt;label&lt;/code&gt; with &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/::before&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;::before&lt;/code&gt; pseudo-element&lt;/a&gt; accordingly. We are able to do that using a simple CSS &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/Adjacent_sibling_combinator&quot;&gt;adjacent sibling selector&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The corresponding HTML markup for that menu item would look something like this:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;li&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;a&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;#&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;input&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;checkbox&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;alwaysShowBookmarksBar&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;checked&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;true&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;label&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;label&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;alwaysShowBookmarksBar&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Always Show Bookmarks Bar&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;label&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;span&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;shortcut&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;⇧⌘B&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;span&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;li&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h5 id=&quot;Separators&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#Separators&quot; aria-label=&quot;Separators permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Separators&lt;/h5&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;scss&quot;&gt;&lt;pre class=&quot;language-scss&quot;&gt;&lt;code class=&quot;language-scss&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.flyout-nav &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// ... other stuff&lt;/span&gt;

    &lt;span class=&quot;token selector&quot;&gt;li &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// ... other stuff&lt;/span&gt;

        &lt;span class=&quot;token comment&quot;&gt;// the separator item&lt;/span&gt;
        &lt;span class=&quot;token selector&quot;&gt;&lt;span class=&quot;token parent important&quot;&gt;&amp;amp;&lt;/span&gt;.separator &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token property&quot;&gt;margin-bottom&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$menu-top-padding&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token property&quot;&gt;border-bottom&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$menu-border-width&lt;/span&gt; solid &lt;span class=&quot;token variable&quot;&gt;$menu-separator&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token property&quot;&gt;padding-bottom&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$menu-top-padding&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h5 id=&quot;Disabled&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#Disabled&quot; aria-label=&quot;Disabled permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Disabled&lt;/h5&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;scss&quot;&gt;&lt;pre class=&quot;language-scss&quot;&gt;&lt;code class=&quot;language-scss&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.flyout-nav &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// ... other stuff&lt;/span&gt;

    &lt;span class=&quot;token selector&quot;&gt;li &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// ... other stuff&lt;/span&gt;

        &lt;span class=&quot;token comment&quot;&gt;// don&apos;t let disabled options respond to hover&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// or click and color them different&lt;/span&gt;
        &lt;span class=&quot;token selector&quot;&gt;&lt;span class=&quot;token parent important&quot;&gt;&amp;amp;&lt;/span&gt;.disabled &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token selector&quot;&gt;.label,
            .shortcut &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token property&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$menu-text-color-disabled&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;token property&quot;&gt;pointer-events&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; none&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;CSS &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/pointer-events&quot;&gt;pointer-events&lt;/a&gt; does the actual trick here. Setting it to &lt;code class=&quot;language-text&quot;&gt;none&lt;/code&gt; makes it invisible as a target for any pointer events.&lt;/p&gt;
&lt;h3 id=&quot;Putting-it-all-together&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#Putting-it-all-together&quot; aria-label=&quot;Putting it all together permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Putting it all together…&lt;/h3&gt;
&lt;p&gt;Now that we’ve gained some understanding of the building blocks, let’s put it all together. Here’s a Codepen link to our multi-level hierarchical flyout navigation menu in action!&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://codepen.io/abhishekcghosh/pen/WqjOaX&quot;&gt;&lt;div&gt;&lt;iframe 
        height=&apos;400&apos; 
        scrolling=&apos;no&apos; 
        src=&apos;//codepen.io/abhishekcghosh/embed/preview/WqjOaX/?height=400&amp;theme-id=dark&amp;default-tab=html,result&apos; 
        frameborder=&apos;no&apos; 
        allowtransparency=&apos;true&apos; 
        allowfullscreen=&apos;true&apos; 
        style=&apos;width: 100%;&apos;&gt;&lt;/iframe&gt;&lt;/div&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h5 id=&quot;Fancier-theming&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#Fancier-theming&quot; aria-label=&quot;Fancier theming permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Fancier theming&lt;/h5&gt;
&lt;p&gt;If you are not a fan of the retro Windows look, here’s another version of the same code with some minor tweaks to the CSS to make it look and feel more like MacOS.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://codepen.io/abhishekcghosh/pen/qzmEWd&quot;&gt;&lt;div&gt;&lt;iframe 
        height=&apos;400&apos; 
        scrolling=&apos;no&apos; 
        src=&apos;//codepen.io/abhishekcghosh/embed/preview/qzmEWd/?height=400&amp;theme-id=dark&amp;default-tab=html,result&apos; 
        frameborder=&apos;no&apos; 
        allowtransparency=&apos;true&apos; 
        allowfullscreen=&apos;true&apos; 
        style=&apos;width: 100%;&apos;&gt;&lt;/iframe&gt;&lt;/div&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;What-doesnt-work&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#What-doesnt-work&quot; aria-label=&quot;What doesnt work permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;What doesn’t work?&lt;/h3&gt;
&lt;p&gt;There are a few things we haven’t handled. For starters,&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;If you’re nitpicky about it, while most of the behaviour works great, a limitation of the deliberate CSS-only approach is that unlike the real-world Windows and MacOS application menus, our menu hides immediately as soon as the pointer goes outside. For more comfortable usage, typically what we’d want to do is wait for a click before hiding (can be always achieved with a bit of JS).&lt;/li&gt;
&lt;li&gt;What if the list of items in a menu is super long? Imagine a bookmarks list as an example. At some point, it might need to be capped into a scrollable view, say at some percentage of the viewport height. At the end of the day, it’s really a choice of the user experience you’re building, but something I wanted to put out there as well.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Hope this was useful. Cheers!&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Starting a blog with a developer mindset in 2019]]></title><link>https://www.ghosh.dev/posts/starting-a-blog-with-a-developer-mindset-in-2019/</link><guid isPermaLink="false">https://www.ghosh.dev/posts/starting-a-blog-with-a-developer-mindset-in-2019/</guid><pubDate>Tue, 18 Jun 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;In a &lt;a href=&quot;/posts/on-personal-blog-as-a-software-developer&quot;&gt;previous post&lt;/a&gt;, I talked about my motivation for starting this blog.&lt;/p&gt;
&lt;p&gt;But starting &lt;em&gt;and maintaining&lt;/em&gt; a blog is a lot of hard work. First, you have to decide what to name it, then figure out how to set it up and host it, what content management system would work for you along with their features and pricing, is there a theme to the blog - whether you’d prefer to stick to some particular subject or be more open - something that imparts a sense of “character” or “identity” to the blog. And all this before you’ve got the chance to put serious thought into how you’re actually going to keep posting regularly - creating the actual content for your blog - which may just turn out to be the most difficult part!&lt;/p&gt;
&lt;!-- ee --&gt;
&lt;p&gt;When I ventured out, I knew I wanted to focus on a “developer blog”, so at least I knew the theme I was going after. I thought that was a great starting point.&lt;/p&gt;
&lt;p&gt;Soon enough surely, hesitation kicked in.&lt;/p&gt;
&lt;p&gt;Should I go for some hosted platform solution, or have something I can call more of &lt;em&gt;my own&lt;/em&gt;? Do I deploy a ready out-of-the-box framework, or start building something from scratch? Or probably something &lt;em&gt;in between&lt;/em&gt;? What is a &lt;em&gt;trusted&lt;/em&gt; and &lt;em&gt;affordable&lt;/em&gt; service provider that I could use for hosting my content? How much should I even begin with as a &lt;em&gt;spending budget&lt;/em&gt; for this? Will I get locked-in on some service provider or technology choices for &lt;em&gt;storing&lt;/em&gt; my content? How difficult could it be to &lt;em&gt;move&lt;/em&gt; my content to somewhere else if I wanted to in the future? How will I ever even manage to get an actual &lt;em&gt;audience&lt;/em&gt; for this blog?&lt;/p&gt;
&lt;p&gt;I had all the liberty to feel giddy looking at the number of options available and the choices to make.&lt;/p&gt;
&lt;p&gt;Baby steps.&lt;/p&gt;
&lt;p&gt;Eventually, I decided to go down the path of setting up my own domain and creating my blog as a &lt;em&gt;static website&lt;/em&gt; generated using &lt;a href=&quot;https://www.gatsbyjs.org/&quot;&gt;Gatsby&lt;/a&gt; and hosted on &lt;a href=&quot;https://netlify.com&quot;&gt;Netlify&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In my &lt;a href=&quot;/posts/the-quintessential-hello-world&quot;&gt;first blog post&lt;/a&gt;, I did add a light-hearted remark around “all the cool kids using Gatsby these days”. And who doesn’t want to be cool, yeah? In fairness though, my decision to follow this path was more deeply deliberated than just getting on the hype train. So far, it seems to have been a good decision.&lt;/p&gt;
&lt;p&gt;Now I am not going to make this article a “Yet another post on how to set up your blog with Gatsby and Netlify”. There are a &lt;a href=&quot;https://daveceddia.com/start-blog-gatsby-netlify/&quot;&gt;great&lt;/a&gt; &lt;a href=&quot;https://codeburst.io/build-a-blog-using-gatsby-js-react-8561bfe8fc91&quot;&gt;many&lt;/a&gt; &lt;a href=&quot;https://www.netlify.com/blog/2016/02/24/a-step-by-step-guide-gatsby-on-netlify/&quot;&gt;resources&lt;/a&gt; &lt;a href=&quot;https://www.freecodecamp.org/news/how-i-built-my-blog-using-gatsby-and-netlify-f921f1a9f33c/&quot;&gt;all&lt;/a&gt; &lt;a href=&quot;https://blog.logrocket.com/gatsby-netlify-cms-a-perfect-pairing-d50d59d16f67/&quot;&gt;over&lt;/a&gt; &lt;a href=&quot;https://dev.to/saigowthamr/build-a-blog-using-gatsby-netlify-cms-adi&quot;&gt;the&lt;/a&gt; &lt;a href=&quot;https://egghead.io/courses/build-a-blog-with-react-and-markdown-using-gatsby&quot;&gt;web&lt;/a&gt; on that already!&lt;/p&gt;
&lt;p&gt;What I will focus on instead, is sharing my rationale towards &lt;em&gt;making that decision&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;I’m certain there are people out there embarking upon the path of setting up their blog and in the process going through some of the same dilemmas that I did. This is an attempt at being of some help if I could.&lt;/p&gt;
&lt;figure class=&quot;disclaimer warn&quot;&gt;
    Content below is bespoke and opinionated.
&lt;/figure&gt;
&lt;p&gt;Here’s are the things that mattered to me, which I’m going to talk about today.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#Self-owned-content-rather-than-hosted-at-somebody-elses-mercy-whom-Im-probably-paying-again-to-serve-content-that-I-created&quot;&gt;Self-owned content rather than hosted at somebody else’s mercy whom I’m probably paying again to serve content that &lt;em&gt;I&lt;/em&gt; created.&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#Creative-freedom-and-customizability-to-establish-identity-and-individuality&quot;&gt;Creative freedom and customizability to establish identity and individuality.&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#Technical-freedom-to-do-more-under-the-same-roof&quot;&gt;Technical freedom to do more under the same roof.&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#Statically-built-website-for-speed-frugality-and-agility-With-possible-eco-friendliness-sprinkled-on-top&quot;&gt;Statically built website for speed, frugality and agility. With possible eco-friendliness sprinkled on top.&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#Transparency-portability-and-version-control-of-content&quot;&gt;Transparency, portability and version control of content&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#The-subliminal-developer-experience&quot;&gt;The subliminal developer experience&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#A-pretext-to-learn-new-things-and-apply-them&quot;&gt;A pretext to learn new things and apply them&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#But-why-Gatsby-Why-Gatsby-at-all&quot;&gt;But why Gatsby? Why Gatsby at all?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#So-is-it-all-sunshine-and-rainbows&quot;&gt;So, is it all sunshine and rainbows?&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h6 id=&quot;Self-owned-content-rather-than-hosted-at-somebody-elses-mercy-whom-Im-probably-paying-again-to-serve-content-that-I-created&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#Self-owned-content-rather-than-hosted-at-somebody-elses-mercy-whom-Im-probably-paying-again-to-serve-content-that-I-created&quot; aria-label=&quot;Self owned content rather than hosted at somebody elses mercy whom Im probably paying again to serve content that I created permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Self-owned content rather than hosted at somebody else’s mercy whom I’m probably paying again to serve content that &lt;em&gt;I&lt;/em&gt; created.&lt;/h6&gt;
&lt;p&gt;I prefer to fully own the content I create, published at my discretion and not subject to some third-party hosted platform’s changing policies and potential censorship. This is why I didn’t go for hosted solutions like &lt;a href=&quot;https://medium.com&quot;&gt;Medium&lt;/a&gt; or even &lt;a href=&quot;https://wordpress.com&quot;&gt;Wordpress.com&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Not to mention the associated service’s usage pricing models that would be involved: while they all &lt;em&gt;start free&lt;/em&gt;, the way it typically goes is that as you get bigger, or desire to be more customizable or feature-rich, it’s common to charge you for various ancillary and auxiliary services. I’m not at all saying that’s unjustified considering all the useful services they may provide, rather something that I felt wasn’t worthwhile for me. At least not as the &lt;em&gt;canonical&lt;/em&gt; place to put my content.&lt;/p&gt;
&lt;h6 id=&quot;Creative-freedom-and-customizability-to-establish-identity-and-individuality&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#Creative-freedom-and-customizability-to-establish-identity-and-individuality&quot; aria-label=&quot;Creative freedom and customizability to establish identity and individuality permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Creative freedom and customizability to establish identity and individuality.&lt;/h6&gt;
&lt;p&gt;The other reason I didn’t prefer hosted solutions is simply the fact that they all practically provide canned, run-of-the-mill experiences to everyone who signs up on their platform. There’s a lot of merit in the standardization of things. As someone who lives off building things for the web, you can understand probably where and why my sentiments lie in the context of standardizing things, but in this case, I’d clearly choose to move away at the behest of &lt;em&gt;creative freedom&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;A blog, portfolio (or both?) that I’ve put out there should reflect what I feel defines me, is unique or just the way I want my audience to experience it. I should be able to design an user experience with the look-n-feel and feature-sets that I &lt;em&gt;want&lt;/em&gt;, free from limitations imposed by a certain service or platform that I’m using.&lt;/p&gt;
&lt;p&gt;As a developer, being able to have that &lt;em&gt;free reign&lt;/em&gt; is critical to me. I can afford to invest some time and effort to have things built exactly to my will. Not being able to do so today or tomorrow is not a chip I’m willing to bargain.&lt;/p&gt;
&lt;p&gt;Some platforms like Wordpress do offer you decent degrees of flexibility, but still, come with a lot of opinions and often fall short of what you want to achieve, either ending up with practical limitations or simply becoming too cost prohibitive and making you lose interest and give up. Some other platforms are even worse. Forget customizing too much, Medium for one has stopped providing some basic capabilities to build your identity such as associating a custom domain name with your blog. They used to do that at a steep price at some point, but have stopped now altogether. I have my personal speculations (a gun to the head of your potential future from easily moving off their platform?), but that’s a conversation for another day.&lt;/p&gt;
&lt;h6 id=&quot;Technical-freedom-to-do-more-under-the-same-roof&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#Technical-freedom-to-do-more-under-the-same-roof&quot; aria-label=&quot;Technical freedom to do more under the same roof permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Technical freedom to do more under the same roof.&lt;/h6&gt;
&lt;p&gt;Continuing with the above, it’s not just limited to the blogging aspect of it. I can imagine a future where I’d probably want to create custom content - say experiments, projects or labs that I’d want to set up, talk about and share. Being able to host and serve all of that together under the same roof would be great.&lt;/p&gt;
&lt;p&gt;There are some counter-arguments to this. In theory, I could still host my blog on some chosen software platform, put that behind a domain and wire other enhancements hosted elsewhere through sub-domains and such… or even some hypothetical under-the-hood rewrites, as unlikely as sounds. I imagine it’s a decision probably best deferred. Right now, the best I could do is make the least assumptions and keep as many potential pathways open as I can. Cheers to &lt;a href=&quot;https://en.wikipedia.org/wiki/Occam%27s_razor&quot;&gt;Occam’s Razor!&lt;/a&gt;&lt;/p&gt;
&lt;h6 id=&quot;Statically-built-website-for-speed-frugality-and-agility-With-possible-eco-friendliness-sprinkled-on-top&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#Statically-built-website-for-speed-frugality-and-agility-With-possible-eco-friendliness-sprinkled-on-top&quot; aria-label=&quot;Statically built website for speed frugality and agility With possible eco friendliness sprinkled on top permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Statically built website for speed, frugality and agility. With possible eco-friendliness sprinkled on top.&lt;/h6&gt;
&lt;p&gt;This is a popular school of thought now, and in my opinion for a lot of good reasons. At the moment, I have been bought into the general philosophy behind &lt;a href=&quot;https://jamstack.org/&quot;&gt;JAMStack&lt;/a&gt;. As a frontend engineer, I can’t help but confess that it has quite effortlessly grown close to my heart. I love the way how it naturally bakes in the segregation of &lt;code class=&quot;language-text&quot;&gt;data&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;view&lt;/code&gt; so easily into its core architectural principles and then enables a myriad of use-cases from the simplest to adequately complex web applications to be cheaply hosted, effectively scaled and swiftly modified to ever-changing needs, the 2010s-generation definition of “static websites”. Yes, that involves ample amount of client-side rendering, but we’ll try not to pick up pitchforks with the performance aspects of that just yet.&lt;/p&gt;
&lt;p&gt;The use-case of a blog flows in surprisingly well into this paradigm. The actual content of your typical blog is predominantly going to be crafted manually rather than being dynamically generated. It’s not going to change on the fly from one reader visiting your blog to another. So are the pages that are rendered with the content, which can be crafted fairly well at &lt;em&gt;build time&lt;/em&gt; rather than runtime.&lt;/p&gt;
&lt;p&gt;There are definitely conceivable things could give your content an edge when done at runtime (say more complex features or optimizations around ranking content or a dynamically computed “top posts this week”), but most of these would be second or third order capabilities for a personal blog rather than the core experience, and if some sweet spot exists between dynamic generation vs. something that is fully statically built, it would fairly strongly lean towards being sufficiently achievable through intermittent build-time computations in a controlled environment as opposed to runtime calculations that need to be done every time a page is requested.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.freecodecamp.org/news/dont-do-it-at-runtime-do-it-at-design-time-c4f59d1775e4/&quot;&gt;Avoid doing at runtime what you can do at design time.&lt;/a&gt; With that in mind, it doesn’t make a lot of sense to introduce an added dependency on a server requiring computational power to serve predominantly non-dynamic content, and on account of that, consume some energy. Again scaling those servers with more computational power with added traffic. More servers, more energy, more money. For a lot of apparent scenarios, it may not seem that big of a deal, but it feels quite wasteful to me.&lt;/p&gt;
&lt;p&gt;With pre-built static components though, it becomes a fairly trivial matter in comparison of having to dumbly serve some static files without any complex computational needs, which can then be done by cheap, distributed and easily scalable &lt;a href=&quot;https://en.wikipedia.org/wiki/Content_delivery_network&quot;&gt;CDNs&lt;/a&gt;. You can practically eliminate the time and energy required to compute things on the server over and over again and consequentially the need to even commission those servers, and at the same time be more easily available and closer to your audience with this static content that can be readily cached and distributed geographically.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Both&lt;/em&gt; of these factors are going to improve your user experience by making the pages of your blog load much &lt;em&gt;faster&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Without the need for provisioning servers with custom requirements and computational demand, these CDNs come &lt;em&gt;really cheap&lt;/em&gt; and easily scale by replicating themselves at various points of presence, which lets them respond to your audience with your content much faster. Imagine the pain you’d have had to go through if you realised you needed to deploy custom servers that compute your content, all across major geographical areas!&lt;/p&gt;
&lt;p&gt;Case in point, Netlify provides services that are practically &lt;em&gt;free of cost&lt;/em&gt; for hosting static websites to until a quite sufficiently large amount of traffic per month. If I were to go beyond that consumption for my blog, I’d take it as a happy problem to have, and pay for the additional amount gladly! On top of that it provides a &lt;a href=&quot;https://medium.com/netlify/10-netlify-features-to-surprise-and-delight-225e846b7b21&quot;&gt;bunch of other great things&lt;/a&gt; that I love, and I’m pretty sure you will too. Not an affiliate promotion. Just a happy customer.&lt;/p&gt;
&lt;p&gt;Security-wise, data isolation is not something that I was gunning for here. That’s by design, where the usual idea of a blog and it’s content is to be available publicly, as was for me. Data integrity, however, is surely important, and something we can work out given a CDN service provider of choice, as well as make sure that we’re doing some basic things such as serving content over &lt;a href=&quot;https://en.wikipedia.org/wiki/HTTPS&quot;&gt;HTTPS&lt;/a&gt; from the CDN &lt;a href=&quot;https://en.wikipedia.org/wiki/Content_delivery_network#Technology&quot;&gt;edge servers&lt;/a&gt;. These are pretty elemental at this point and commonly supported by these service providers out-of-the-box. For more scrupulous aspects of security, that would sadly be beside the point of this article. And the fact that you’re probably not looking for a security expert in me right now. So I’ll stop digressing more on that front.&lt;/p&gt;
&lt;h6 id=&quot;Transparency-portability-and-version-control-of-content&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#Transparency-portability-and-version-control-of-content&quot; aria-label=&quot;Transparency portability and version control of content permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Transparency, portability and version control of content&lt;/h6&gt;
&lt;p&gt;Imagine owning a blog with a large number of articles, thoughts, and posts that you’ve accrued over time with your blood, sweat and tears. Now imagine all that gone in a poof due to a database corruption with your blog hosting service provider. It makes my skin crawl.&lt;/p&gt;
&lt;p&gt;Hosting service providers will generally make sure to host your content well. But bad things happen. Systems fail. And while the scenario of an acute loss of system availability may seem a little bit contrived in context, considering any well-meaning and sensible hosting service provider will intend to (and hopefully) make certain of data redundancy, at the end of the day it’s a matter of placed trust and the perceived transparency.&lt;/p&gt;
&lt;p&gt;Of course, the whole damn world is running on the cloud. But not every cloud thunders the same, so to speak. My goal here is not to be paranoid but I’d prefer to avoid losing any sleep over this. It’s not possible for me to know upfront how well-built a certain xyz-affordable-hosting-dot-com might be, or to be more appropriate, the quality of service it’ll always provide me.&lt;/p&gt;
&lt;p&gt;So rather than going down the route of hiring some service provider who spins a server and a rents out some storage space in some hosted database somewhere where it becomes hard for me to easily keep track of all the content that I create and keep changing; or don’t even know how complicated it is going to be to port all of that content in and out of that system, I’d bias towards keeping things simple and stick to mechanisms I already trust to not make my life harder later.&lt;/p&gt;
&lt;p&gt;For a blog which is again mostly text-based serialized documents in some form and could be easily stored directly as files, rather than relying on some database-service backed system that stores my content and then potentially requires some additional processes (and &lt;a href=&quot;https://en.wikipedia.org/wiki/Service-level_agreement&quot;&gt;SLAs&lt;/a&gt;) to push, pull, transform or migrate data, I’d stick to a simpler way where the content I create is retained directly natively in the form I created them. As files. In some repository. In the same way that I’m used to storing and maintaining with a lot of reliability, the source code that I write, again, with my blood, sweat and tears. This also gets me all the benefits of a well-defined, robust, and battle-tested &lt;em&gt;version-control&lt;/em&gt; system.&lt;/p&gt;
&lt;p&gt;I’m talking about authoring in &lt;a href=&quot;https://en.wikipedia.org/wiki/Markdown&quot;&gt;Markdown&lt;/a&gt; and maintaining them on &lt;a href=&quot;https://github.com/&quot;&gt;Github&lt;/a&gt; (or &lt;a href=&quot;https://about.gitlab.com/&quot;&gt;GitLab&lt;/a&gt; or &lt;a href=&quot;https://bitbucket.org/&quot;&gt;BitBucket&lt;/a&gt; if you will).&lt;/p&gt;
&lt;p&gt;I know I don’t have to sell Markdown here. In &lt;em&gt;developer-town&lt;/em&gt;, it’s quite the universally accepted format for authoring documents. So I’m probably safe to not worry about potential pains of migration later if I wanted to move all my content from one system to another. If in future my destination system needs to understand something other than Markdown, it’s still probably possible that there’s some transpiler available somewhere, if this so-called destination system is popular enough.&lt;/p&gt;
&lt;p&gt;What’s truly great about this approach is now the content that I create is apparent to me, how it’s stored and transformed. It’s simple. They are files. I can hit a simple file copy and replicate them as many times as I want, back them up when I want, where I want, extremely easily (probably along with git repo itself to preserve the versioning and it’s history). Given that, the chances of me losing it all is really, really slim. It does come with placing a lot of trust on Github really. That’s fair. Also acceptable. And definitely a much better bet than xyz-affordable-hosting-dot-com.&lt;/p&gt;
&lt;h6 id=&quot;The-subliminal-developer-experience&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#The-subliminal-developer-experience&quot; aria-label=&quot;The subliminal developer experience permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;The subliminal developer experience&lt;/h6&gt;
&lt;p&gt;In continuation of the above theme, this is where I risk pissing off some great proponents of &lt;a href=&quot;https://en.wikipedia.org/wiki/Separation_of_concerns&quot;&gt;separation of concerns&lt;/a&gt; who also believe that all things should be black or white.&lt;/p&gt;
&lt;p&gt;For me as a developer, maintaining my blog is not too different from maintaining any other software project. In fact, &lt;em&gt;it’s both&lt;/em&gt;.&lt;/p&gt;
&lt;figure class=&quot;float-left&quot; style=&quot;width: 300px&quot;&gt;
    &lt;img src=&quot;/static/media/meme-morpheus-1.jpg&quot; alt=&quot;What if I told you, it&apos;s okay&quot;&gt;
&lt;/figure&gt;
&lt;p&gt;In the developer ecosystem, we’ve historically built great systems and tooling to make sure things work the way they are supposed to, and that building and maintaining those things are a good experience.&lt;/p&gt;
&lt;p&gt;So it naturally feels great to impart a lot of those benefits to creating and maintaining &lt;em&gt;content&lt;/em&gt; as well.&lt;/p&gt;
&lt;p&gt;Does that mean we store all of that code and content intertwined together? The initial thought of “coupling together” code and content sounds repulsive. Where is the abstraction, the modularity, the single-responsibility, the separation of concerns? Oh, and the risk of non-generalization of software? Heresy!&lt;/p&gt;
&lt;p&gt;But sacrificing those qualities is not exactly what we’re doing here. What I merely suggest is a bit more nuanced mechanism for the specific use-case of creating and maintaining a personal blog (or even one managed by a small group of people).&lt;/p&gt;
&lt;p&gt;I know a typical personal blog is not going to blow out of proportions, neither does it have to be overly generalized and potentially over-engineered. So I’d start with a simple setup that I can easily maintain. Starting with one repository that contains everything is perfectly fine. In fact, having it all together pleasantly surprised me with some unexpected advantages!&lt;/p&gt;
&lt;p&gt;It’s one single portable setup, and I don’t have to run around between multiple workplaces, editor instances or software tools which are essentially contributing to the same thing.&lt;/p&gt;
&lt;p&gt;I get to use the same toolchain to construct my content that I use to construct my code. Writing content feels like writing code. When I write a new article, I get the benefits of pushing a version-controlled commit to a Github repository, a hook that automatically kicks off a build in Netlify when this commit is pushed and then deploys it to their CDN that hosts my website, through &lt;a href=&quot;https://www.netlify.com/docs/continuous-deployment/&quot;&gt;continuous deployment&lt;/a&gt;. It’s a simple workflow, predictable and hassle-free. It makes me happy.&lt;/p&gt;
&lt;p&gt;The best part is, when I create content and see it in action, I get the benefit of getting instant feedback on code components that I build for rendering the pages of this blog off that bare-bone content, and if I need to tweak something, it’s a fairly simple matter of modifying the code in a file that I also have open in another tab in my code editor. I am my own principal customer, and it’s one of the best things that can ever happen to a software developer when building a product. I call this the &lt;em&gt;“firewire of feedback”&lt;/em&gt; (yes, I just came up with that now).&lt;/p&gt;
&lt;p&gt;It’s important to realise that building a blog - both the content and the software around publishing it - is &lt;em&gt;not&lt;/em&gt; a one-before-the-other process. They happen in tandem. The day you first set up your blog; and every day henceforth. So as long as scale permits, and your source is designed to not complicate a separation if ever required later at a later point in time, it’s perfectly fine to have had put them all together now.&lt;/p&gt;
&lt;p&gt;At a superficial level, this can sound unconventional, but not all conventional wisdom of the time is necessarily right. I’m tempted to draw a parallel to the kind of backlash that us “separatists” may have helped contribute to when &lt;a href=&quot;https://reactjs.org/&quot;&gt;React&lt;/a&gt; came along and said it’s probably better to put HTML, CSS and JS code all together in one single file for a component. But I will not. It’s a bit too extreme. And not very analogous. But hey, we all know how that went. It’s correct if it’s done for the right reasons. I guess the way to look at it is re-evaluating over time whether the concerns, in fact, are really so separate.&lt;/p&gt;
&lt;h6 id=&quot;A-pretext-to-learn-new-things-and-apply-them&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#A-pretext-to-learn-new-things-and-apply-them&quot; aria-label=&quot;A pretext to learn new things and apply them permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;A pretext to learn new things and apply them&lt;/h6&gt;
&lt;p&gt;So far I talked about the &lt;em&gt;means&lt;/em&gt; and the &lt;em&gt;motive&lt;/em&gt;. Let’s also talk about an &lt;em&gt;opportunity&lt;/em&gt; in the overall equation.&lt;/p&gt;
&lt;p&gt;This is perhaps the simplest reasoning of all.&lt;/p&gt;
&lt;p&gt;The idea of setting up your own blog, building features and being able to customize it to your heart’s content does come with a great appeal to a developer who’s excited to play around with new things.&lt;/p&gt;
&lt;p&gt;It’s of course always possible to go about hacking around with new stuff anyway, but in my experience, more often than not such endeavours end up remaining temporal and isolated efforts, which without a structured motive to build, maintain or scale something do not really lead to any sustained excitement or a meaningful learning experience.&lt;/p&gt;
&lt;p&gt;It just works better when you have a real-world target use-case in action. So why lose a great opportunity to capitalize on that?&lt;/p&gt;
&lt;p&gt;Great software is built on well-executed principles of abstraction. I don’t love abstractions I don’t understand. I &lt;em&gt;do&lt;/em&gt; love them later, but only &lt;em&gt;after&lt;/em&gt; I’ve broken into, dissected and then put ‘em all back together. I love to work with primitives. I like to understand how the tools in my belt work… to a sufficient degree. This is not to say that I’d prefer to reinvent the laws of motion before using them to solve a problem, but I am just one of those people who can’t stay away long from getting their hands dirty messing around with the nitty-gritties. This habit is not always necessarily your friend. Sometimes, it just likes to manifest as a compulsive disorder.&lt;/p&gt;
&lt;p&gt;So here’s me simply cashing in on that to learn a new concept or just a new way to look at a problem. Or a framework, tool or language. Fiddling with them. Applying them in context to real-world problems.&lt;/p&gt;
&lt;p&gt;It enriches you.&lt;/p&gt;
&lt;h6 id=&quot;But-why-Gatsby-Why-Gatsby-at-all&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#But-why-Gatsby-Why-Gatsby-at-all&quot; aria-label=&quot;But why Gatsby Why Gatsby at all permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;But why Gatsby? Why Gatsby at all?&lt;/h6&gt;
&lt;figure&gt;
	&lt;blockquote&gt;
		&lt;p&gt;&quot;I wasn&apos;t actually in love, but I felt a sort of tender curiosity.&quot;&lt;/p&gt;
		&lt;footer&gt;
			&lt;cite&gt;— F. Scott Fitzgerald, The Great Gatsby&lt;/cite&gt;
		&lt;/footer&gt;
	&lt;/blockquote&gt;
&lt;/figure&gt;
&lt;p&gt;Come on. I could not just pass on the opportunity to &lt;em&gt;not&lt;/em&gt; do that.&lt;/p&gt;
&lt;p&gt;But putting all the flare and drama aside, it really boils down to one dead simple fact. For me, &lt;a href=&quot;https://www.gatsbyjs.org&quot;&gt;Gatsby&lt;/a&gt; simply seemed to fit the bill really well.&lt;/p&gt;
&lt;p&gt;I wanted a static site generator, but in a language or toolchain that I was comfortable sticking around with for a while. And Gatsby seemed to meet that, along with having been a massive success in the JavaScript frontend community.&lt;/p&gt;
&lt;p&gt;It seemed-thought through and well-designed, with a lot of the right bells and whistles to make you productive, yet comfortably distant in its own way to not breathe down your neck with too many opinions on how to build websites.&lt;/p&gt;
&lt;p&gt;It came built using technologies I was convinced I would be interested to learn and play around with: &lt;a href=&quot;https://reactjs.org/&quot;&gt;React&lt;/a&gt;, &lt;a href=&quot;https://graphql.org/&quot;&gt;GraphQL&lt;/a&gt;. And a &lt;a href=&quot;https://www.gatsbyjs.org/docs/why-gatsby-uses-graphql/&quot;&gt;thoroughly justified&lt;/a&gt; rationale at that.&lt;/p&gt;
&lt;p&gt;It seemed to come with an architecture that consisted of well-defined component &lt;a href=&quot;https://www.narative.co/articles/understanding-the-gatsby-lifecycle&quot;&gt;lifecycles&lt;/a&gt; and somewhat neutrally opinionated hooks for common and useful things, making it immensely pluggable and strangely powerful at the same time.&lt;/p&gt;
&lt;p&gt;It did &lt;em&gt;not&lt;/em&gt; come with a prescription on what to build and how to build it. It was not a blogging solution. But it was a solution using which blogs, and thankfully much more, could be so elegantly built.&lt;/p&gt;
&lt;p&gt;It had &lt;em&gt;great&lt;/em&gt; &lt;a href=&quot;https://www.gatsbyjs.org/contributing/community/&quot;&gt;community support&lt;/a&gt;, and the right pedigree to build websites that are &lt;em&gt;genuinely&lt;/em&gt; &lt;a href=&quot;https://www.freecodecamp.org/news/how-gatsby-is-so-blazing-fast-c99a6f2d405e/&quot;&gt;fast&lt;/a&gt;, that’s not just focused on the final end user experience (UX), but also the developer experience (DX). Powered with &lt;a href=&quot;https://webpack.js.org/&quot;&gt;Webpack&lt;/a&gt; it supported out-of-the-box hot-reloads, came with a lot of sensible defaults, a large number of &lt;a href=&quot;https://www.gatsbyjs.org/plugins/&quot;&gt;plugins&lt;/a&gt; and a rich set of &lt;a href=&quot;https://www.gatsbyjs.org/docs/&quot;&gt;guidelines&lt;/a&gt;, &lt;a href=&quot;https://www.gatsbyjs.org/starters/&quot;&gt;starter packs&lt;/a&gt; and &lt;a href=&quot;https://www.gatsbyjs.org/showcase/&quot;&gt;inspiration&lt;/a&gt; to get you up and kickin’ sooner than you’d believe.&lt;/p&gt;
&lt;p&gt;And great support for easily building &lt;a href=&quot;https://developers.google.com/web/progressive-web-apps/&quot;&gt;Progressive Web Apps&lt;/a&gt; (PWA). The direction things are going, it’s probably fallible today to underestimate the power of PWAs. But I believe it’s even more dangerous to underestimate the power of a simple, elegant and well-built tool to facilitate building PWAs.&lt;/p&gt;
&lt;p&gt;Maybe I did actually fall in love with it. Or maybe not. But rest assured I was convincingly sold enough to go ahead and give it a real try.&lt;/p&gt;
&lt;p&gt;The documentation could improve though in being a bit more detailed, I suppose.&lt;/p&gt;
&lt;p&gt;There’s a &lt;a href=&quot;https://www.gatsbyjs.org/features/&quot;&gt;detailed chart&lt;/a&gt; of what Gatsby can and can not do in comparison to other popular static site generators such as &lt;a href=&quot;https://jekyllrb.com/&quot;&gt;Jekyll&lt;/a&gt; or content management systems (CMS) like Wordpress or site-builders if you’re interested.&lt;/p&gt;
&lt;h6 id=&quot;So-is-it-all-sunshine-and-rainbows&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#So-is-it-all-sunshine-and-rainbows&quot; aria-label=&quot;So is it all sunshine and rainbows permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;So, is it all sunshine and rainbows?&lt;/h6&gt;
&lt;p&gt;Surely not. But it’s more sunny than gloomy though. In a good way.&lt;/p&gt;
&lt;p&gt;At the end of it all, Gatsby is still a static site generator, although deceivingly powerful.&lt;/p&gt;
&lt;p&gt;There are some features that a more “true” and powerful CMS with more dynamically rendered content and features could provide that I could wish for. Remember that dynamically computed “top posts this week” example I had in a section above?&lt;/p&gt;
&lt;p&gt;However, having the pros and cons weighed and the cost-benefit measured, the return on investment of using Gatsby and Netlify in building and maintaining a blog has been so far simply amazing! And I would not have it in any other way! Well… if the software industry has taught me something, &lt;em&gt;never&lt;/em&gt; say never. But today is not that day.&lt;/p&gt;
&lt;p&gt;Also, not having gone the route of using an existing hosted platform that has baked in capabilities to distribute your content within its community, gather audience and cross-pollinate, it definitely becomes a much harder challenge to figure out to gather a sustained audience. To be honest, it’s one of the biggest things to solve for a successful blog. And I have not solved that yet.&lt;/p&gt;
&lt;p&gt;There are definitely a bunch of ways and ideas though. For one, I could always cross-post keeping my own blog site as the canonical, to start seeding some community presence and feedback. It’s a common pattern, and I believe many communities, such as &lt;a href=&quot;https://dev.to/&quot;&gt;Dev.to&lt;/a&gt;, encourage that (I absolutely adore what Dev.to is and stands for). Over time, I definitely plan to add more capabilities to my blog to subscribe readers, share, and engage them.&lt;/p&gt;
&lt;p&gt;It takes some time to create good content, surface that (be it on dedicated communities or search engines) and start getting people to talk over it. It’s not easy, but neither unthinkable. Existing on a hosted platform does not solve that magically as well. It’s hard and a much longer battle, but people have done that time and again. And it’s a topic that I’ll want to talk about in more detail as well in future sometime.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;So that’s about it today.&lt;/p&gt;
&lt;p&gt;Phew, that was one long article! And I genuinely appreciate you if you’ve read all through it and come this far!&lt;/p&gt;
&lt;p&gt;Feedback and opinions welcome!&lt;/p&gt;</content:encoded></item><item><title><![CDATA[On personal blog as a software developer]]></title><link>https://www.ghosh.dev/posts/on-personal-blog-as-a-software-developer/</link><guid isPermaLink="false">https://www.ghosh.dev/posts/on-personal-blog-as-a-software-developer/</guid><pubDate>Sat, 01 Jun 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I am a software engineer. I have been in the industry for five years now. I have been part of a “big-five tech company”, working on things that more often than not, gives me the opportunity to fulfil my intellectual need and curiosity as a developer. All that is great. However, when I look at the general developer community out there, I largely don’t exist.&lt;/p&gt;
&lt;!-- ee --&gt;
&lt;p&gt;Five years is a significant time for someone to learn, grow and establish themselves out there. Not that I have not grown or established myself professionally - in fact, it’s quite the opposite - I could confidently say that all this time, there has been a tremendous amount of learning on my part for which I am quite so thankful. A lot of that learning and expression of skills, for good or bad, ever so stays within the confines this immediate environment and the people within it, where I get to exist comfortably in that world of its own! While no matter how great that can be, in the globally open community out there, I still don’t quite so, &lt;em&gt;exist&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;I’d be lying if I didn’t say that I was tempted to blame it on the fact that I would have been engrossed in technologies and projects that existed within that cocoon, or that I felt that I always had more things to do than I had the time to sit down and share with the community my thoughts or those little side-projects which I’d think why would anyone even want to care about anyway? But that’s not necessarily true. I didn’t get around since I hadn’t bothered enough to, myself. I somehow did not feel that need. In retrospect, that’s a bit of a regret.&lt;/p&gt;
&lt;p&gt;So why this new-found urge to &lt;em&gt;exist&lt;/em&gt; out there now? It’s for sure not about showing the world how ‘great’ I am. This is not a showcase or portfolio. Or even the fact that I don’t really care for one. I am definitely not looking out for work opportunities as well. I wouldn’t naively disagree that having a great community presence and a medium to talk about your work, skills or interests definitely adds to your career, but that’s not why I believe I have this lingering feeling that I need to put myself out there. This is not an egotistical existential crisis. This is an epiphany - that largely - this situation, this pattern - is in fact, the general, sad truth out there.&lt;/p&gt;
&lt;p&gt;I personally know a lot of great people, who have so much to offer. I don’t personally know even fractionally close to as many people who have actively found out and settled down on ways to be part of and regularly contribute to the general global software development community out there. Of course, everyone is entitled to their choices, but as for me, I can only genuinely hope that I am not on the wrong side of this universe.&lt;/p&gt;
&lt;figure class=&quot;float-left&quot; style=&quot;width: 240px&quot;&gt;
	&lt;img src=&quot;/static/media/isaac-newton-pixabay-reuse.png&quot; alt=&quot;https://pixabay.com/vectors/isaac-newton-portrait-vintage-3936704/&quot;&gt;
	&lt;figcaption&gt;
        &quot;I do not know what I may appear to the world, but to myself I seem to have been only like a boy playing on the sea-shore, and diverting myself in now and then finding a smoother pebble or a prettier shell than ordinary, whilst the great ocean of truth lay all undiscovered before me.&quot;
        &lt;br&gt;
        &lt;br&gt;
        &quot;If I have seen further it is by standing on ye sholders of Giants&quot;
        &lt;br&gt;
        &lt;br&gt;
        - Isaac Newton
    &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;It’s generally agreed that the software engineering industry is something that is split out so vast in its breadth and depth and ever grows so exponentially, that it’s practically impossible for anyone to really grasp and know about most of any of it at all. This is not something necessarily special about this field though. It’s the general hallmark of anything worth having knowledge about. Even Isaac Newton, the man who discovered the laws of motion, invented calculus, and fundamentally changed the understanding of the physical world around us at his time, is famously quoted more than once to have shared &lt;a href=&quot;https://www.goodreads.com/quotes/30410-i-do-not-know-what-i-may-appear-to-the&quot;&gt;such&lt;/a&gt; &lt;a href=&quot;https://www.goodreads.com/quotes/10286-if-i-have-seen-further-it-is-by-standing-on&quot;&gt;sentiments&lt;/a&gt;. And it is not surprising to see &lt;a href=&quot;https://www.google.com/search?q=imposter+syndrome+in+software+development&amp;#x26;num=50&quot;&gt;how much the software development community suffers from imposter syndrome&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I can’t help but feel that this is perhaps one of the biggest reasons as to why I, and like me, thousands of others, may have not cared enough to bother putting ourselves out there.&lt;/p&gt;
&lt;p&gt;But I’m going to give you a counterpoint to this. This, in fact, is exactly &lt;em&gt;the reason why&lt;/em&gt; we should care about putting ourselves out. There is too much out there to know and share, and we are, in fact, all just boys, girls or otherwise, playing on the proverbial sea-shore. Finding a few more pebbles to play with will only make everyone happier.&lt;/p&gt;
&lt;p&gt;We have all had our own unique forays into the world of software development, with our own unique experiences leading to the acquisition of a unique combination of knowledge and &lt;em&gt;realisations&lt;/em&gt; out of the knowledge acquired.&lt;/p&gt;
&lt;p&gt;Not that all of this specifically applies to just blogging (there are so many other avenues that we can try), but in the context of this conversation, that’s the extent I will resort to. Personally, I don’t think I need to preach &lt;a href=&quot;http://manojsinghnegi.com/blog/2017/01/24/Why-every-developer-should-have-a-blog/&quot;&gt;again&lt;/a&gt; and &lt;a href=&quot;https://www.freecodecamp.org/news/every-developer-should-have-a-blog-heres-why-and-how-to-stick-with-it-5fd55a247fbf/&quot;&gt;again&lt;/a&gt; and &lt;a href=&quot;https://www.freecodecamp.org/news/if-youre-a-developer-you-should-start-blogging-and-here-s-why-b5cb2951d95c/&quot;&gt;again&lt;/a&gt; why having your own blog as a developer is a great thing and how it benefits you (and the community). I believe a lot of people have already done an amazing job out there on explaining all the great things about doing so.&lt;/p&gt;
&lt;p&gt;What I &lt;em&gt;will&lt;/em&gt; urge though, is hankering about it.&lt;/p&gt;
&lt;p&gt;So as it seems, this post evidently turned out to be a shoutout to my inner self and then to all of those people out there! There is enough substance in you to share. There’s someone somewhere out there who may be desperately seeking an effective way to obtain the knowledge that you may have in your possession so nonchalantly. Of course, not every bit of information needs to flow free openly, that’s fair. But there’s ever far so less than what positively could be.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;So give it a shot&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;If nothing else, this attempt will still result in the creation of a medium of expression and validation for yourself in the least. Hopefully, that could be motivation enough for some.&lt;/p&gt;
&lt;p&gt;So this blog is my attempt at practising what I preach, a little pebble at the shore of that great ocean!&lt;/p&gt;
&lt;p&gt;Better late than never, eh?&lt;/p&gt;</content:encoded></item><item><title><![CDATA[The Quintessential Hello World!]]></title><link>https://www.ghosh.dev/posts/the-quintessential-hello-world/</link><guid isPermaLink="false">https://www.ghosh.dev/posts/the-quintessential-hello-world/</guid><pubDate>Mon, 20 May 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Hello there! I’m glad to have found you land here. Welcome to my personal blog, where I plan to write on tech and other things that interest me.&lt;/p&gt;
&lt;!-- ee --&gt;
&lt;p&gt;I have been meaning to set up a fresh personal website-slash-blog for myself for a bit now and some time back someone told me (actually, I read somewhere) that “all the cool kids are using &lt;a href=&quot;https://www.gatsbyjs.org/&quot;&gt;Gatsby&lt;/a&gt; these days”, so here I am - trying out my luck, dabbling around with Gatsby and &lt;a href=&quot;https://netlify.com&quot;&gt;Netlify&lt;/a&gt; to set this up! I’ll probably do a more detailed post in the future on my journey through it, and talk about things that I like (or not!) about them.&lt;/p&gt;
&lt;p&gt;So like most good things in this world, this website is still very much work-in-progress. A shout-out to the wonderful &lt;a href=&quot;https://github.com/alxshelepenok/gatsby-starter-lumen&quot;&gt;gatsby-starter-lumen&lt;/a&gt; starter theme by Alexander Shelepenok and team which helped me a lot by providing an amazing ground work while starting to build this; although I’ll admit I can’t help myself going crazy with all the customizations!&lt;/p&gt;
&lt;p&gt;You can check out the &lt;a href=&quot;/pages/about&quot;&gt;about&lt;/a&gt; and &lt;a href=&quot;/pages/contact&quot;&gt;get in touch&lt;/a&gt; pages to know more about me.&lt;/p&gt;
&lt;p&gt;Cheers!&lt;/p&gt;</content:encoded></item></channel></rss>