Native Image Lazy-Loading: loading-attribute-eagle-polyfill

Today, Rick Viscomi noted that some sites have set eagle – instead of eager – as the value for Native Image Lazy-Loading:

While this is most likely a classic case of #damnyouautocorrect (instead of jokingly being a LOTR/Scrubs reference), that didn’t keep Jay Phelps from creating loading-attribute-eagle-polyfill to cater for those small mishaps:

A polyfill for <img loading="eagle" />. Displays an America Eagle as the placeholder of the image while the your real images are still loading.

LOL 😁 β€” I love the internet.

Here’s a code example on how to use, if you ever were to use it in the first place:

<head>
  <script src="https://unpkg.com/loading-attribute-eagle-polyfill/loading-attribute-eagle-polyfill.js"></script>
</head>
<body>
  <!-- Here's an example URL that artificially delays the src so you can see the proud Eagle -->
  <img
    loading="eagle"
    src="https://deelay.me/2000/https://img.webmd.com/dtmcms/live/webmd/consumer_assets/site_images/article_thumbnails/other/cat_relaxing_on_patio_other/1800x1200_cat_relaxing_on_patio_other.jpg"
    width="300"
    height="200"
  />
</body>

loading-attribute-eagle-polyfill

ℹ️ Remember Native Image Lazy Loading being way too eager? Chrome recently updated the thresholds and are backporting the changes back to Chrome version 79:

Simple Image Gallery with display: grid; and object-fit: cover;

On the Full Stack Belgium Slack channel, user @Brammm recently asked how to create a simple image gallery.

Anyone have a favorite way of making an image grid with CSS? I’d like one of those β€œfancy” ones where no matter the aspect ratio of the image, they all have the same gap between them.

While some suggested existing solutions such as React Photo Gallery or photo-stream, I focussed on only the layout-question and suggested a DIY solution using display: grid; and object-fit: cover;.

Using only those those two props it’s really easy to create a grid based gallery.

  • Using display: grid; you create a grid layout. I decided to create a grid of square cells/tiles.
  • As I stretch out each image inside a tile, the images can get deformed. Using object-fit: cover; this can be prevented. Note that you will visually lose some data, as the edges will get clipped, but that was not a concern to @Brammm.

~

Proof Of Concept

Using photos from the birth of my son Noah I knocked up a small small proof of concept in about 10 minutes. Tweaking it a bit more – by adding some CSS Variables into the mix – I eventually landed on this:

See the Pen
Simple Gallery (display: grid; + object-fit: cover;) V1
by Bramus (@bramus)
on CodePen.

πŸ’β€β™‚οΈ To create a consistent gap between all items I used the CSS gap property, which replaces grid-gap.

~

Making it feel more dynamic

To make things visually more interesting, and also since some photos are portrait and some landscape, the size of the tiles must differ a bit. With CSS Grid in place it’s really easy to stretch out cells so that they span more than one column or row:

ul.gallery > li.wide {
    grid-column: span 2;
}

ul.gallery > li.wider {
    grid-column: span 3;
}

ul.gallery > li.high {
    grid-row: span 2;
    height: auto; /* to undo the height */
}

With these in place, my gallery started to look like this:

There, looks good, right? πŸ™‚

πŸ˜‹ I know, I cheated a bit as I added the .wide/.high classes onto carefully selected tiles, leaving no gaps in my grid. To workaround potential gaps, one can always use grid-auto-flow: dense;, or use the (still under development) Grid Masonry from the Grid Level 2 spec. Note that in those cases the order of the images will differ from their source order.

~

Going further: Zoom / Lightbox functionality

Where I had originally stopped working on the gallery after that, today I wondered if I could adjust it a bit further and give it the ability to show the images at full screen, in some sort of Lightbox. Still using only CSS it’s possible to show an overlay while pressing+holding a tile.

  • Using the :active pseudo-selector you can know which element is currently being pressed
  • Using position: fixed; you can put content on top of the entire viewport

Combining the two as follows …

ul.gallery > li:active > img {
    position: fixed; /* Position the image on top of the viewport contents */
    z-index: 11;
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
    object-fit: contain; /* Make sure the image isn't distorted */
    padding: 1vw; /* Add some padding to make it breathe a bit */
    background: rgba(0, 0, 0, 0.8); /* Dim the background */
}

… will get you this:

See the Pen
Simple Gallery (display: grid; + object-fit: cover;) V3 (WIP)
by Bramus (@bramus)
on CodePen.

While the version above does work (on Desktop), there’s a huge problem with it: it’s not (keyboard) accessible at all. In order to give the browser – and therefore user – hints about stuff being clickable we could add a truckload of ARIA attributes or simply use … links. An extra benefit of this approach is that we then start using CSS :target, and eventually create Pure CSS Lightbox.

See the Pen
Simple Gallery (display: grid; + object-fit: cover;) V4b
by Bramus (@bramus)
on CodePen.

It’s not entirely perfect though, when using only the keyboard to navigate, you have to do some trickery to close the Lightbox: after zooming in on an image you’ll have to hit TAB to focus the close link and then hit ENTER to activate it β€” If only autofocus where available on non-inputs …

As an extra I also added some JS to make the ←, β†’, and ESC keys work, so that it behaves like a β€œreal” Lightbox. There’s plenty more things I could add to it, but that’s something way beyond the original goal of this post so I left it at that.

~

Did this help you out? Like what you see?
Thank me with a coffee.

I don't do this for profit but a small one-time donation would always put a smile on my face. Thanks!

β˜•οΈ Buy me a Coffee (€3)

InfluxDB Flux: type conflict: int != float

In a Flux query I was writing I wanted to negate the fetched values using a map function to multiply each value by -1:

map(fn: (r) => ({ _value: r._value * -1 }))

To my surprise this yielded an error:

type conflict: int != float

Took me a few Google Search Coupons to realize the error got trigger not by the type of the _value, but by the -1 in there. The solution therefore is really simple: don’t multiply by an integer (e.g. -1) but multiply by a float (e.g. -1.0); like so:

map(fn: (r) => ({ _value: r._value * -1.0 }))

πŸ˜…

Did this help you out? Like what you see?
Thank me with a coffee.

I don't do this for profit but a small one-time donation would always put a smile on my face. Thanks!

β˜•οΈ Buy me a Coffee (€3)

Checkboxland – Render anything as HTML checkboxes

Checkboxland is a JavaScript library for rendering anything as HTML checkboxes.

You can use it to display animations, text, and arbitrary data. It also supports plugins, so you can build more powerful APIs on top of it.

Heh. Can’t quite think of a reason when exactly to use this, but it’s fun nonetheless πŸ˜…

Checkboxland →

πŸ‘Ύ This reminded me of Fullstack, a game built by Hakim El Hattab:

10 Modern Layouts in 1 Line of CSS

Already making rounds on Twitter last week, but now the video’s been released too:

In this dynamic talk, Una goes over the power of modern CSS layout techniques by highlighting a few key terms and how much detail can be described in a single line of code. Learn a few layout tricks you can implement in your codebase today, and be able to write entire swaths of layout with just a few lines of code.

Ten modern layouts in one line of CSS →
1-Line Layouts Demo Site →

ESNext: Proposals to look forward to (ESNEXT Conf)

Happening right now is ESNEXT Conf a fully remote conference exploring the future of JavaScript and the web. When I saw the CFP float by, it sounded like a perfect match for my talk β€œESNext: Proposals to look forward to”. Thankfully the organisers – Fred and Drew from Pika – also felt that ways and the put me in the line-up.

With the yearly ECMAScript releases (ES2015..ES2020) of a lot of things have changed in JavaScript-land, and there’s even more to come. This talk takes a look at a few of the newest (ES2020) and some of the upcoming ECMAScript features, which (hopefully) will become part of the ECMAScript Language Specification in the near future.

~

Talk Slides

The slides are up on slidr.io, and also embedded below:

~

Talk Outline

  • ECMAScript History and Timeline
  • TC39: Info on TC39, what they do, and how they work.
  • ESNext Proposal: Field Declarations
  • ES2020 Feature: Optional Chaining
  • ES2020 Feature: Null Coalescing
  • ESNext Proposal: Logical Assignment
  • ES2020 Feature: Dynamic Imports
  • ESNext Proposal: Decimal
  • ESNext Proposal: Cancellation API (+ Mention of AbortController)
  • ESNext Proposal: Declarations In Conditionals
  • ES2019 Feature: Object.fromEntries()
  • ESNext Proposal: Slice Notation
  • ESNext Proposal: Pattern Matching
  • Fin.

~

Talk Video

Once the video has been released, I’ll update this post to include it. Here it is:

~

Thanks to the organisers for having me, and thanks to the attendees for coming to see me. I hope you all had fun attending this talk. I know I had making it (and whilst bringing it forward). A shame we couldn’t meet in person though, perhaps at a next edition πŸ˜‰

πŸ’β€β™‚οΈ If you are a conference or meetup organiser, don’t hesitate to contact me to come speak at your event.

New in Chrome 84

Chrome 84 has been released, which contains some nice additions. Pete LePage walks us through:

Chrome 84 is rolling out now! Users can start common tasks within your app with App Icon Shortcuts. The Web Animations API adds support for a slew of previously unsupported features. Wake Lock, and the Content Indexing API graduate from origin trial. There are new origin trials for Idle detection and SIMD. And there’s a whole bunch more. Let’s dive in and see what’s new for developers in Chrome 84!

I’ve written about the Wake Lock API before, which has been part of Chrome’s Origin Trials since Chrome 79.

New in Chrome 84 →

PHP: Convert a Geolocation (Latitude / Longitude Coordinates) to a Timezone identifier

Part of a PHP project I’m working contains a list of sites/buildings. For each site/building we monitor some data, for example its energy usage.

We decided that we wanted to generate a daily/weekly/monthly reports of the data, by aggregating the datapoints. As our sites/buildings are spread across the globe – and thus timezones – we can’t simply select data between 00:00:00 UTC and 23:59:59 UTC but have to use its geographical location’s “day window” to do our calculations.

Unfortunately we don’t didn’t store the timezone for a site/building, but since we do keep track of its geographical location – using a WGS84 latitude-longitude pair – it should be possible to derive its timezone, right?

Right! On StackOverflow I found this little snippet that does the job:

function get_nearest_timezone($cur_lat, $cur_long, $country_code = '') {
    $timezone_ids = ($country_code) ? DateTimeZone::listIdentifiers(DateTimeZone::PER_COUNTRY, $country_code)
                                    : DateTimeZone::listIdentifiers();

    if($timezone_ids && is_array($timezone_ids) && isset($timezone_ids[0])) {

        $time_zone = '';
        $tz_distance = 0;

        //only one identifier?
        if (count($timezone_ids) == 1) {
            $time_zone = $timezone_ids[0];
        } else {

            foreach($timezone_ids as $timezone_id) {
                $timezone = new DateTimeZone($timezone_id);
                $location = $timezone->getLocation();
                $tz_lat   = $location['latitude'];
                $tz_long  = $location['longitude'];

                $theta    = $cur_long - $tz_long;
                $distance = (sin(deg2rad($cur_lat)) * sin(deg2rad($tz_lat))) 
                + (cos(deg2rad($cur_lat)) * cos(deg2rad($tz_lat)) * cos(deg2rad($theta)));
                $distance = acos($distance);
                $distance = abs(rad2deg($distance));
                // echo '<br />'.$timezone_id.' '.$distance; 

                if (!$time_zone || $tz_distance > $distance) {
                    $time_zone   = $timezone_id;
                    $tz_distance = $distance;
                } 

            }
        }
        return  $time_zone;
    }
    return 'unknown';
}

Usage is as follows:

// Timezone for one NY coordinate
echo get_nearest_timezone(40.772222,-74.164581);
// ~> America/New_York

// Timezone for one Belgian coordinate
echo get_nearest_timezone(51.0162167, 3.7338451);
// ~> Europe/Brussels

// More faster and accurate if you can pass the country code 
echo get_nearest_timezone(40.772222, -74.164581, 'US');
// ~> America/New_York

With this timezone identifier now being available, we can include it in our queries and generate our daily/weekly/monthly reports πŸ™‚

🍻 Here’s to copying-and-pasting from StackOverflow!

On a related note: Falsehoods programmers believe about time and time zones is worth a read, especially if you’ve already dealt with time and timezones.

Song Exploder Episode 187: Apparat – Goodbye

This episode from Song Exploder gives us a good insight in how Goodbye by Apparat came to be.

The Netflix original series Dark debuted in December 2017. It’s a really mysterious, mind-bending German science fiction show with a unique tone. A big part of that tone is announced every episode with the music in the show’s opening title sequence. It’s the song β€œGoodbye,” by German electronic artist Apparat, the solo project of Sascha Ring. This song actually came out years ago, on the 2011 Apparat album The Devil’s Walk. Since then, before it was used as the theme song for Dark, it’s been featured in a bunch of films and commercials, and notably, in the Season 4 finale of Breaking Bad.

Back in the day I was really delighted when I heard it in the Season 4 finale of Breaking Bad as it complemented the camerawork, montage, and story.

It is a bit of a pity that the track is now known only as “The theme from Dark”, taking away the focus from Apparat’s other work which is also really good. How good? Let me put it this way: his album β€œThe Devil’s Walk” (featuring Goodbye) didn’t make it into my top 10 of played albums over time for no reason πŸ˜‰

The Sound of Silence

If you’re a regular reader of bram.us you might have noticed that I haven’t posted anything here during the past month. In short: I’ve been busy with many (personal) things that needed my attention. Here’s a few of the things that kept me from blogging …

Public Speaking

With COVID-19 many conferences have switched to an online format. With this came the opportunity to speak at rather distant conferences, which wouldn’t have been possible before. I’m honoured to have been able to speak at JS VidCon, JS RemoteConf, JSNation Live, and JSConf.be.

Some of those events worked with prerecorded talks, while others were done live. It was always a mix and match of sites and apps to use (Crowdcast, Zoom, Wirecast, Run the World, Slack, Discord, etc) all while I remained at home. Easy, although I’m kinda missing the personal connections one can make at IRL conferences.


Me, speaking at home #JSNationLive

Prepping for those conferences took some time, but thankfully I could reiterate on my ESNext: Proposals to look forward to talk. As the talk has grown over the past two years I’ve come rather fluent at it, and can easily adapt it to both short and long formats. There’s another rather big conference planned this mid-July, but I can’t talk about it just yet πŸ˜‰

~

Life as a family of 3 + 2

Tomorrow it will be exactly five months ago that my youngest son Noah was born. If you’re a parent yourself I shouldn’t tell you how hard it is to have a baby in the house, especially if that baby only sleeps for short periods of time (bursts of 30 minutes during the day, 3 to 4 hours at night). He’s a very pleasant and happy baby to have around, yet the only thing we wish for is to get a few more hours of unbroken sleep. It’s really hard to focus and think when in Zombie mode 😡

We’ve also had some issues with getting his formula right, as he tends to be rather sensitive (but not allergic as the doctor said) to cow milk, but after four months of trial and error I think we finally got it right.


Noah, June 2020

My oldest son Finn (aged 11) and my daughter Tila (aged 9) live with us every other week, as they spend the other week at their mums place. When they’re at home with us, they really are “at home” as all the schools in Belgium had switched to a format called “pre-teaching” because of COVID-19: the kids don’t go to school physically, and are required to finish a set of daily tasks (both online and offline) at home. I’m blessed that I have quite smart and standalone children by now, yet my oldest son Finn is quite the slacker to be honest … always reminding him to get started, double checking his work, etc. is quite time consuming to be honest.


Ellie + Finn + Tila, May 2020

Thankfully schools have reopened their doors just last week, so that’s a small load off of my shoulders. And oh, I’m very grateful that my daughter Tila also helps us with the baby when she’s around.

~

We bought a new house

Ever since (before) the birth of our son Noah we’ve been on the lookout for a new house to live in, as we need an extra bedroom for him to sleep in (right now he sleeps with us in our bedroom, as we don’t have a room for him). About a month ago my colleague Roel stumbled upon a house while biking and sent it over. One thing led to the other and before we knew we were talking with the bank about a new loan and everything that goes along with that. And yes, our bid got accepted so mid-September we’ll be moving house 🀩


Our new house, soon to be home

~

We’re about to sell our current house

As we will no longer be needing our current home β€” and we kinda rely on the money that we’ll get from selling it β€” I’ve been busy finishing up the list of home improvements that needed to be done. The biggest job on that list was finishing the drywall in our living room. After sitting there unfinished for nearly 5 years (😬) I – with the help of a friend – finally got to smoothing it out, putting up a wallpaper and painting the whole thing white.


Before and after

In order for the realtor who will be selling our house to start, they need some photos. To do so, we got a set of instructions (it’s in Dutch but you get the idea with the photos). As a result we’ve been cleaning out our closets and have been making trips to the local thrift shop and/or recycling depot . It’s crazy what one can collect over a period of 10 years time πŸ˜…

Our house is expected to go up for sale early July.

~

Work

In between all that I also tried to do some actual work. I’m still working on a long-term Monitoring platform / IOT project which we at vBridge started almost two years ago, doing mainly PHP and cloud-based things nowadays. I try to sneak in some JS from time to time (both client- and serverside), but it’s quite limited to be honest.

As I’m currently the only web developer on the project (*), I’m a bit overbooked: refactoring a huge part of the app while also squashing bugs (if one were to be found) and catching deadlines of new promised features is quite a lot of juggling that needs to be done. This week I was finally able to finish up one of two planned refactors, so I can finally focus on new features again.

(*) Although we haven’t announced it officially yet, we’re on the lookout for good people to join our team. We’re basically looking for a web developer with preferably a PHP background and an interest in cloud development. Our policy is to hire for talent+attitude and train for skill, so don’t worry if you think you lack certain skills.