To no one’s surprise, I’m sure, there are lots of different ways to do the same thing on the web. Shape morphing, being a thing on the web, is no different. There are some native technologies, some libraries that leverage those, and some libraries that do things all on their own. Let’s look at some of the options (with demos) and weigh the advantages and disadvantages.
SMIL
The original, native technology for shape morphing is SMIL. We have both a guide to SMIL on CSS-Tricks, and an article talking about replacements for it, since it doesn’t work in Microsoft browsers and Blink threatened to yank it at one point.
I wouldn’t suggest doing important work in SMIL but it is OG shape morphing.
Moving right along to probably the most robust possible option, Greensock’s MorphSVG is a powerhouse. Bear in mind:
MorphSVGPlugin is a bonus plugin for Club GreenSock members („Shockingly Green” and „Business Green” levels). It’s our way of showing our gratitude to those who are fueling innovation at GreenSock.
Worth it. Just MorphSVG alone is amazing. Unlike almost any other shape morphing method, it can tween between shapes of any number of points. It does so performantly, in a safe cross-browser fashion, and gives you more fine grain control over how the animation goes down.
If you’d like to play with MorphSVG, I created a drag-and-drop Pen to morphing between any two shapes (best results with SVG’s with viewBox="0 0 100 100" SVGs with just one <path>):
It’s a little hard to believe, but CSS is getting in on the shape morphing action! Eric Willigers, a Chrome developer, emailed me last year:
’d’ has become a presentation attribute. This allows SVG path elements to be animated using CSS animations and Web Animations, with path(’…’) keyframes.
I assume this is a spec change, so browsers other than Chrome will, presumably, start allowing this. For now, this works great in Chrome:
The API gives you some control over the morphing, like how precise you want it to be and the ability to reverse the draw direction so different points match up to tween.
d3
d3, probably the biggest library out there for data viz folks, can also do shape morphing. Here’s a GIF recording of a demo by Mike Bostock:
There is also a d3 plugin called d3-interpolate-path that helps make it better:
Interpolates path `d` attribute smoothly when A and B have different number of points.
mo.js
„Motion graphics for the web” is the mo.js tagline, another newer library. Oleg Solomka (@LegoMushroom) has some incredible demos of what is is capable of:
If you happen to be a video person before you became a web person (or you’re still both) perhaps you have experience working in Adobe After Effects, which is all about creating „incredible motion graphics and visual effects”. The bodymovin library exports After Effects into SVG, including plenty of hot morphing action. Here’s a great demo:
If I had to blindly guess about global marketshare, I would have gotten it wrong. I probably would have forgotten about UC browser (kind of the point of Peter O’Shaughnessy’s article) that’s so huge in Asia. I would have guessed Firefox has a slight edge on Safari (turns out Firefox is half the share of Safari), and that Edge would be outpacing IE by now (also only half).
This is good dinner party conversation fodder, but I wouldn’t base any major decision making on it. The only stats that matter at your websites stats.
Here’s this sites, in the last 7 days.
There is always the chicken-or-egg conundrum though. If you support a browser that you didn’t before, doesn’t it stand to reason the numbers would go up for it? The time-on-site and bounce rate stuff would get better, anyway.
In CSS, some properties have shorthand. One property that takes separated values. Syntactic sugar, as they say, to make authoring easier. Take transition, which might look something like:
Dan Wilson recently used this kind of thing with animations to show how it’s possible to pause individual animations!
Here’s browser support:
Green indicates full support at the version listed (and above). Yellow indicates partial support. Red indicates no support. See Caniuse for full browser support details.
The other day I was watching an interview with Ade Oshineye where he discussed his work on the PWA Directory at Google, a showcase of progressive web apps. And it’s pretty neat!
It lists a whole bunch of PWAs out there and you can filter them by Lighthouse metrics – that’s the auditing tool from Google that scores a web app and gives us developers the ability to improve them.
Let’s say you have an element with a background-image, where only part of the image is visible, because the image is bigger than the element itself. The rest is cropped away, outside the element.
Now you want to move that background-image such that you’re focusing the center of the element on a specific point in it. You also want to do that with percentage values rather than pixels. We’re going to have to get clever.
This is going to involve some math.
Let’s use this as our image, which has markers for sizing:
And here’s our element, with that background-image applied. Notice we can only see the (top) left of the image:
Now let’s say we want to align a specific point on the image with the center of that element. Say, the point at 300px in from the left.
Since we’re asking for this position in pixels, it’s straightforward. With no position defined, the background-image „starts” with the point at 100 pixels at the center, so you need to move it to the left by 200 pixels:
It takes a second to figure out, but it’s nothing too taxing. You could have probably figured that out intuitively without needing to use a formula.
But what if you wanted to (or had to) express background-position as a percentage? Shouldn’t be too hard, right? Let’s try using a percentage to get ourselves centered at 300px again. We had a background-position-x of -200px, so let’s convert that to percent: -200 / 800 = -25%, so:
That feels like it kind of makes sense; background-position: 100% 0; makes the background-image flush-right and centered at 700px, or 7/8 the width of the image. But what if we wanted to center it at 100%? I guess we’d have to do… 9/8?
For example, with a value pair of '0% 0%’, the upper left corner of the image is aligned with the upper left corner of, usually, the box’s padding edge. A value pair of ‘100% 100%’ places the lower right corner of the image in the lower right corner of the area. With a value pair of '75% 50%’, the point 75% across and 50% down the image is to be placed at the point 75% across and 50% down the area.
Maybe we can reverse-engineer this.
On that last one, 112.5%, it was aligning the point at 112.5% across the background-image with the point at 112.5% across the bounding box. That kind makes sense. The spec seems written to make it easy for only three values: 0%, 50%, and 100%. Any other value isn’t so intuitive.
With background-position: 0;, we were focused on 100px, or 12.5%. With background-position: 100% 0;, we were focused on 700px, or 87.5%. How does background-position: 50% 0; look, exactly?
With background-position set to 100%, the center of our bonding box has „panned” from the center of the image, 3/4 of the way to the rightmost edge (from 400px to 700px). If we want to „pan” to the rightmost edge, we need to go that extra 1/4, or 200px. 1/4 is 1/3 of 3/4, so we need to go a third more than we did a moment ago, or a total of 66.667% from the center:
Whew! So to focus on the rightmost edge of a background-image that is 4 times the size of our bounding box, we need to set background-position: 116.667% 0;.
How the heck are we supposed to figure that out?
It’s a difference of 16.667% from the 100% we might expect. So if we wanted to focus on our original goal of 300px (or, 37.5%), we’d, uh, add 16.667%? There’s no way this is going to work:
To focus at 100% or 0%, you have to „overshoot” those values by a certain amount, when measured from the center.
So if we want to focus on 0%, or „100% of the way to the left of the center,” we have to subtract 66.667% from 50%. If we want to focus on 100%, or „100% of the way to the right of the center,” we have to add 66.667% to 50%.
I might have expected to have to add or subtract 50% to or from 50% to get to those edges: a 1:1 ratio of „how far I want to go from the center” to „what my background-position value should be.” But instead, we have to use a 4:3 ratio. In other words, we have to use a value four-thirds more „away from the center.”
Things are getting a little hairy here, so let’s introduce some terms:
c: Desired focal point (in percent) from leftmost edge of background image
So why 4/3? 4 is the ratio of our background-image width to our bounding box width; and 3 is… 1 less than 4. Could it be that simple? Let’s try a larger background-image, this time 1000px wide, or 5 times the width of our bounding box. And let’s again try to focus on the point at 200px. Here our equation would be:
So to revisit our equation, with a variable background-to-bounding-box ratio:
(c − 50%) × z/(z − 1) + 50% = p
Let’s turn this into English:
Given a point on a background-image at location c…
with c expressed as a percentage of the width of the image
where c is intended to lie in the horizontal center of the background image’s bounding box
and where the background-image is z times as wide as its bounding box
the correct background-position p (expressed as a percentage) is given by the following formula:
(c − 50%) × z/(z − 1) + 50% = p
Can we generalize this even more?
What if we wanted to align the point at 25% of the background-image with the point at 75% of the bounding box? Yikes!
Let’s revisit our original formula:
(c − 50%) × z/(z − 1) + 50% = p
Now let’s introduce some new terms:
b: Desired focal point (in percent) from the leftmost edge of the bounding box. Earlier we had assumed this to always be 50% so that the center of the bounding box would be focused on our target in the background-image.
d: background-image focal point (in percent) to align to bounding box’s midpoint in order to get c to align to b in the bounding box; if d of the background-image aligns with 50% of the bounding box, then c of the background-image will align with b of the bounding box.
Let’s think of it this way
To want to align position c of a background-image with position b of a bounding box is to want to align some other position, d, of a background-image with the center of the bounding box – and we already know how to do that. So can we figure out a way to derive d, the spot we need to be at 50%, from c, b, and z? Sure!
With our 800px wide background-image, in a 200px-wide bounding box (z = 4), if we want to focus the rightmost edge of the bounding box (b = 100%) on the position at 600px (c = 75%) in the image, we would want the center of the bounding box to be focused on the point at 500px (d = 62.5%).
How do we get from c (75%) to d (62.5%)? Where does that -12.5% difference come from?
Well, our b was 100%, 50% greater than our old „default” b of 50%. And 12.5% is 1/4 of that; 1/4 is the inverse of our z of 4. Is that where our d comes from? That would be:
d = c + (50% - b)/z
Looks promising. Now we can substitute d in for c in the original formula:
(d − 50%) × z/(z − 1) + 50% = p
Or:
(c + (50% − b)/z - 50%) × z/(z − 1) + 50% = p
Whew! Let’s test this. Let’s try to align the position at 25% in our background-image (200px) with the position at 75% in our bounding box. This would be:
p = (25% + (50% - 75%)/4 - 50%) × 4/(4 - 1) + 50%
p = -31.25% × 1.333 + 50%
p = 8.333%
Unbelievable! Let’s double check. How about the point at 87.5% in our background-image (700px) aligned with the position at 33.333% in our bounding box:
p = (87.5% + (50% - 33.333%)/4 - 50%) × 4/(4 - 1) + 50%
p = 41.6667% × 1.333 + 50%
p = 105.555%
When I began tackling this problem I did not expect it to be this difficult, but what a journey. I hope guiding you through my thought process has been enlightening, and that you may at some point find value in our little Sass function.
All the Pens embedded in this article can be found in this collection.
I was on vacation this past week and at some little beach gift shop they were selling this really cool big thick book called Ocean: A Photicular Book. You’ve probably seen something like it before… a plastic card that shows different images depending on how you are looking at it. This book is extremely well done in that the image are very high quality, and the design of the book makes the images move as you turn the pages.
Photicular, also known as Lenticular, or Integrated Photography, was first conceived in the early 20th century, but the basic concept has been around since 1692 when a French painter created paintings that revealed one, and then the other as the observer walked by. The simplest form of it is to cut two images into thin vertical strips and interleave them, placing in front of the composite image a plane of bars, like a picket fence, which only allows one to see one image at a time through the gaps. Instead of bars, lenses can be used, and more than two images – as many as dozens – can be interleaved.
The end result is really neat in the Ocean book. After mentioning this one on Twitter, I’ve heard good things about the Safari one as well. I’m sure they are all awesome. It’s almost like the newspapers and paintings in Harry Potter!
Of course, it made me think about how we might do something Photicular-like on the web. We have videos and GIFs on the web, so it’s not exactly hard to make images move. But it’s not just in the movement itself that made these books cool, it was that it moves along with your actions (of turning the page).
Perhaps we could page through a GIF as a user scrolled. Can you programmatically control which frame of GIF is currently being shown? Sorrrrrrta. There is no simple native technology for that, but there are some alternatives.
If you really wanted to stick with a GIF, BuzzFeed actually created a JavaScript library called libgif-js specifically for exerting control over GIFs in this way. Through this lib, you can jump to certain frames, pause and start the GIF like you can with video, and even make the GIFs „rubbable”, meaning dragging left-and-right on them scrub the GIF backward and forwards.
To get super meta, here’s a GIF of me playing with a „rubbable” GIF:
The squishing is just what the GIF is of, it’s the ability to scrub through the GIFs frames that is interesting here. Well, squishing Steve Buscemi’s head is interesting too, but you know what I mean.
Related
A decade ago, I remember my old boss Tim Chatman designing a paper flip book thing for a client of ours.
It’s not Photicular, but it uses the same kind of slotted paper tabs you pull to make the images change.
I’m also reminded of one of my own favorite childhood books, Dinnertime, where different animals pop out at you as you turn the pages.
Modules, as in, this kind of syntax right in JavaScript:
import { myCounter, someOtherThing } from 'utilities';
Which we’d normally use Webpack to bundle, but now is supported in Safari Technology Preview, Firefox Nightly (flag), and Edge.
It’s designed to support progressive enhancement, as you can safely link to a bundled version and a non-bundled version without having browsers download both.
Stefan Judis shows:
<!-- in case ES6 modules are supported -->
<script src="app/index.js" type="module"></script>
<!-- in case ES6 modules aren't supported -->
<script src="dist/bundle.js" defer nomodule></script>
Not bundling means simpler build processes, which is great, but forgoing all the other cool stuff a tool like Webpack can do, like „tree shaking”. Also, all those imports are individual HTTP requests, which may not be as big of a deal in HTTP/2, but still isn’t great:
Khan Academy discovered the same thing a while ago when experimenting with HTTP/2. The idea of shipping smaller files is great to guarantee perfect cache hit ratios, but at the end, it’s always a tradeoff and it’s depending on several factors. For a large code base splitting the code into several chunks (a vendor and an app bundle) makes sense, but shipping thousands of tiny files that can’t be compressed properly is not the right approach.
Preprocessing build steps are likely here to stay. Native tech can learn from them, but we might as well leverage what both are good at.
Media queries are great for changing values in sudden snaps at different screen sizes. But, combining the power of calc() and viewport units like vw and vh , we can get an awful lot of fluidity across our layouts. For this we’ll use a technique called linear interpolation.
Linear interpolation is a formula used to find a value between two points on a line. In our case those two points are CSS values, like font-sizes, margins or widths, that we want to interpolate between over a set of viewport widths.
The reason we might want to interpolate between values over a set of viewport widths is to avoid having to create multiple breakpoints to control the flow of our content when the viewport changes. Instead, we let the user’s browser calculate, according to our instructions, what values it gets. Let me explain.
The following snippet is a Sass function based on linear interpolation that we, at Aranja, have been calling between.
/**
* Computes a CSS calc function that betweens a value from
* A to B over viewport-width A to viewport-width B.
* Requires a media query to cap the value at B.
*/
@function between($to, $from, $toWidth, $fromWidth) {
$slope: ($to - $from) / ($toWidth - $fromWidth);
$base: $from - $slope * $fromWidth;
@return calc(#{$base} + #{100vw * $slope});
}
The function is used like so:
$small: 400px;
$large: 1000px;
.Container {
/* The base (smallest) value. */
padding: 20px;
/* In $small it should be 20px and in $large it should be 100px, */
/* In viewports between that its padding should be calculated */
@media (min-width: $small) {
padding: between(20px, 100px, $small, $large);
}
/* In $large we cap the value at 100px */
@media (min-width: $large) {
padding: 100px;
}
}
The code example above shows how we can between a container’s padding from being 20px in “mobile”-size to being 100px in “desktop”-size. Any size between that would get a calculated amount of padding ranging between 20 and 100 pixels. To prevent the padding from exceeding the maximum value we cap it with a breakpoint.
Try resizing the following demo to see the example in action:
The between function excels in solving spacing problems. Problems that previously we would have solved by hand using different media queries.
Example layout
The talented Carly Sylvester lent me the following wireframe she designed for a volunteer firefighter website so I get to demonstrate this technique on a real-world layout.
The design document consists of a desktop, tablet and mobile versions of the website, at 1440px, 720px and 324px respectively.
In reality, the lines between these different devices are not so clear-cut, so we’ll use the given values as target points and interpolate between them using our function.
Notice how the layout of the desktop and tablet versions are similar except for the use of white-space and font-sizes–which we’ll be able to interpolate between smoothly. When we get to our smallest target point we snap the layout into a classic full-width mobile layout.
Try browsing the demos in different screen sizes and the benefits of our function should be clear. To make the „regular” version of the website better we’d need to add more breakpoints in addition to the original three from the design document, resulting in more jumps.
Combined with the power of rem
A powerful technique we can do with the function is to between the root font-size and base our styles on rems:
The remunit refers to the rootfont-size (the font size set on the :root or html element) to determine its value and the root font-size refers to the viewport width to determine its value. We set the root font-size to be 10px at the smallest and 18px at the largest and the result is a smooth transition between the two. The smallest the card in the demo ever gets is 245px, at our $small breakpoint, and it gradually grows until it reaches its peak, 440px, at our $large breakpoint.
Closing
I am convinced that by utilizing this technique we can make the web a better experience for users browsing on devices that don’t fit into the generalization of „mobile” or „tablet” sizes. For me, at least, it also makes the development process more enjoyable as there are fewer layout-related bugs on my table.
Regarding browser support–the fact that we only need calc() and vw for the technique to work puts it at around 97% in USA and Europe, and around 84% globally.
In the demos above, for example, unsupported browsers (Which are mainly Opera Mini and UC Browser) will either use our lowest (base) values or our highest (capped) values, depending on the viewport width.
As Atomic CSS (also known as Functional CSS) has been gaining in popularity, some confusion has occurred about similar related terms. The goal of this article is to clarify this terminology.
There are other projects that use the term Atomic, including Atomic Web Design by Brad Frost. Atomic CSS is a completely separate concept from these.
Let’s start by defining Atomic CSS:
Atomic CSS is the approach to CSS architecture that favors small, single-purpose classes with names based on visual function.
There are different ways to write Atomic CSS (see variations below). One example would be this:
.bgr-blue {
background-color: #357edd;
}
The term „Atomic CSS” was coined by Thierry Koblenz in his foundational article „Challenging CSS Best Practices” in October 2013.
Some people started referring to this approach as „Functional CSS” some time later. Though there have been cases in the past where Functional CSS has been used to describe something else, today the terms Functional CSS and Atomic CSS are used interchangeably.
While Atomic CSS has been the title of an open source project which originated with Thierry’s original article, the term itself applies to the architectural philosophy, not any one particular variation (see below).
Variations of Atomic CSS
Static
Many people write Atomic CSS much like they have always written CSS. It is common to use a preprocessor to generate a library of classes with set variations that define a unit-based design system for spacing, color, typography, etc.
The advantage to this style is that because it is familiar, it has a lower barrier to entry, and is more easily understood by those who are not well-versed in CSS.
Programmatic
The Programmatic approach to Atomic CSS involves using a build tool to automatically generate styles based on what it finds in the HTML.
For example, given this:
<!-- Programmatic Atomic CSS Example -->
<div class="Bgc(#0280ae) C(#fff) P(20px)">
Lorem ipsum
</div>
This flavor of Atomic CSS uses the syntax of a function call with parameters. As demonstrated by the open source ACSS project, which uses Atomizer to programmatically generate styles, it is no longer necessary to write CSS whatsoever. Stylesheets that are generated during the build process are fully optimized with no unused styles of any sort.
Longhand/Shorthand
The longhand style favors more readable class names (see Expressive CSS and Solid), while shorthand favors brevity (see Tachyons and Basscss).
Utility classes (also referred to as Helper Classes) are easily understood, single function classes that help with common styling patterns (e.g. .clearfix, .hidden).
Many CSS architectures rely on utility classes (e.g. grids, spacing, typography) but do not necessarily fully embrace the concept of Atomic CSS.
Immutable CSS
An aspect of Atomic/Functional CSS where utility classes are never to be modified, thus producing highly dependable results.
Immutability plays an essential role in the proper execution of Atomic CSS architecture. Without it, you risk this:
.black {color:navy;}
Atomic CSS is about shifting complexity away from stylesheets and into HTML. When design changes occur in a Atomic CSS project, it is best if you have well-structured HTML templates so that, for example, you can use an editing tool to quickly change Bgc(colorOne) to Bgc(colorTwo) wherever you may need to.
Breakpoint Prefixing
This technique is a way to allow utility classes to override styles at different breakpoints to make implementation of responsive web design simple and efficient.
/* Examples of breakpoint prefixing */
.grid-12 /* Full width by default */
.m-grid-6 /* 2 column at medium screen sizes */
.l-grid-4 /* 3 column for large screen sizes */
Getting Started
Below is a list of places you can go to begin, depending on which variation you prefer.
Atomic CSS has gained in popularity particularly among those who use it for rapid prototyping and, on the other end of the spectrum, for large scale ongoing front end projects.
On the engineering side, we use inline SVGs. These have many advantages. One advantage is that SVG is a well-structured format that we can manipulate with code. Paper is also using React and has a component for inserting icons.
They:
Use a single Sketch file, checked into the repo, as the place to design and house all the icons.
The build script continues by optimizing them all and building a source of data with all the icons and their properties.
That data fuels the their <SvgIcon /> React component. (Also see our article).
They call it Papercons.
Now, whenever someone asks for an icon, we can just share a link to all the latest production icons. No more hunting, context switching, and long conversation threads.