<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Abhishek Ghosh</title>
    <description>The latest articles on Forem by Abhishek Ghosh (@abhishekcghosh).</description>
    <link>https://forem.com/abhishekcghosh</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F89029%2F2a88c272-4ef3-448c-be4f-f7517b23409e.jpeg</url>
      <title>Forem: Abhishek Ghosh</title>
      <link>https://forem.com/abhishekcghosh</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/abhishekcghosh"/>
    <language>en</language>
    <item>
      <title>Playing with video scrubbing animations on the web</title>
      <dc:creator>Abhishek Ghosh</dc:creator>
      <pubDate>Sat, 28 Dec 2019 18:00:00 +0000</pubDate>
      <link>https://forem.com/abhishekcghosh/playing-with-video-scrubbing-animations-on-the-web-29k</link>
      <guid>https://forem.com/abhishekcghosh/playing-with-video-scrubbing-animations-on-the-web-29k</guid>
      <description>&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="https://en.wikipedia.org/wiki/Number_line"&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="https://www.apple.com/airpods-pro/"&gt;Airpods Pro&lt;/a&gt; website.&lt;/p&gt;

&lt;p&gt;So far every time, almost to a fault, &lt;a href="https://twitter.com/rauchg/status/1189327508442943488"&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="https://twitter.com/jaffathecake/status/1189653092117155842"&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="https://video-scrub.playground.ghosh.dev/"&gt;live demos&lt;/a&gt; and &lt;a href="https://github.com/abhishekcghosh/experiment-video-scrub"&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="https://gist.github.com/jsturgis/3b19447b304616f18657"&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="https://www.speedtest.net/result/8895140476"&gt;fairly good&lt;/a&gt; WiFi connection.&lt;/p&gt;

&lt;h1&gt;
  
  
  Approaches
&lt;/h1&gt;

&lt;h2&gt;
  
  
  #1: video-current-time (&lt;a href="https://video-scrub.playground.ghosh.dev/video-current-time/"&gt;demo&lt;/a&gt;)
&lt;/h2&gt;

&lt;p&gt;This mechanism simply loads the video in an HTML5 &lt;code&gt;video&lt;/code&gt; tag and sets the &lt;code&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;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="https://twitter.com/thespite/status/1189328760266510339"&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&gt;
  
  
  #2: video-play-unpack-frames-canvas (&lt;a href="https://video-scrub.playground.ghosh.dev/video-play-unpack-frames-canvas/"&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&gt;video&lt;/code&gt; tag and &lt;em&gt;unpack&lt;/em&gt; video frames from it by starting to &lt;code&gt;play&lt;/code&gt; the video and then listening to &lt;code&gt;timeupdate&lt;/code&gt; events at regular intervals on the&lt;code&gt;video&lt;/code&gt; element being fired as it’s being played, at which point we &lt;code&gt;pause&lt;/code&gt; the video and grab the current frame by painting the outcome on an &lt;code&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&gt;OffscreenCanvas&lt;/code&gt; for possible &lt;a href="https://developers.google.com/web/updates/2018/08/offscreen-canvas"&gt;performance benefits&lt;/a&gt; on top of a normal&lt;code&gt;canvas&lt;/code&gt; element, but that would work as well.&lt;/p&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="https://developer.mozilla.org/en-US/docs/Web/API/ImageBitmap"&gt;&lt;code&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&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&gt;canvas&lt;/code&gt; (as opposed to trying to encode and put them into &lt;code&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&gt;
  
  
  #3: video-seek-unpack-frames-canvas (&lt;a href="https://video-scrub.playground.ghosh.dev/video-seek-unpack-frames-canvas/"&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&gt;seek&lt;/code&gt; instead of &lt;code&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&gt;
  
  
  #4: video-seek-media-stream-image-capture (&lt;a href="https://video-scrub.playground.ghosh.dev/video-seek-media-stream-image-capture/"&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&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="https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/captureStream"&gt;&lt;code&gt;captureStream()&lt;/code&gt;&lt;/a&gt;on the &lt;code&gt;video&lt;/code&gt; element to capture the video stream and then we use the captured stream’s&lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/ImageCapture"&gt;&lt;code&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="https://developer.mozilla.org/en-US/docs/Web/API/MediaStream/MediaStream"&gt;&lt;code&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&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&gt;
  
  
  #5: video-server-frames (&lt;a href="https://video-scrub.playground.ghosh.dev/video-server-frames/"&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="https://www.apple.com/in/airpods-pro/"&gt;Apple Airpods Pro&lt;/a&gt; example above. Other people have tried to&lt;a href="https://canvas.narative.now.sh/"&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="https://www.freecodecamp.org/news/dont-do-it-at-runtime-do-it-at-design-time-c4f59d1775e4/"&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&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="https://webassembly.github.io/"&gt;WebAssembly&lt;/a&gt; to have an in-browser&lt;a href="https://www.ffmpeg.org/"&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="https://github.com/Kagami/ffmpeg.js"&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&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;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&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="https://github.com/WICG/web-codecs/blob/master/explainer.md"&gt;WebCodecs&lt;/a&gt;?&lt;/p&gt;

&lt;h1&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="https://video-scrub.playground.ghosh.dev/frame-extract-tool/"&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;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>showdev</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Chrome Dev Summit 2019: Everything you need to know</title>
      <dc:creator>Abhishek Ghosh</dc:creator>
      <pubDate>Sat, 14 Dec 2019 00:00:00 +0000</pubDate>
      <link>https://forem.com/abhishekcghosh/chrome-dev-summit-2019-everything-you-need-to-know-5cf3</link>
      <guid>https://forem.com/abhishekcghosh/chrome-dev-summit-2019-everything-you-need-to-know-5cf3</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;"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;."&lt;/p&gt;


&lt;cite&gt;
- from &lt;a href="https://blog.chromium.org/2019/11/chrome-dev-summit-2019-elevating-web.html"&gt;
blog.chromium.org&lt;/a&gt;
&lt;/cite&gt;

&lt;/blockquote&gt;

&lt;p&gt;This would be the uber pitch for &lt;a href="https://developer.chrome.com/devsummit/"&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="https://www.youtube.com/watch?v=F1UP7wRCPH8&amp;amp;list=PLNYkxOF6rcIDA1uGhqy45bqlul0VcvKMr"&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&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;blockquote&gt;
&lt;p&gt;In their natural progression, browsers have become viable, full-fledged application runtimes.&lt;/p&gt;
&lt;/blockquote&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="https://en.wikipedia.org/wiki/Node.js"&gt;Technologies&lt;/a&gt; &lt;a href="https://en.wikipedia.org/wiki/Blink_layout_engine"&gt;arising&lt;/a&gt; out of browser engines have been used for a while to build &lt;a href="https://code.visualstudio.com/"&gt;massively successful desktop applications&lt;/a&gt;; browsers themselves have been used as the basis of building &lt;a href="https://en.wikipedia.org/wiki/Chrome_OS"&gt;entire operating system UI&lt;/a&gt; and even run &lt;a href="https://blog.mozilla.org/blog/2014/03/12/mozilla-and-epic-preview-unreal-engine-4-running-in-firefox/"&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;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--VHl9j2iT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.ghosh.dev/static/media/thanos-1.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--VHl9j2iT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.ghosh.dev/static/media/thanos-1.jpg" alt="I am inevitable" width="711" height="351"&gt;&lt;/a&gt;&lt;br&gt;
        Source: &lt;a href="https://imgflip.com/i/3j3vbb"&gt;imgflip.com&lt;/a&gt;&lt;br&gt;
    &lt;/p&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&gt;
  
  
  Project Fugu
&lt;/h2&gt;

&lt;p&gt;The goal of &lt;a href="https://www.chromium.org/teams/web-capabilities-fugu"&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;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Jmlp2_4L--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.ghosh.dev/static/media/thanos-2.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Jmlp2_4L--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.ghosh.dev/static/media/thanos-2.jpg" alt="They called me a madman" width="652" height="428"&gt;&lt;/a&gt;&lt;br&gt;
        Source: &lt;a href="https://imgflip.com/i/3j3vtu"&gt;imgflip.com&lt;/a&gt;&lt;br&gt;
    &lt;/p&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="https://developer.mozilla.org/en-US/docs/Web/API"&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="https://developer.mozilla.org/en-US/docs/Web/Progressive_web_apps"&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="https://developers.google.com/web/fundamentals/app-install-banners"&gt;installable&lt;/a&gt; or bringing &lt;a href="https://developers.google.com/web/fundamentals/push-notifications"&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;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--uBCtT3Ub--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.ghosh.dev/static/media/fugu-logo-1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--uBCtT3Ub--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.ghosh.dev/static/media/fugu-logo-1.png" alt="Project Fugu" width="300" height="244"&gt;&lt;/a&gt;&lt;br&gt;
        Source: &lt;a href="https://slides.com/mhadaily/hardware-connectivity-on-pwa#/0/7"&gt;slides.com&lt;/a&gt;&lt;br&gt;
    &lt;/p&gt;

&lt;p&gt;In case you’ve been wondering, the word “Fugu” is Japanese for &lt;a href="https://en.wikipedia.org/wiki/Tetraodontidae"&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="https://web.dev/native-file-system/"&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="https://web.dev/contact-picker/"&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="https://web.dev/web-share/"&gt;Web Share&lt;/a&gt; and &lt;a href="https://web.dev/web-share-target/"&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="https://web.dev/sms-receiver-api-announcement/"&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="https://developers.google.com/web/updates/2018/05/webauthn"&gt;WebAuthN&lt;/a&gt; to let web apps access hardware tokens (eg. &lt;a href="https://en.wikipedia.org/wiki/YubiKey"&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="https://web.dev/get-installed-related-apps/"&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="https://web.dev/periodic-background-sync/"&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="https://web.dev/shape-detection/"&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="https://web.dev/badging-api/"&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="https://web.dev/wakelock/"&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="https://www.chromestatus.com/feature/5133150283890688"&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="https://github.com/GoogleChrome/OriginTrials/blob/gh-pages/explainer.md"&gt;Origin Trials&lt;/a&gt; or targeted to be built in the future. Here is an open &lt;a href="https://goo.gle/fugu-api-tracker"&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;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--VAFwAdLH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.ghosh.dev/static/media/fugu-process-1.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--VAFwAdLH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.ghosh.dev/static/media/fugu-process-1.jpg" alt="Project Fugu process" width="880" height="348"&gt;&lt;/a&gt;&lt;br&gt;
        Source: &lt;a href="https://developers.google.com/web/updates/capabilities#process"&gt;developer.google.com&lt;/a&gt;&lt;br&gt;
    &lt;/p&gt;

&lt;p&gt;Also worth noting, &lt;a href="https://webwewant.fyi/"&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&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="https://developers.google.com/web/showcase/tags/progressive-web-apps"&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;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--LGV24mJ3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.ghosh.dev/static/media/twa-logo-1.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--LGV24mJ3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.ghosh.dev/static/media/twa-logo-1.jpg" alt="TWA" width="557" height="500"&gt;&lt;/a&gt;&lt;br&gt;
        Source: &lt;a href="https://medium.com/@firt/google-play-store-now-open-for-progressive-web-apps-ec6f3c6ff3cc"&gt;medium.com&lt;/a&gt;&lt;br&gt;
    &lt;/p&gt;

&lt;p&gt;A key development for installable web apps has been the emergence of &lt;a href="https://developers.google.com/web/updates/2019/02/using-twa"&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="https://developer.chrome.com/multidevice/android/customtabs"&gt;Chrome Custom Tabs&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To quote from the &lt;a href="https://blog.chromium.org/2019/02/introducing-trusted-web-activity-for.html"&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="https://developers.google.com/web/fundamentals/push-notifications/"&gt;web push notifications&lt;/a&gt;, &lt;a href="https://developers.google.com/web/updates/2015/12/background-sync"&gt;background sync&lt;/a&gt;, &lt;a href="https://support.google.com/chrome/answer/142893?co=GENIE.Platform%3DDesktop&amp;amp;hl=en"&gt;form autofill&lt;/a&gt;, &lt;a href="https://www.w3.org/TR/media-source/"&gt;media source extensions&lt;/a&gt; and the &lt;a href="https://developers.google.com/web/updates/2016/09/navigator-share"&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="https://youtu.be/Hp_dQvQyYEI?list=PLNYkxOF6rcIDA1uGhqy45bqlul0VcvKMr&amp;amp;t=1516"&gt;showcase&lt;/a&gt; on OYO.&lt;/p&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;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="https://www.w3.org/TR/appmanifest/#shortcuts-member"&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="https://www.youtube.com/watch?v=Hp_dQvQyYEI&amp;amp;feature=youtu.be&amp;amp;list=PLNYkxOF6rcIDA1uGhqy45bqlul0VcvKMr&amp;amp;t=82"&gt;“Add to Homescreen” verbiage&lt;/a&gt; and it might probably simply be called “Install” in the future.&lt;/p&gt;

&lt;h1&gt;
  
  
  Because Performance. Obviously!
&lt;/h1&gt;

&lt;h2&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="https://developer.mozilla.org/en-US/docs/Web/API/Network_Information_API"&gt;Network Information API&lt;/a&gt; that informs effective connection type, downlink speed, RTT or data-saver information, &lt;a href="https://developers.google.com/web/updates/2017/12/device-memory"&gt;DeviceMemory API&lt;/a&gt;, CPU &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/NavigatorConcurrentHardware/hardwareConcurrency"&gt;HardwareConcurrency API&lt;/a&gt; and a mechanism to communicate such information as &lt;a href="https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/client-hints"&gt;Client-Hints&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--7tSYIRLn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.ghosh.dev/static/media/adaptive-loading-1.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--7tSYIRLn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.ghosh.dev/static/media/adaptive-loading-1.jpg" alt="Adaptive Loading" width="600" height="338"&gt;&lt;/a&gt;&lt;br&gt;
        Source: &lt;a href="https://youtu.be/puUPpVrIRkc?list=PLNYkxOF6rcIDA1uGhqy45bqlul0VcvKMr&amp;amp;t=454"&gt;Adaptive Loading - improving web performance on slow devices&lt;/a&gt;&lt;br&gt;
    &lt;/p&gt;

&lt;p&gt;Facebook &lt;a href="https://youtu.be/puUPpVrIRkc?list=PLNYkxOF6rcIDA1uGhqy45bqlul0VcvKMr&amp;amp;t=1438"&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="https://github.com/GoogleChromeLabs/react-adaptive-hooks"&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&gt;
  
  
  Better to hang by more than a thread.
&lt;/h2&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;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;amp; Javascript; and all the successive work required to be able to eventually &lt;a href="https://developers.google.com/web/fundamentals/performance/rendering"&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="https://www.afasterweb.com/2015/08/29/what-the-jank/"&gt;jank&lt;/a&gt; and feel sluggish.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--MBORpauF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.ghosh.dev/static/media/main-thread-1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--MBORpauF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.ghosh.dev/static/media/main-thread-1.png" alt="Pixel pipeline" width="600" height="333"&gt;&lt;/a&gt;&lt;br&gt;
        Source: &lt;a href="https://youtu.be/7Rrv9qFMWNM?list=PLNYkxOF6rcIDA1uGhqy45bqlul0VcvKMr&amp;amp;t=485"&gt;The main thread is overworked &amp;amp; underpaid&lt;/a&gt;&lt;br&gt;
    &lt;/p&gt;

&lt;p&gt;Patterns to effectively distribute the amount of client-side computational work across multiple threads by leveraging &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers"&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;amp; 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="http://npm.im/comlink"&gt;Comlink&lt;/a&gt; can help a lot to that end.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://proxx.app/"&gt;Proxx.app&lt;/a&gt; is a great example to look at all of this in action.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lighthouse shines brighter!
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--9xZ-FsFX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.ghosh.dev/static/media/lighthouse-logo-1.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--9xZ-FsFX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.ghosh.dev/static/media/lighthouse-logo-1.jpg" alt="Lighthouse" width="299" height="168"&gt;&lt;/a&gt;&lt;br&gt;
        Source: &lt;a href="https://developers.google.com/web/tools/lighthouse/"&gt;google.com&lt;/a&gt;&lt;br&gt;
    &lt;/p&gt;

&lt;p&gt;Not to throw any shade at &lt;a href="https://developers.google.com/web/tools/lighthouse"&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="https://github.com/GoogleChrome/lighthouse-ci"&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="https://github.com/GoogleChrome/lighthouse-stack-packs"&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="https://github.com/GoogleChrome/lighthouse/blob/master/docs/plugins.md"&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&gt;
  
  
  … and comes with new Performance Metrics.
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--d6cjUQe1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.ghosh.dev/static/media/lighthouse-scores-1.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--d6cjUQe1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.ghosh.dev/static/media/lighthouse-scores-1.jpg" alt="Lighthouse scores" width="880" height="252"&gt;&lt;/a&gt;&lt;br&gt;
        Source: &lt;a href="https://youtu.be/iaWLXf1FgI0?list=PLNYkxOF6rcIDA1uGhqy45bqlul0VcvKMr&amp;amp;t=509"&gt;Speed tooling evolutions: 2019 and beyond&lt;/a&gt;&lt;br&gt;
    &lt;/p&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="https://web.dev/lcp/"&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="https://web.dev/lighthouse-total-blocking-time/"&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="https://web.dev/long-tasks-devtools"&gt;long tasks&lt;/a&gt; between &lt;a href="https://web.dev/first-contentful-paint/"&gt;First Contentful Paint&lt;/a&gt; and &lt;a href="https://web.dev/interactive/"&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="https://web.dev/cls/"&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="https://developers.google.com/web/updates/2018/07/page-lifecycle-api"&gt;lifecycle state&lt;/a&gt; changes to hidden. Layout shifts are defined by the &lt;a href="https://github.com/WICG/layout-instability"&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="https://developer.mozilla.org/en-US/docs/Web/CSS/writing-mode"&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="https://web.dev/first-meaningful-paint/"&gt;First Meaningful Paint&lt;/a&gt;(FMP) and &lt;a href="https://web.dev/first-cpu-idle/"&gt;First CPU Idle&lt;/a&gt; (FCI).&lt;/p&gt;

&lt;h2&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;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--aPwO0mc7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.ghosh.dev/static/media/visual-slow-fast-1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--aPwO0mc7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.ghosh.dev/static/media/visual-slow-fast-1.png" alt="Visual indicators for slow vs. fast websites" width="600" height="491"&gt;&lt;/a&gt;&lt;br&gt;
        Source: &lt;a href="https://blog.chromium.org/2019/11/moving-towards-faster-web.html"&gt;Moving towards a faster web&lt;/a&gt;&lt;br&gt;
    &lt;/p&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&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;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;p&gt;The prime example of this was a partnership with &lt;a href="https://nextjs.org/"&gt;Next.js&lt;/a&gt;, a popular web framework based on &lt;a href="https://reactjs.org/"&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="https://github.com/babel/preset-modules"&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="https://opencollective.com/chrome"&gt;Framework Fund&lt;/a&gt; run by Chrome has also been announced multiple times across this CDS.&lt;/p&gt;

&lt;h2&gt;
  
  
  How awesome is WebAssembly now?
&lt;/h2&gt;

&lt;p&gt;Spoiler alert: pretty awesome! 💯&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--6O-NdHcU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.ghosh.dev/static/media/wasm-logo-1.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--6O-NdHcU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.ghosh.dev/static/media/wasm-logo-1.svg" alt="WebAssembly" width="612" height="612"&gt;&lt;/a&gt;&lt;br&gt;
        Source: &lt;a href="https://commons.wikimedia.org/wiki/File:Web_Assembly_Logo.svg"&gt;Wikimedia Commons&lt;/a&gt;&lt;br&gt;
    &lt;/p&gt;

&lt;p&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/WebAssembly"&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="https://dzone.com/articles/webassembly-caching-when-using-emscripten"&gt;implicit caching&lt;/a&gt;, introduction of support for &lt;a href="https://developers.google.com/web/updates/2018/10/wasm-threads"&gt;threads&lt;/a&gt;, and &lt;a href="https://www.chromestatus.com/feature/6533147810332672"&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="https://riju.github.io/WebCamera/samples/"&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&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="https://knowyourmeme.com/memes/the-cake-is-a-lie"&gt;reference&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--czACHV9F--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.ghosh.dev/static/media/portals-logo-1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--czACHV9F--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.ghosh.dev/static/media/portals-logo-1.png" alt="Portals" width="300" height="300"&gt;&lt;/a&gt;&lt;br&gt;
        Source: &lt;a href="https://dev.to/tomayac/hands-on-with-portals-seamless-navigation-on-the-web-4ho0-temp-slug-198334"&gt;web.dev&lt;/a&gt;&lt;br&gt;
    &lt;/p&gt;

&lt;p&gt;One of the unique new web platform capabilities that garnered a lot of interest was &lt;a href="https://dev.to/tomayac/hands-on-with-portals-seamless-navigation-on-the-web-4ho0-temp-slug-198334"&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="https://www.youtube.com/watch?v=X2zqwMBBvIs&amp;amp;feature=youtu.be&amp;amp;list=PLNYkxOF6rcIDA1uGhqy45bqlul0VcvKMr&amp;amp;t=956"&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&gt;
  
  
  Web Bundles
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--f9R9U0q8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.ghosh.dev/static/media/web-bundles-logo-1.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--f9R9U0q8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.ghosh.dev/static/media/web-bundles-logo-1.jpg" alt="Web Bundles" width="300" height="300"&gt;&lt;/a&gt;&lt;br&gt;
        Source: &lt;a href="https://web.dev/web-bundles/"&gt;web.dev&lt;/a&gt;&lt;br&gt;
    &lt;/p&gt;

&lt;p&gt;In the simplest terms, a &lt;a href="https://web.dev/web-bundles/"&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="https://wicg.github.io/webpackage/draft-yasskin-wpack-bundled-exchanges.html"&gt;Bundled HTTP Exchanges&lt;/a&gt;, it’s part of the &lt;a href="https://github.com/WICG/webpackage"&gt;Web Packaging&lt;/a&gt; proposal (and as someone wise would say, not to be confused with &lt;a href="https://webpack.js.org/"&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&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;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;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="https://play.google.com/store/apps/details?id=com.lenovo.anyshare.gps&amp;amp;hl=en_IN"&gt;Share-It&lt;/a&gt; or &lt;a href="https://play.google.com/store/apps/details?id=cn.xender&amp;amp;hl=en_IN"&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="https://en.wikipedia.org/wiki/MHTML"&gt;MHTML&lt;/a&gt; file format (&lt;code&gt;.mhtml&lt;/code&gt; or &lt;code&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="https://en.wikipedia.org/wiki/MHTML#Browser_support"&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="https://developers.google.com/web/updates/2018/11/signed-exchanges"&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="https://www.youtube.com/watch?v=4fndeDfaWCg"&gt;Backstreet Boys&lt;/a&gt; again from those days! Hashtag Nostalgia.&lt;/p&gt;

&lt;h1&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&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="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Scroll_Snap"&gt;&lt;code&gt;scroll-snap&lt;/code&gt;&lt;/a&gt; that introduces scroll snap positions, which enforce the scroll positions that a &lt;a href="https://developer.mozilla.org/en-US/docs/Glossary/scroll_container"&gt;scroll container’s&lt;/a&gt; &lt;a href="https://developer.mozilla.org/en-US/docs/Glossary/scrollport"&gt;scrollport&lt;/a&gt; may end at after a scrolling operation has completed.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/:focus-within"&gt;&lt;code&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&gt;@media (prefers-*)&lt;/code&gt; queries, namely the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme"&gt;&lt;code&gt;prefers-color-scheme&lt;/code&gt;&lt;/a&gt;, &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-contrast"&gt;&lt;code&gt;prefers-contrast&lt;/code&gt;&lt;/a&gt;, &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-reduced-motion"&gt;&lt;code&gt;prefers-reduced-motion&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-reduced-transparency"&gt;&lt;code&gt;prefers-reduced-transparency&lt;/code&gt;&lt;/a&gt;, which in &lt;a href="https://youtu.be/-oyeaIirVC0?list=PLNYkxOF6rcIDA1uGhqy45bqlul0VcvKMr&amp;amp;t=396"&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="https://css-tricks.com/position-sticky-2/"&gt;&lt;code&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&gt;relative&lt;/code&gt; positioned until it crosses a specified threshold, at which point it is treated as &lt;code&gt;fixed&lt;/code&gt; positioned.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Logical_Properties"&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&gt;ltr&lt;/code&gt;, &lt;code&gt;rtl&lt;/code&gt; or &lt;code&gt;vertical-rl&lt;/code&gt; content. Here’s a &lt;a href="https://webdesign.tutsplus.com/tutorials/how-to-use-css-logical-properties--cms-33024"&gt;nice article&lt;/a&gt; that talks about it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Hq0m1tFQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.ghosh.dev/static/media/css-logical-properties-1.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Hq0m1tFQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.ghosh.dev/static/media/css-logical-properties-1.jpg" alt="CSS Logical Properties" width="800" height="267"&gt;&lt;/a&gt;&lt;br&gt;
        Source: &lt;a href="https://www.youtube.com/watch?v=-oyeaIirVC0&amp;amp;list=PLNYkxOF6rcIDA1uGhqy45bqlul0VcvKMr&amp;amp;index=2"&gt;Next-generation web styling&lt;/a&gt;&lt;br&gt;
    &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://css-tricks.com/almanac/selectors/i/is/"&gt;&lt;code&gt;:is()&lt;/code&gt; selector&lt;/a&gt;, the new name for the &lt;a href="https://www.w3.org/TR/selectors-4/#matches"&gt;Matches-Any Pseudo-class&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/backdrop-filter"&gt;&lt;code&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&gt;
  
  
  Houdini
&lt;/h2&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;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="https://developer.mozilla.org/en-US/docs/Web/Houdini"&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="https://developer.mozilla.org/en-US/docs/Web/API/CSS_Object_Model"&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="https://www.smashingmagazine.com/2016/03/houdini-maybe-the-most-exciting-development-in-css-youve-never-heard-of/"&gt;other&lt;/a&gt; &lt;a href="https://www.qed42.com/blog/building-powerful-custom-properties-CSS-houdini"&gt;great&lt;/a&gt; &lt;a href="https://medium.com/front-end-field-guide/how-to-be-houdini-and-escape-the-limits-of-css-460af7307d41"&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="https://developers.google.com/web/updates/2018/01/paintapi"&gt;Paint API&lt;/a&gt;, &lt;a href="https://developers.google.com/web/updates/2018/03/cssom"&gt;Typed OM&lt;/a&gt; and &lt;a href="https://web.dev/css-props-and-vals/"&gt;Properties and Values API&lt;/a&gt; have shipped to Chrome. &lt;a href="https://github.com/w3c/css-houdini-drafts/blob/master/css-layout-api/EXPLAINER.md"&gt;Layout API&lt;/a&gt; is in Canary.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--3M2VxsNU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.ghosh.dev/static/media/css-houdini-1.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--3M2VxsNU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.ghosh.dev/static/media/css-houdini-1.jpg" alt="CSS Houdini" width="800" height="541"&gt;&lt;/a&gt;&lt;br&gt;
        Source: &lt;a href="https://youtu.be/-oyeaIirVC0?list=PLNYkxOF6rcIDA1uGhqy45bqlul0VcvKMr&amp;amp;t=1258"&gt;Next-generation web styling&lt;/a&gt;&lt;br&gt;
    &lt;/p&gt;

&lt;p&gt;Check out the cool &lt;a href="https://houdini.glitch.me/"&gt;Houdini Spellbook&lt;/a&gt; or the &lt;a href="https://ishoudinireadyyet.com/"&gt;ishoudinireadyyet.com&lt;/a&gt; website for more.&lt;/p&gt;

&lt;h1&gt;
  
  
  Showing some ❤ to HTML
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Form elements
&lt;/h2&gt;

&lt;p&gt;In HTML, the visual redesign, accessibility upgrade and extensibility of &lt;a href="https://www.youtube.com/watch?v=ZFvPLrKZywA&amp;amp;list=PLNYkxOF6rcIDA1uGhqy45bqlul0VcvKMr&amp;amp;index=5"&gt;form elements&lt;/a&gt; such as &lt;code&gt;&amp;lt;select&amp;gt;&lt;/code&gt; or &lt;code&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&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="https://github.com/WICG/virtual-scroller"&gt;Virtual Scrolling&lt;/a&gt;, and one of the possible prototypes is a new web platform primitive called &lt;a href="https://github.com/WICG/display-locking"&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;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--PjONxsTy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.ghosh.dev/static/media/display-locking-1.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--PjONxsTy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.ghosh.dev/static/media/display-locking-1.jpg" alt="Display Locking" width="700" height="428"&gt;&lt;/a&gt;&lt;br&gt;
        Source: &lt;a href="https://www.youtube.com/watch?v=ZFvPLrKZywA&amp;amp;list=PLNYkxOF6rcIDA1uGhqy45bqlul0VcvKMr&amp;amp;index=5"&gt;HTML isn’t done!&lt;/a&gt;&lt;br&gt;
    &lt;/p&gt;

&lt;p&gt;To quote from Nicole Sullivan from the &lt;a href="https://youtu.be/ZFvPLrKZywA?list=PLNYkxOF6rcIDA1uGhqy45bqlul0VcvKMr&amp;amp;t=1100"&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&gt;
  
  
  Safety and Privacy
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Visual updates to URL display
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--FyAfqJ_1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.ghosh.dev/static/media/privacy-url-1.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--FyAfqJ_1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.ghosh.dev/static/media/privacy-url-1.jpg" alt="URL security" width="300" height="190"&gt;&lt;/a&gt;&lt;br&gt;
        Source: &lt;a href="https://youtu.be/WnCKlNE52tc?list=PLNYkxOF6rcIDA1uGhqy45bqlul0VcvKMr&amp;amp;t=146"&gt;Protecting users on a thriving web&lt;/a&gt;&lt;br&gt;
    &lt;/p&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="https://ai.google/research/pubs/pub48199"&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="https://en.wikipedia.org/wiki/Extended_Validation_Certificate"&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;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--K7HsfkRX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.ghosh.dev/static/media/privacy-url-2.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--K7HsfkRX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.ghosh.dev/static/media/privacy-url-2.jpg" alt="URL security" width="300" height="188"&gt;&lt;/a&gt;&lt;br&gt;
        Source: &lt;a href="https://youtu.be/WnCKlNE52tc?list=PLNYkxOF6rcIDA1uGhqy45bqlul0VcvKMr&amp;amp;t=637"&gt;Protecting users on a thriving web&lt;/a&gt;&lt;br&gt;
    &lt;/p&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="https://publications.sba-research.org/publications/2019-Pfeffer-HTTPS_Mental_Models.pdf"&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="https://chrome.google.com/webstore/detail/suspicious-site-reporter/jknemblkbdhdcpllfgbfekkdciegfboi"&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&gt;
  
  
  Combating sophisticated spoofing attacks
&lt;/h2&gt;

&lt;p&gt;Sophisticated spoofing attacks like &lt;a href="https://en.wikipedia.org/wiki/IDN_homograph_attack"&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;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--QzTMfgF3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.ghosh.dev/static/media/idn-attack-1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--QzTMfgF3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.ghosh.dev/static/media/idn-attack-1.png" alt="IDN spoofing" width="700" height="394"&gt;&lt;/a&gt;&lt;br&gt;
        Source: &lt;a href="https://youtu.be/WnCKlNE52tc?list=PLNYkxOF6rcIDA1uGhqy45bqlul0VcvKMr&amp;amp;t=274"&gt;Protecting users on a thriving web&lt;/a&gt;&lt;br&gt;
    &lt;/p&gt;

&lt;p&gt;Chrome is introducing what it calls &lt;a href="https://www.zdnet.com/article/google-chrome-to-get-warnings-for-lookalike-urls/"&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&gt;
  
  
  Committed to privacy, but with ads? 🤷‍♀️
&lt;/h2&gt;

&lt;p&gt;Following a brief stint on &lt;a href="https://youtu.be/WnCKlNE52tc?list=PLNYkxOF6rcIDA1uGhqy45bqlul0VcvKMr&amp;amp;t=1175"&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&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&gt;Secure&lt;/code&gt; attribute, this makes cookies only accessible over HTTPS connections. Read more about it on &lt;a href="https://blog.chromium.org/2019/10/developers-get-ready-for-new.html"&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="https://www.zdnet.com/article/firefox-to-add-tor-browser-anti-fingerprinting-technique-called-letterboxing/"&gt;committed&lt;/a&gt; &lt;a href="https://gizmodo.com/apple-declares-war-on-browser-fingerprinting-the-sneak-1826549108"&gt;interest&lt;/a&gt; in fighting against fingerprinting, all this seems arguably feeble. Then again, &lt;a href="https://www.eff.org/deeplinks/2019/08/dont-play-googles-privacy-sandbox-1"&gt;not that&lt;/a&gt; it has had a great track record.&lt;/p&gt;

&lt;h1&gt;
  
  
  Chrome Extensions
&lt;/h1&gt;

&lt;h2&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="https://developer.chrome.com/extensions/migrating_to_manifest_v3"&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="https://developer.chrome.com/extensions/migrating_to_service_workers"&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="https://blog.chromium.org/2019/06/web-request-and-declarative-net-request.html"&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="https://chrome.google.com/webstore/detail/adblock-%E2%80%94-best-ad-blocker/gighmmpiobklfepjocnamgkkbiglidom"&gt;AdBlock&lt;/a&gt;) and provide more restrictive and performant models to achieve the same.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--PBJlE_yS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.ghosh.dev/static/media/chrome-ext-1.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--PBJlE_yS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.ghosh.dev/static/media/chrome-ext-1.jpg" alt="Chrome Extensions: declarative net request" width="700" height="193"&gt;&lt;/a&gt;&lt;br&gt;
        Source: &lt;a href="https://youtu.be/7-ALJiZCI6w?list=PLNYkxOF6rcIDA1uGhqy45bqlul0VcvKMr&amp;amp;t=1630"&gt;Chrome extensions and the world of tomorrow&lt;/a&gt;&lt;br&gt;
    &lt;/p&gt;

&lt;h1&gt;
  
  
  SEO
&lt;/h1&gt;

&lt;p&gt;Google &lt;a href="https://www.youtube.com/watch?v=4pOH8Smd0Xs&amp;amp;list=PLNYkxOF6rcIDA1uGhqy45bqlul0VcvKMr&amp;amp;index=6"&gt;announced&lt;/a&gt; that the Googlebot crawler user-agent would be updated soon to reflect the &lt;a href="https://webmasters.googleblog.com/2019/05/the-new-evergreen-googlebot.html"&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;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--cm_fzAFf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.ghosh.dev/static/media/googlebot-1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--cm_fzAFf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.ghosh.dev/static/media/googlebot-1.png" alt="Googlebot user-agent" width="700" height="212"&gt;&lt;/a&gt;&lt;br&gt;
        Source: &lt;a href="https://www.youtube.com/watch?v=4pOH8Smd0Xs&amp;amp;list=PLNYkxOF6rcIDA1uGhqy45bqlul0VcvKMr&amp;amp;index=6"&gt;How to make your content shine on Google Search&lt;/a&gt;&lt;br&gt;
    &lt;/p&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="https://developer.mozilla.org/en-US/docs/Web/Web_Components"&gt;web components&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Also, going ahead, images set as &lt;code&gt;background-image&lt;/code&gt; URLs may not be indexed.&lt;/p&gt;

&lt;h1&gt;
  
  
  Other stuff to call out…
&lt;/h1&gt;

&lt;h2&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="https://www.youtube.com/watch?v=y3EZx_b-7tk&amp;amp;list=PLNYkxOF6rcIDA1uGhqy45bqlul0VcvKMr&amp;amp;index=17"&gt;video&lt;/a&gt; and &lt;a href="https://blog.chromium.org/2019/11/intent-to-explain-demystifying-blink.html"&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="http://www.chromium.org/blink"&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&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="https://github.com/GoogleChrome/OriginTrials/blob/gh-pages/explainer.md"&gt;explainer&lt;/a&gt;, &lt;a href="https://github.com/GoogleChrome/OriginTrials/blob/gh-pages/developer-guide.md"&gt;developer guide&lt;/a&gt; or &lt;a href="https://www.chromium.org/blink/origin-trials/running-an-origin-trial"&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&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="https://github.com/rayankans/content-index"&gt;proposal&lt;/a&gt; to learn more.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;That’s all folks!&lt;/em&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>webperf</category>
      <category>techtalks</category>
      <category>webplatform</category>
    </item>
    <item>
      <title>Creating a multi-level hierarchical flyout navigation menu using only HTML and CSS</title>
      <dc:creator>Abhishek Ghosh</dc:creator>
      <pubDate>Sun, 07 Jul 2019 00:00:00 +0000</pubDate>
      <link>https://forem.com/abhishekcghosh/creating-a-multi-level-hierarchical-flyout-navigation-menu-using-only-html-and-css-33ke</link>
      <guid>https://forem.com/abhishekcghosh/creating-a-multi-level-hierarchical-flyout-navigation-menu-using-only-html-and-css-33ke</guid>
      <description>&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 animation on how that would look like:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--eu8Yp0Y3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://i.ibb.co/TrrSZ9Z/css-nav-menu-3.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--eu8Yp0Y3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://i.ibb.co/TrrSZ9Z/css-nav-menu-3.gif" alt="Css nav menu" width="600" height="450"&gt;&lt;/a&gt;&lt;/p&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;p&gt;&lt;em&gt;Impatient? Scroll down below to the Codepen embeds!&lt;/em&gt;&lt;/p&gt;

&lt;h3&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 &amp;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&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&gt;ul&lt;/code&gt; element, with individual items being the obvious &lt;code&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&gt;span&lt;/code&gt; elements with their corresponding CSS classes (&lt;code&gt;label&lt;/code&gt; or &lt;code&gt;shortcut&lt;/code&gt;) inside an anchor (&lt;code&gt;a&lt;/code&gt;) tag inside the &lt;code&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&gt;ul&lt;/code&gt; element inside the current menu &lt;code&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&gt;has-children&lt;/code&gt; CSS class to this parent &lt;code&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&gt;separator&lt;/code&gt; to the &lt;code&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&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&gt;nav&lt;/code&gt; element (it’s good to be &lt;a href="https://en.wikipedia.org/wiki/Semantic_HTML"&gt;semantic&lt;/a&gt;) and add the &lt;code&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="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;nav&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"flyout-nav"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;ul&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;li&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"#"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"label"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;File&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;ul&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;li&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"#"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"label"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;New Tab&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"shortcut"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;⌘T&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;li&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"#"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"label"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;New Window&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"shortcut"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;⌘N&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;li&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"separator"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;li&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"has-children"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"#"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"label"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Share...&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;ul&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;li&amp;gt;&lt;/span&gt;
                            &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"#"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                                &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"label"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;✉️ Email&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
                            &lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;li&amp;gt;&lt;/span&gt;
                            &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"#"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                                &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"label"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;💬 Messages&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
                            &lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;/ul&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/ul&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/ul&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/nav&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Adding behaviours in CSS
&lt;/h3&gt;

&lt;p&gt;I lied. We’ll use &lt;a href="https://sass-lang.com/"&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="https://developer.mozilla.org/en-US/docs/Web/CSS/:hover"&gt;&lt;code&gt;hover&lt;/code&gt; pseudo-class&lt;/a&gt; for this.&lt;/p&gt;

&lt;h5&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="https://developer.mozilla.org/en-US/docs/Web/CSS/position"&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&gt;ul&lt;/code&gt; element inside a “parent” &lt;code&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&gt;ul&lt;/code&gt; and &lt;code&gt;li&lt;/code&gt; elements.&lt;/p&gt;

&lt;p&gt;For doing this, we’ll use a combination of &lt;code&gt;absolute&lt;/code&gt; positioning and &lt;code&gt;top&lt;/code&gt;, &lt;code&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="https://developer.mozilla.org/en-US/docs/Web/CSS/Containing_block"&gt;containing block&lt;/a&gt;. By non-static, we mean that the CSS position property for an element is not &lt;code&gt;static&lt;/code&gt; (which happens by default in the HTML document flow), but instead is one of &lt;code&gt;relative&lt;/code&gt;, &lt;code&gt;absolute&lt;/code&gt;, &lt;code&gt;fixed&lt;/code&gt; or &lt;code&gt;sticky&lt;/code&gt;. To make sure of that, we’ll assign the position &lt;code&gt;relative&lt;/code&gt; to the &lt;code&gt;li&lt;/code&gt; elements with their child &lt;code&gt;ul&lt;/code&gt; elements positioned &lt;code&gt;absolute&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scss"&gt;&lt;code&gt;&lt;span class="nc"&gt;.flyout-nav&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// list of menu items at any level&lt;/span&gt;
    &lt;span class="nt"&gt;ul&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nl"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nl"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;absolute&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nl"&gt;list-style-type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// a menu item&lt;/span&gt;
    &lt;span class="nt"&gt;li&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nl"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;relative&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;block&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="c1"&gt;// show the next level drop-down on&lt;/span&gt;
        &lt;span class="c1"&gt;// the right at the same height&lt;/span&gt;
        &lt;span class="k"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nd"&gt;:hover&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nt"&gt;ul&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;block&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="nl"&gt;top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="nl"&gt;left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&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;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--z1XeU2jM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i.ibb.co/T8kTCjs/css-nav-menu-4.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--z1XeU2jM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i.ibb.co/T8kTCjs/css-nav-menu-4.jpg" alt="Sub-menu positioning" width="600" height="361"&gt;&lt;/a&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;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scss"&gt;&lt;code&gt;&lt;span class="nc"&gt;.flyout-nav&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// ... other stuff&lt;/span&gt;

    &lt;span class="c1"&gt;// overrides for first-level behaviour (horizontal bar)&lt;/span&gt;
    &lt;span class="k"&gt;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nt"&gt;ul&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nl"&gt;flex-flow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="nb"&gt;nowrap&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nl"&gt;justify-content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;flex-start&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nl"&gt;align-items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;stretch&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="c1"&gt;// first-level drop-down should appear&lt;/span&gt;
        &lt;span class="c1"&gt;// below at the same left position&lt;/span&gt;
        &lt;span class="k"&gt;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nt"&gt;li&lt;/span&gt;&lt;span class="nd"&gt;:hover&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nt"&gt;ul&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nl"&gt;top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&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&gt;display: block&lt;/code&gt; and &lt;code&gt;display: inline-block&lt;/code&gt; on the &lt;code&gt;ul&lt;/code&gt; and &lt;code&gt;li&lt;/code&gt; items as well.&lt;/p&gt;

&lt;h5&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;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scss"&gt;&lt;code&gt;&lt;span class="c1"&gt;// variables&lt;/span&gt;
&lt;span class="nv"&gt;$page-bg&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mh"&gt;#607d8b&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nv"&gt;$base-font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;16px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// becomes 1rem&lt;/span&gt;
&lt;span class="nv"&gt;$menu-silver&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mh"&gt;#eee&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nv"&gt;$menu-border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mh"&gt;#dedede&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nv"&gt;$menu-focused&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mh"&gt;#1e88e5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nv"&gt;$menu-separator&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mh"&gt;#ccc&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nv"&gt;$menu-text-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mh"&gt;#333&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nv"&gt;$menu-shortcut-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mh"&gt;#999&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nv"&gt;$menu-focused-text-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mh"&gt;#fff&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nv"&gt;$menu-text-color-disabled&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mh"&gt;#999&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nv"&gt;$menu-border-width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nv"&gt;$menu-shadow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2px&lt;/span&gt; &lt;span class="m"&gt;2px&lt;/span&gt; &lt;span class="m"&gt;3px&lt;/span&gt; &lt;span class="m"&gt;-3px&lt;/span&gt; &lt;span class="nv"&gt;$menu-text-color&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nv"&gt;$menu-content-padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="mi"&gt;.5rem&lt;/span&gt; &lt;span class="m"&gt;1rem&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="mi"&gt;.5rem&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="mi"&gt;.75rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nv"&gt;$menu-border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="mi"&gt;.5rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nv"&gt;$menu-top-padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="mi"&gt;.25rem&lt;/span&gt;&lt;span class="p"&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&gt;
  
  
  Anchors, Labels and Shortcuts - the actual visual elements
&lt;/h5&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scss"&gt;&lt;code&gt;&lt;span class="nc"&gt;.flyout-nav&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// ... other stuff&lt;/span&gt;

    &lt;span class="nt"&gt;li&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// ... other stuff&lt;/span&gt;

        &lt;span class="c1"&gt;// the menu items - text, shortcut info and hover effect (blue bg)&lt;/span&gt;
        &lt;span class="nt"&gt;a&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nl"&gt;text-decoration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;$menu-text-color&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;relative&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;table&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

            &lt;span class="nc"&gt;.label&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
            &lt;span class="nc"&gt;.shortcut&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;table-cell&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;$menu-content-padding&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;

            &lt;span class="nc"&gt;.shortcut&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nl"&gt;text-align&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;right&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;$menu-shortcut-color&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;

            &lt;span class="nt"&gt;label&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nl"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;pointer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;

            &lt;span class="c1"&gt;// for menu items that are toggles&lt;/span&gt;
            &lt;span class="nt"&gt;input&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'checkbox'&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;

            &lt;span class="nt"&gt;input&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'checkbox'&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="nd"&gt;:checked&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nc"&gt;.label&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nd"&gt;::before&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="nl"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'✔️'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                    &lt;span class="nl"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;absolute&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                    &lt;span class="nl"&gt;top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                    &lt;span class="nl"&gt;left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="mi"&gt;.25rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                    &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="mi"&gt;.25rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;

            &lt;span class="k"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nd"&gt;:hover&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;$menu-focused&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="nc"&gt;.label&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                &lt;span class="nc"&gt;.shortcut&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;$menu-focused-text-color&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&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&gt;input[type='checkbox']&lt;/code&gt;?&lt;/p&gt;

&lt;h5&gt;
  
  
  Toggle Items
&lt;/h5&gt;

&lt;p&gt;For toggles, we use a hidden HTML &lt;code&gt;checkbox&lt;/code&gt; element to maintain state (on or off) and style the &lt;code&gt;label&lt;/code&gt; with &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/::before"&gt;&lt;code&gt;::before&lt;/code&gt; pseudo-element&lt;/a&gt; accordingly. We are able to do that using a simple CSS &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Adjacent_sibling_combinator"&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;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;li&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"#"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"checkbox"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"alwaysShowBookmarksBar"&lt;/span&gt; &lt;span class="na"&gt;checked=&lt;/span&gt;&lt;span class="s"&gt;"true"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;label&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"label"&lt;/span&gt; &lt;span class="na"&gt;for=&lt;/span&gt;&lt;span class="s"&gt;"alwaysShowBookmarksBar"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Always Show Bookmarks Bar&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"shortcut"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;⇧⌘B&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h5&gt;
  
  
  Separators
&lt;/h5&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scss"&gt;&lt;code&gt;&lt;span class="nc"&gt;.flyout-nav&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// ... other stuff&lt;/span&gt;

    &lt;span class="nt"&gt;li&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// ... other stuff&lt;/span&gt;

        &lt;span class="c1"&gt;// the separator item&lt;/span&gt;
        &lt;span class="k"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nc"&gt;.separator&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nl"&gt;margin-bottom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;$menu-top-padding&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;border-bottom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;$menu-border-width&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="nv"&gt;$menu-separator&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;padding-bottom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;$menu-top-padding&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h5&gt;
  
  
  Disabled
&lt;/h5&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scss"&gt;&lt;code&gt;&lt;span class="nc"&gt;.flyout-nav&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// ... other stuff&lt;/span&gt;

    &lt;span class="nt"&gt;li&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// ... other stuff&lt;/span&gt;

        &lt;span class="c1"&gt;// don't let disabled options respond to hover&lt;/span&gt;
        &lt;span class="c1"&gt;// or click and color them different&lt;/span&gt;
        &lt;span class="k"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nc"&gt;.disabled&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nc"&gt;.label&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
            &lt;span class="nc"&gt;.shortcut&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;$menu-text-color-disabled&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="nl"&gt;pointer-events&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;CSS &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/pointer-events"&gt;pointer-events&lt;/a&gt; does the actual trick here. Setting it to &lt;code&gt;none&lt;/code&gt; makes it invisible as a target for any pointer events.&lt;/p&gt;

&lt;h3&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;iframe height="600" src="https://codepen.io/abhishekcghosh/embed/WqjOaX?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h5&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;iframe height="600" src="https://codepen.io/abhishekcghosh/embed/qzmEWd?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h3&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. &lt;/p&gt;

&lt;p&gt;Cheers!&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>webdev</category>
      <category>css</category>
      <category>ui</category>
    </item>
    <item>
      <title>Starting a blog with a developer mindset in 2019</title>
      <dc:creator>Abhishek Ghosh</dc:creator>
      <pubDate>Tue, 18 Jun 2019 00:00:00 +0000</pubDate>
      <link>https://forem.com/abhishekcghosh/starting-a-blog-with-a-developer-mindset-in-2019-114m</link>
      <guid>https://forem.com/abhishekcghosh/starting-a-blog-with-a-developer-mindset-in-2019-114m</guid>
      <description>&lt;p&gt;In a &lt;a href="https://dev.to/abhishekcghosh/on-personal-blog-as-a-software-developer-ngm"&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;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="https://www.gatsbyjs.org/"&gt;Gatsby&lt;/a&gt; and hosted on &lt;a href="https://netlify.com"&gt;Netlify&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In my &lt;a href="https://www.ghosh.dev/posts/the-quintessential-hello-world"&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="https://dev.to/dceddia/start-a-blog-in-2019-with-gatsbyjs-and-netlify-3g92-temp-slug-9107789"&gt;great&lt;/a&gt;, &lt;a href="https://codeburst.io/build-a-blog-using-gatsby-js-react-8561bfe8fc91"&gt;many&lt;/a&gt;, &lt;a href="https://www.netlify.com/blog/2016/02/24/a-step-by-step-guide-gatsby-on-netlify/"&gt;resources&lt;/a&gt;, &lt;a href="https://www.freecodecamp.org/news/how-i-built-my-blog-using-gatsby-and-netlify-f921f1a9f33c/"&gt;all&lt;/a&gt;, &lt;a href="https://blog.logrocket.com/gatsby-netlify-cms-a-perfect-pairing-d50d59d16f67/"&gt;over&lt;/a&gt;, &lt;a href="https://dev.to/saigowthamr/build-a-blog-using-gatsby-netlify-cms-adi"&gt;the&lt;/a&gt;, &lt;a href="https://egghead.io/courses/build-a-blog-with-react-and-markdown-using-gatsby"&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;p&gt;&lt;em&gt;⚠️ Content below is bespoke and opinionated.&lt;/em&gt;&lt;/p&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;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;/li&gt;
&lt;li&gt;Creative freedom and customizability to establish identity and individuality.&lt;/li&gt;
&lt;li&gt;Technical freedom to do more under the same roof.&lt;/li&gt;
&lt;li&gt;Statically built website for speed, frugality and agility. With possible eco-friendliness sprinkled on top.&lt;/li&gt;
&lt;li&gt;Transparency, portability and version control of content&lt;/li&gt;
&lt;li&gt;The subliminal developer experience&lt;/li&gt;
&lt;li&gt;A pretext to learn new things and apply them&lt;/li&gt;
&lt;li&gt;But why Gatsby? Why Gatsby at all?&lt;/li&gt;
&lt;li&gt;So, is it all sunshine and rainbows?&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Self-owned content rather than hosted at somebody else’s mercy whom I’m probably paying again to serve content that I created.
&lt;/h4&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="https://medium.com"&gt;Medium&lt;/a&gt; or even &lt;a href="https://wordpress.com"&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;h4&gt;
  
  
  Creative freedom and customizability to establish identity and individuality.
&lt;/h4&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;h4&gt;
  
  
  Technical freedom to do more under the same roof.
&lt;/h4&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="https://en.wikipedia.org/wiki/Occam%27s_razor"&gt;Occam’s Razor!&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Statically built website for speed, frugality and agility. With possible eco-friendliness sprinkled on top.
&lt;/h4&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="https://jamstack.org/"&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&gt;data&lt;/code&gt; and &lt;code&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="https://www.freecodecamp.org/news/dont-do-it-at-runtime-do-it-at-design-time-c4f59d1775e4/"&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="https://en.wikipedia.org/wiki/Content_delivery_network"&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="https://medium.com/netlify/10-netlify-features-to-surprise-and-delight-225e846b7b21"&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="https://en.wikipedia.org/wiki/HTTPS"&gt;HTTPS&lt;/a&gt; from the CDN &lt;a href="https://en.wikipedia.org/wiki/Content_delivery_network#Technology"&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;h4&gt;
  
  
  Transparency, portability and version control of content
&lt;/h4&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="https://en.wikipedia.org/wiki/Service-level_agreement"&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="https://en.wikipedia.org/wiki/Markdown"&gt;Markdown&lt;/a&gt; and maintaining them on &lt;a href="https://github.com/"&gt;Github&lt;/a&gt; (or &lt;a href="https://about.gitlab.com/"&gt;GitLab&lt;/a&gt; or &lt;a href="https://bitbucket.org/"&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;h4&gt;
  
  
  The subliminal developer experience
&lt;/h4&gt;

&lt;p&gt;In continuation of the above theme, this is where I risk pissing off some great proponents of &lt;a href="https://en.wikipedia.org/wiki/Separation_of_concerns"&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;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Tq3x3GcI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i.ibb.co/myZWPss/meme-morpheus-1.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Tq3x3GcI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i.ibb.co/myZWPss/meme-morpheus-1.jpg" alt="What if I told you, it's okay" width="525" height="475"&gt;&lt;/a&gt;&lt;/p&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="https://www.netlify.com/docs/continuous-deployment/"&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="https://reactjs.org/"&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;h4&gt;
  
  
  A pretext to learn new things and apply them
&lt;/h4&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;h4&gt;
  
  
  But why Gatsby? Why Gatsby at all?
&lt;/h4&gt;

&lt;blockquote&gt;
&lt;p&gt;"I wasn't actually in love, but I felt a sort of tender curiosity."&lt;/p&gt;


&lt;cite&gt;— F. Scott Fitzgerald, The Great Gatsby&lt;/cite&gt;

&lt;/blockquote&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="https://www.gatsbyjs.org"&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="https://reactjs.org/"&gt;React&lt;/a&gt;, &lt;a href="https://graphql.org/"&gt;GraphQL&lt;/a&gt;. And a &lt;a href="https://www.gatsbyjs.org/docs/why-gatsby-uses-graphql/"&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="https://www.narative.co/articles/understanding-the-gatsby-lifecycle"&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="https://www.gatsbyjs.org/contributing/community/"&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="https://www.freecodecamp.org/news/how-gatsby-is-so-blazing-fast-c99a6f2d405e/"&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="https://webpack.js.org/"&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="https://www.gatsbyjs.org/plugins/"&gt;plugins&lt;/a&gt; and a rich set of &lt;a href="https://www.gatsbyjs.org/docs/"&gt;guidelines&lt;/a&gt;, &lt;a href="https://www.gatsbyjs.org/starters/"&gt;starter packs&lt;/a&gt; and &lt;a href="https://www.gatsbyjs.org/showcase/"&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="https://developers.google.com/web/progressive-web-apps/"&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="https://www.gatsbyjs.org/features/"&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="https://jekyllrb.com/"&gt;Jekyll&lt;/a&gt; or content management systems (CMS) like Wordpress or site-builders if you’re interested.&lt;/p&gt;

&lt;h4&gt;
  
  
  So, is it all sunshine and rainbows?
&lt;/h4&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="https://dev.to/"&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;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;

</description>
      <category>blogging</category>
      <category>beginners</category>
      <category>career</category>
      <category>developer</category>
    </item>
    <item>
      <title>On personal blog as a software developer</title>
      <dc:creator>Abhishek Ghosh</dc:creator>
      <pubDate>Sat, 01 Jun 2019 00:00:00 +0000</pubDate>
      <link>https://forem.com/abhishekcghosh/on-personal-blog-as-a-software-developer-ngm</link>
      <guid>https://forem.com/abhishekcghosh/on-personal-blog-as-a-software-developer-ngm</guid>
      <description>&lt;p&gt;&lt;em&gt;I have been setting up a blog for myself very recently. This is a cross-post and the first ever that I'm posting on &lt;a href="https://dev.to"&gt;dev.to&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;




&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;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;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Bb3XOLcA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.ghosh.dev/static/media/isaac-newton-pixabay-reuse.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Bb3XOLcA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.ghosh.dev/static/media/isaac-newton-pixabay-reuse.png" alt="https://pixabay.com/vectors/isaac-newton-portrait-vintage-3936704/"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;br&gt;
"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."

&lt;p&gt;"If I have seen further it is by standing on ye sholders of Giants"&lt;/p&gt;

&lt;p&gt;-- Isaac Newton&lt;br&gt;
&lt;/p&gt;


&lt;/blockquote&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="https://www.goodreads.com/quotes/30410-i-do-not-know-what-i-may-appear-to-the"&gt;such&lt;/a&gt; &lt;a href="https://www.goodreads.com/quotes/10286-if-i-have-seen-further-it-is-by-standing-on"&gt;sentiments&lt;/a&gt;. And it is not surprising to see &lt;a href="https://www.google.com/search?q=imposter+syndrome+in+software+development&amp;amp;num=50"&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="http://manojsinghnegi.com/blog/2017/01/24/Why-every-developer-should-have-a-blog/"&gt;again&lt;/a&gt; and &lt;a href="https://www.freecodecamp.org/news/every-developer-should-have-a-blog-heres-why-and-how-to-stick-with-it-5fd55a247fbf/"&gt;again&lt;/a&gt; and &lt;a href="https://www.freecodecamp.org/news/if-youre-a-developer-you-should-start-blogging-and-here-s-why-b5cb2951d95c/"&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 &lt;a href="https://www.ghosh.dev"&gt;this blog&lt;/a&gt; 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?&lt;/p&gt;

</description>
      <category>blogging</category>
      <category>developer</category>
      <category>community</category>
      <category>career</category>
    </item>
  </channel>
</rss>
