Squircles on the Web — Houdini to the rescue 🚑 🚀

Pavel Laptev
9 min readApr 8, 2021

Hallo zusammen! Today I want to tell about my experience with CSS Houdini, what is it and how it works. In particular, I’ll show and tell you about Paint API, this is the part of Houdini CSS.

About a year ago I heard about CSS Houdini and that it’s a powerful thing and will change the way we build CSS today. But only today, I’ve decided to give it a shot, and I was surprised about the effects that you can achieve with it and also a little disappointed about the feature coverage across browsers.

Because I remember how fast CSS grid layout was implemented — pretty fast. And I consider CSS Houdini in the same way — this is the feature that will help designers to achieve effects that is possible today only in graphic/design tools.

What is CSS Houdini 🧐

“Houdini is a set of low-level APIs that exposes parts of the CSS engine, giving developers the power to extend CSS by hooking into the styling and layout process of a browser’s rendering engine”

It means that developers can parse CSSOM, write custom properties and easily manipulate them through JS or CSS.

And Houdini has three basic API to make custom things:

  1. Paint API — https://developers.google.com/web/updates/2018/01/paintapi
  2. Animation API — https://developers.google.com/web/updates/2018/10/animation-worklet
  3. Layout API — https://dev.to/adrianbdesigns/css-houdini-s-layout-api-explained-33pa

Find really cool examples here 👇

I found a few really nice Houdini examples that use Pain API and decided to try to make something with it too.

What I really wanted to see on the web today is the ability to use squircles or superellipse shapes instead of the standard border-radius.

And I found an example by Vincent De Oliveira that’s can make Squrcles, but doesn’t fit my needs 100%.

https://css-houdini.rocks/smooth-corners/

Squircles on the web today

Before I will dig deep into the module I wrote, let's quickly overview how we can bring squircles experience today.

There are some ways how to implement a squircle shape on the Web, but none of them are native or flexible enough.

This is unfortunate. Also, many of these ways are pretty hacky, which means that each method has some restrictions which are not so good if you want to use it widely.

Current implementation methods

Let’s take a look at two methods and let's take this checklist to make some conclusions about them. The winner is the one with fewer points.

  • can’t use as a background image
  • use only as separated component
  • can’t change Squircle smoothness
  • Squircle not scalable
  • Can’t outline
  • Noticeable artifacts or shape imperfections

1. CSS border-radius hack

This method works, but it’s pretty restricted, is not flexible, and I can’t use it on different elements.

Cant apply gradients and change the size
Artifacts because the shape isn’t solid

This method scores 6 out of 6 points. I don’t see how I use it as a button or how I’m trying to scale it to different sizes.

2. SVG shape as a background

This is a good method in terms of the shape build, but the manipulation can be done here only through JS.

Read this article (which was written in 2018), this is a pretty detailed investigation:

Here are a few playground examples. Check it out 🔭 🕹

So, this method scores only 3/6.

We can use outline, effect, change the shape only via JS or only if an element predefined by its size.

Both these methods can’t make two important things that stop me from using them — not flexible. My element should be a square or predefined rectangle.

But here is the case where Paint API will save us. It can easily solve flexibility color, effects, and manipulation issues.

UPD: a cool SVG generator example

Here are two cool examples of squrcle-generators by Mathieu Jouhet 👇

3. Paint API 🎨

What is great in Pain API is that we can write our own custom CSS properties — without involving JS. We can make our effects flexible, which means that we can implement them and scale with any element.

Here is an abstract scheme of how Paint API works:

All good with the example below, it supports negative values and uses a correct script, but it’s not scalable, and I also would like to use cubic-bezier, I use it in Figma and I understand how to build a squircle shape with it.

https://css-houdini.rocks/smooth-corners/

Build a squircle shape ✏️ 📐️

I wouldn’t describe each line of the module, but I’ll explain the most important parts of it and how I build the shape.

1. Curvature. What is the difference between these two shapes? First is that a squircle shape rounding starts smoothly, while standard rounding always has the same angle (pic.1)

pic.1

In order to build the curvature, I’ll use bezier-curves. Here is the demonstration, how to build standard and squircle shapes with bezier-curves.

2. Early rounding. 20px radius for a squircle shape is not the same as for the standard one, because smoothing starts earlier. It means that when a user writes e.g. 20px we will need to take a ratio to make values on squircle and standard shape visually the same (pic.2). The example below I made in Figma and you can see that the squircle starts to round earlier, but visually both figures have the same radius.

pic.2

in my case, the ideal ratio is 1.8. I will add this ratio to the bezier distance.

So, the ratio here means that I wouldn’t draw bezier-lines almost to the very end of corners, but stop them a little earlier — at 0.1 earlier (or 0.2 because we double the input radius). And I’ll multiply my radius at this ratio as well.

The ratio I took doesn’t rely on any math formulas, I just tried to find the ideal number, and I’m pretty good with the 1.8.

But you can also change the default ratio using secret 🤭 custom property --squircle-ratio: <number>

How to draw bezier-curve with canvas ✍️

This is actually pretty simple. First, we need to draw a simple line lineTo(x,y) and our line will consist of four arguments: x1,y1 → x2,y2

Then we draw the arc with a special method bezierCurveTo where we have 3 dots. x1,y1 — where the first bezier line ends (x0,y0 was taken from the previous line automatically), x2,y2 — where the second bezier line begins, and x3,y3— where it ends.

So, for instance, if we want to change a standard radius 50%50 to a squircle like, we can change x1 to 190 and y2 to 10.

Prevent dots overlapping ⚡️

What if --squircle-radius property will be bigger than the figure width or height? We will see something like in picture 3.

What we need to do to avoid overlapping like this is a simple check:

squircleRadius < geom.width / 2 && squircleRadius < geom.height / 2

in this line, we check if width and height lesser than radius divided by 2. If it's bigger, then we need to pass the radius equals to the smaller parameter (width or height). But how do you know which parameter is smaller?

Use Math.min() method. It will automatically check which value is smaller and return it.

Math.min(geom.width / 2, geom.height / 2)

In conclusion and demos

Well… Houdini and Paint API, in particular, is a cool feature, and I hope we can use it on all browsers in the near future. Meanwhile, check this module out here 👇

All links and references from the article

--

--