redact.photo – Free and Private Image Redaction in the Browser

Wonderful little tool by Rik Schennink to redact photos straight in the browser.

The redacted parts can’t be reversed, as the pixels get randomly shifted before they are blurred.

The tool itself is powered by Pintura, a powerful JavaScript Image Editor that Rik has been working on.

redact.photo →
Pintura →

A Guide To CSS Debugging

Over at Smashing Magazine, Stephanie Eckles has published an extensive piece on CSS Debugging.

In my experience, CSS layout issues often fall out of one of the following categories:

  1. Overflow of content from its parent resulting in extra or unexpected scrollbars and content being pushed out of the regular viewport area.
  2. Inheriting browser inconsistencies leading to mixed results across browsers and devices.
  3. Unexpected inheritance from the cascade where multiple styles override one another, which may cause alignment and spacing issues, among other things.
  4. CSS resiliency failures from DOM changes, including when child elements have gained wrapping divs or additional elements are unexpectedly added.

We’ll review debugging for each category by learning common culprits for these issues and see how to use dev tools and other methods to pinpoint the offending styles. Of course, we’ll discuss possible resolutions to these bugs as well.

Felt nice to see scrollbar-gutter, Viewport Units, and Cascade Layers be mentioned in the post there 🙂

A Guide To CSS Debugging →

The Beauty of Bézier Curves

They’re used for animation, text rendering, and all sorts of curved shapes! But how do they actually work? well, like, that’s what the video is about, so, watch it to find out etc!!

Conditional Border Radius In CSS

Ahmad Shadeed recently spotted this declaration in Facebook’s CSS:

.card {
   border-radius: max(0px, min(8px, calc((100vw - 4px - 100%) * 9999)));
}

It’s a really interesting piece of code, which acts as a toggle to only show rounded corners in case the element does not entirely span the viewport width.

You can see it in action in this (resizable) demo:

See the Pen
Border radius / FB
by Ahmad Shadeed (@shadeed)
on CodePen.

That calc((100vw - 4px - 100%) * 9999) in the code will either yield a very big positive number or very big negative one. You might recall this technique from the Flexbox Holy Albatross. Combined with min and max, the result will be one of 0px or 8px.

The 4px in the equation acts as an offset to take into account: the border-radius will only be gone when there’s less than 4px (2px on each side) available. In the demo below I’ve animated the width and adjusted the offset to be 40px (thus 20px on each side).

See the Pen
Border radius / FB
by Bramus (@bramus)
on CodePen.

On his blog, Ahmad explains (and illustrates) the math in more detail: Conditional Border Radius In CSS →

Understanding How Facebook Disappeared from the Internet

Interesting read on how the folks at Cloudflare saw Facebook go down last night, and how it impacted traffic on their end.

The Internet is literally a network of networks, and it’s bound together by BGP. BGP allows one network (say Facebook) to advertise its presence to other networks that form the Internet. As we write Facebook is not advertising its presence, ISPs and other networks can’t find Facebook’s network and so it is unavailable.

Apparently this failure not only brought the websites and services down, but als prevented employees from opening doors and entering rooms as all those devices/locks are IoT devices which also rely on DNS to communicate with the proper servers. One (unverified) source even claimed that it was a code review bot that auto-merged the faulty BGP rule.

Understanding How Facebook Disappeared from the Internet →

Manage Google Maps API keys with the Google Cloud SDK (gcloud)

To manage cloud infrastructure I’m a huge fan of Terraform. Unfortunately I noticed that it’s not possible to use Terraform for managing API Keys for Google Maps (and other services). After some digging I found that the alpha version of the Google Cloud SDK has support for it.

Commands available in the stable gcloud release:

describe                Describe an API key's metadata.
list                    Lists API keys.

Commands available in the alpha gcloud release:

clone                   *(ALPHA)*  Create a new API key with the same
                              metadata as input key.
create                  *(ALPHA)*  Create an API key.
delete                  *(ALPHA)*  Delete an API key.
describe                *(ALPHA)*  Describe an API key's metadata.
get-key-string          *(ALPHA)*  Get key string of an API key.
list                    *(ALPHA)*  Lists API keys.
lookup                  *(ALPHA)*  Look up resource name of a key string.
undelete                *(ALPHA)*  Undelete an API key.
update                  *(ALPHA)*  Update an API key's metadata.

Aha, that’s more like it!

~

To create an API key use gcloud alpha services api-keys create. A basic API Key for uses the Map JavaScript API can be created with this command:

gcloud alpha services api-keys create \
    --display-name="My Google Maps Key" \
    --api-target=service=maps_backend \
    --allowed-referrers="https://bram.us/*,https://*.bram.us/*"

Note that this API key requires the maps-backend.googleapis.com API to be active for your GCP project:

gcloud alpha services enable maps-backend.googleapis.com

The other gcloud alpha services api-keys subcommands are pretty straightforward.

~

One thing that did require some more tweaking was the ability to extract the key string for an existing key using the Key’s Display Name. The “problem” is that get-key-string only accepts a Key Id, not a Display Name.

To work around that I nested the gcloud alpha services api-keys list call (to find the Key Id based on the Display Name) inside a gcloud alpha services api-keys get-key-string call, along with some grep and cut magic.

gcloud alpha services api-keys get-key-string \
    "$( \
        gcloud alpha services api-keys list \
        --filter DISPLAY_NAME:"My Google Maps Key" \
        | grep "projects/" \
        | cut -d ' ' -f1\
    )" \
    | cut -d ' ' -f2

(Replace My Google Maps Key with the Display Name of your key)

~

To integrate this a bit more nicely with Terraform I whipped up this little shell script:

# Extract gc-project value from Terraform environments file
TF_WORKSPACE=`terraform workspace show`
GC_PROJECT=`cat "./environments/$TF_WORKSPACE.json" | grep '"gc-project"' | cut -d '"' -f4`

# Name of the Key to create
KEY_NAME="My Google Maps Key"

# Get ID for the key.
KEY_ID=`gcloud alpha services api-keys list --filter DISPLAY_NAME:"$KEY_NAME" --project="$GC_PROJECT" | grep "projects/" | cut -d ' ' -f1`

# No Key ID Found
# ~> Key does not exist yet
# ~> Create it + Output the Key String
if [ -z "$KEY_ID" ]; then

    echo -e "\033[33m♺\033[0m Creating Key '$KEY_NAME'"

    gcloud alpha services api-keys create \
        --project="$GC_PROJECT" \
        --display-name="$KEY_NAME" \
        --api-target=service=maps_backend \
        --allowed-referrers="https://bram.us/*,https://*.bram.us/*" \
        &> /dev/null

    KEY_ID=`gcloud alpha services api-keys list --filter DISPLAY_NAME:"$KEY_NAME" --project="$GC_PROJECT" | grep "projects/" | cut -d ' ' -f1`
    KEY_STRING=`gcloud alpha services api-keys get-key-string "$KEY_ID" --project="$GC_PROJECT" | cut -d ' ' -f2`

    echo -e "\033[32m✓\033[0m Created key '$KEY_NAME'. Key String = $KEY_STRING"

# Key ID found
# ~> Output the Key String
else
    KEY_STRING=`gcloud alpha services api-keys get-key-string "$KEY_ID" --project="$GC_PROJECT" | cut -d ' ' -f2`
    echo -e "\033[32m✓\033[0m Key '$KEY_NAME' already exists. Key String = $KEY_STRING"
fi

Note that I’ve written this script to be run in conjunction with Terraform using our specific code structure. On the first few lines you can see that it extracts the value for GC_PROJECT from a JSON file that’s named after the Terraform Workspace.

Created keys won’t be stored in Terraform’s state. However, the code is written in such a way that successive runs will not create a new key nor overwrite the existing one. Note that this check to see if a key exists is only a superficial check, as it does not check the key’s settings (such as api-target) — Only the Display Name is checked.

To not have to run this script on the site, you can execute it using a local-exec

~

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 surely put a smile on my face. Thanks!

☕️ Buy me a Coffee (€3)

To stay in the loop you can follow @bramus or follow @bramusblog on Twitter.

CodeSwing — Interactive Coding Playground (like CodePen, JSFiddle, etc.) Extension for Visual Studio Code

The Visual Studio Code extension named “CodeSwing” just blew my socks off.

CodeSwing is an interactive coding playground for VS Code, that allows you to build/explore/learn rich web applications (“swings”). It’s like having the magic of a traditional web playground (e.g. CodePen, JSFiddle), but in Visual Studio Code!

~

Using the Command Palette you can create a new Swing in a directory. Here I’ve chosen for the classic HTML/CSS/JS (cfr. CodePen).

Using the “Add Library” command one can quickly import ES Modules, without needing to install any package. This is possible thanks to the wonderful SkyPack.

~

As you’d expect, you choose your own layout

~

The rendering engine is powered by Chromium, so you can use its DevTools.

The version of Chrome that’s bundled with the extension (or is it the Chromium that Electron uses?), is Chrome 91. Because it’s bundled, you can’t set any feature flags — such as Experimental Web Platform Features to enable Scroll-Linked Animations — on it.

~

CodeSwing also has support for React/Svelte/Vue and preprocessors.

When you create swings, you can use your favorite web languages (HTML/Pug, CSS/SCSS/Less, JS/TS) and libraries (React, Vue, Angular, etc.) and have a live preview as you type, without needing to worry about compiling or bundling anything. It’s like a visual REPL for managing runnable code snippets.

In the video below I quickly create a React Component:

~

There’s a ton of more stuff to dig into — such as exporting your Swing to CodePen — detailed in the README.

And oh, if go to codeswing.dev you get redirected to GitHub’s CodeSpaces with a preloaded CodeSwing.

Visual Studio Marketplace: Codeswing →
Codeswing Source (GitHub) →

Cloudflare R2 Storage

Cloudflare just announced their Cloud Storage solution, R2, with a very interesting pricing model (emphasis mine):

Traditional object storage charges developers for three things: bandwidth, storage size and storage operations.

R2 builds on Cloudflare’s commitment to the Bandwidth Alliance, providing zero-cost egress for stored objects — no matter your request rate.

Cloudflare R2 will be priced at $0.015 per GB of data stored per month — significantly cheaper than major incumbent providers.

In a very brilliant move, they also made their API fully compatible with the S3 API. That way it’s compatible with existing tools and applications, so switching over from S3 should go without any issue.

Announcing Cloudflare R2 Storage: Rapid and Reliable Object Storage, minus the egress fees →

Free Video Course: Build Static Pages Dynamically using Next.js and the Notion API

This free video course over at Egghead forms a nice introduction to both Next.js and the Notion API.

In this course, you will learn how to pre-render static pages with data from the Notion API. We will cover querying page, block and database data, as well as updating properties with mutations, and some helpful techniques for transforming raw responses into convenient data structures for our React components. Using Incremental Static Regeneration (ISR), we will learn about dynamically creating static pages any time new data is available in Notion.

I also like the fact that it’s short and on-point.

Build Static Pages Dynamically using Next.js and the Notion API →

Partytown: Run Third-Party Scripts off the Main Thread in a Web Worker

The folks from builder.io set out to create a way to prevent Third-Party Scripts from blocking the main thread. The result is Partytown, which runs Third-Party Scripts Within a Web Worker.

Partytown is able to sandbox and isolate third-party scripts within a web worker and allow, or deny, access to main thread APIs. This includes Cookies, localStorage, userAgent, etc. Because the code must go through Partytown’s Proxy in order to access the main thread, Partytown also has the ability to log every read and write, and even restrict access to certain DOM APIs.

It works by creating JavaScript Proxies to replicate and forward calls to the main thread APIs (such as DOM operations) and making calls to them using synchronous XHR requests. Pretty crazy, right?! 🤯

To mark third-party scripts to run in a Partytown web worker, set the type attribute of its opening script tag to text/partytown.

<script type="text/partytown">
  // Third-party analytics scripts
</script>

Also comes with integrations for frameworks like React.

Partytown (GitHub) →
Introducing Partytown: Run Third-Party Scripts From a Web Worker →