India has always been a country with an ambitious goal. And this time, the country has announced an even more ambitious goal: to install 40 gigawatts (GW) of rooftop solar power plants by 2022. That’s more than the current installed capacity of all solar power plants in India.
This new target is part of India’s goal to achieve 175 GW of renewable energy by 2022. This would make up around 40% of the country’s installed capacity and help reduce India’s dependence
That is why India is planning to set up a large number of rooftop solar plants with a cumulative capacity of 20 gigawatts (GW) in the next five years. This would be in addition to the 20 GW of solar power that the country has planned to set up by 2022.
The new target was announced by Minister of State for Power, Coal, New and Renewable Energy and Mines Piyush Goyal on the sidelines of the International Solar Alliance (ISA) summit in New Delhi on Sunday.
“We have set a target of having 40 GW of rooftop solar power installed in the country in the next five years
Global styles, a feature of the block themes, is one of my favorite parts of creating block themes. The concept of global style variations in WordPress were introduced in Gutenberg 12.5 which would allow theme authors to create alternate variations of a block theme with different combinations of colors, fonts, typography, spacing, etc. Different theme.json files stored under /styles folder “lets users quickly and easily switch between different looks in the same theme.”
The global styles panel UI is in active development iteration. More details on the development of this feature can be found and tracked here at this GitHub ticket (#35619).
In this article, I will walk through creating a proof-of-concept global style variation using alternate /styles/theme.json files and create child themes with different color modes by swapping color palettes only.
This article is intended for those who have basic understanding of WordPress block themes and some familiarity of using Full Site Editor (FSE) interface. If you’re new to block themes and the FSE, you can get started here on CSS-Tricks with this deep introduction to WordPress block themes and site editor documentation. This Full Site Editing website is one of the most up-to-date tutorial guides to learn all FSE features including block themes and styles variations discussed in this article.
Global style variations
For some background, let’s briefly overview global style variation. Twenty Twenty-Two (TT2) theme lead and Automattic design director Kjell Reigstad introduced global styles variations with this tweet and GitHub ticket #292 as child themes. In the ticket, Kjell notes that they were initially intended as alternate color patterns and fonts combinations, but they can be used for building simple child themes.
Since then, the Automattic theme team has been experimenting with the concept to create variable child themes (variable color and fonts only), including the following:
geologist with blue, cream, slate, yellow variations
quadrat with black, green, red, white, and yellow versions
Global style switcher
The Gutenberg 12.5 release has introduced a global styles switcher which would allow users quickly and easily switch between different looks in the same theme via different theme.json files stored under a /styles folder.
The concept of allowing switching global style variation via theme.json has been discussed on GitHub for a while now. Gutenberg lead engineer Matias Ventura gave renewed importance to it by adding it to the WordPress 6.0 roadmap recently.
Embrace style alternates driven by json variations. This was teased in various videos around the new default theme and should be fully unveiled and presented in 6.0. One of the parallel goals is to create a few distinct variations of TT2 made just with styles. (35619)
The latest development iteration of theme style variation switcher is available with Gutenberg 13.0 and included in WordPress 6.0. In this Exploring WordPress 6.0 video, Automattic product liaison Anne McCarthy provides an overview of its major features, including style variations and Webfonts API (starting 5:18) discussed in this article.
Theme style variation versus child theme
In my previous article, I briefly covered building block child themes. Global style variations have blurred the line between alternate-theme.json and child themes. For example, the only difference between a recently released alante-dark child theme and its parent theme is an alternate.json file in the child theme that overrides the global theme styles like this:
At the root of your child theme folder, create a /styles folder, which holds style variations as JSON files. For this demo example, I created three variations of Twenty Twenty-Two’s theme.json color palettes — blue.json, maroon.json, and pink.json — by swapping the foreground and background colors:
The child theme file structure of “blue.json”, “maroon.json”, and “pink.json” in the styles directory.
Here is the final result after clicking the styles icon from the admin dashboard (located at Appearance → Editor):
Walking through the WordPress admin interface to select the “blue”, “maroon”, and “pink” styles.
Click the Other Styles button (recently revised to Browser styles), which displays “blue”, “maroon”, and “pink” color style icons in addition to its original styles.
To change and choose a style, select your preferred variation and click the Save button (top-right), which is displayed on the front end of your browser.
First, install and set up a WordPress site with some dummy content. For this demo, I made a fresh WordPress install, activated Twenty Twenty-Two theme, and added Gutenberg test data.
The theme style variations and WebFonts API discussed in this article require installation and activation of the Gutenberg 13.0 plugin or WordPress 6.0.
Step 2: Create a TT2 child theme
In this demo child theme example, let’s slightly vary the body color from the header and footer color, with all site content centered:
Screenshot of the default appearance of the demo theme in a browser window.
Step 3: Create JSON files
Create /styles in your child theme’s root folder with blue, maroon, and pink.json files:
Next up, create your alternate-theme.json files with desired color pallets under /styles folder. For this demo example, I created three color palettes (blue, maroon, and pink). Here is the code for maroon.json:
The other two alternate blue.json and pink.json files swap values of foreground and background-body, foreground-dark and primarycolor properties with their respective blue and pink hex color values.
Section 2: An example of a use case
As I noted in my previous article, I have been working on block themes and using them for my own personal project site. Inspired by the theme style variations and Webfonts API features in Gutenberg plugin, I started tweaking my work-in-progress block theme with an alternate dark color mode and by configuring the Webfonts API.
In this section, I will walk you through how I created TT2 Gopher Blocks, a demo sibling of my work-in-progress block theme created for this article. The theme includes maroon, dark, and light color modes created using theme style variations and Webfonts API that became available with the Gutenberg 12.8 release.
Screenshot displaying a sample site using the TT2 Gopher theme with maroon default color.
Some highlights of the TT2 Gopher theme include centered, single-column content display, distinct header and footer, more user-friendly archive and search pages.
A copy of TT2 Gopher Blocks is available at the GitHub repository, which you can fork and customize.
Creating a dark mode in WordPress block themes without a plugin involves several steps. Over a year ago, Ari Stathopoulos created a dark support for the TT1 Blocks theme at the GitHub. Looking at the example here, it involves some JavaScript knowledge to create assets (e.g., toggler, customize, editor-mode-support), dark color CSS variables, and expanded functions.php files.
In this short video, Automattic’s Anne McCarthy demonstrates how simple it is to create a dark mode of TT2 block theme with global style variation by adding kllejr’s gist of JSON snippets in the TT2 /styles folder.
Creating the demo TT2 Gopher blocks theme
The TT2 Gopher is a very simple and modified version of the default Twenty Twenty-Two theme. It includes three theme style variations — maroon, dark, and white.
Let me briefly walk you through how I created theme style variations.
Adding and configuring webfonts
The Gutenberg 12.8 plugin introduced a new Webfonts API that allows the authors to load local (bundled) fonts “in a performance-friendly, privacy-friendly, and future-proof manner.” This feature can be implemented in a block theme the PHP way or the theme.json way.
The TT2 theme adds Source Serif Pro font files to the theme’s assets/fonts folder. Two additional fonts — Work Sans and Public Sans — are also provided in he GitHub repository.
Step 2: Registering webfonts
In the TT2 theme, local Source Serif Pro webfonts are registered with PHP in its functions.php file:
Gutenberg 12.8 introduced the ability to register local web fonts with theme.json file. The following theme.json snippets from the demo TT2 Gopher theme show how local Work Sans web fonts are registered in the maroon theme style variation:
Following the steps described in the previous section, I created two alternate versions of the theme.json file — white.json and black.json — with different color and fonts combinations inside the child theme’s /styles folder.
This feature requires version 2 of theme.json. Since Gutenberg 12.5, title can also be added at theme.json to display style label in the site editor or file name (without extension) will be displayed by default.
This code swaps color palettes from theme.json and also registers and defines the local Public Sans font files.
The black.json is also very similar and uses Source Serif Pro fonts registered in the functions.php file.
Side-by-side comparison of the light (left) and dark (right) color themes for TT2 Gopher.
Example of block themes with theme styles variations
Twenty Twenty-Two – the first default theme to include style variations. Its updated 1.2, bundled with WordPress 6.0 includes three style variations — “Blue”, “Pink”, and “Swiss” — allowing users to quickly swap between different visual styles.
Frost – an experimental block theme with dark theme style variation.
Alara – has above 100 active installs and includes 7 style variations.
Wabi– which powers Rich Tabor website contains 3 style variants and 300+ active installations.
Brisky – has more than 600 installs and one dark theme style variation.
Pendant – a theme by Automattic theme team under development at GitHub contains 3 theme style variation.
In this WP Tavern article, Justin speculates that this new feature may be utilized by theme authors by tying to the site visitor’s settings, while some users may prefer to tweak their site giving a seasonal or event-based design look. This is probably a little early, but only time will tell how this powerful feature would be utilized by both theme authors and users.
Wrapping up
Creating style variations of a block theme with different typography and color combination has been greatly simplified, without using plugins. It’s one of my favorite feature of the block editor that I plan to apply in my personal projects.
In my opinion, theme style variations are definitely a game changer for block themes and with this handy feature there might not be a need for child themes or even many cooky-cutter block themes. A few well-designed base block themes, similar to Automattic theme team’s block-canvas or blockbase (work-in-progress base block themes at GitHub), could be customized with theme style variation.
In my last article we saw how CSS background properties allow us to create cool hover effects. This time, we will focus on the CSS text-shadow property to explore even more interesting hovers. You are probably wondering how adding shadow to text can possibly give us a cool effect, but here’s the catch: we’re not actually going to make any shadows for these text hover effects.
text-shadow but no text shadows?
Let me clear the confusion by showing the hover effects we are going to build in the following demo:
CodePen Embed Fallback
Without looking at the code many of you will, intuitively, think that for each hover effect we are duplicating the text and then independently animating them. Now, if you check the code you will see that none of the text is actually duplicated in the HTML. And did you notice that there is no use of content: "text" in the CSS?
The text layers are completely made with text-shadow!
The first thing to notice is that I am making the color of the actual text transparent (using #0000) in order to hide it. After that, I am using text-shadow to create two shadows where I am defining only two length values for each one. That means there’s no blur radius, making for a sharp, crisp shadow that effectively produces a copy of the text with the specified color.
That’s why I was able to claim in the introduction that there are no shadows in here. What we’re doing is less of a “classic” shadow than it is a simple way to duplicate the text.
We have two text layers that we move on hover. If we hide the overflow, then the duplicated text is out of view and the movement makes it appear as though the actual text is being replaced by other text. This is the main trick that that makes all of the examples in this article work.
Let’s optimize our code. I am using the value 1.2em a lot to define the height and the offset of the shadows, making it an ideal candidate for a CSS custom property (which we’re calling --h):
We can still go further and apply more calc()-ulations to streamline things to where we only use the text-shadow once. (We did the same in the previous article.)
In case you are wondering why I am adding an underscore to the --_t variable, it’s just a naming convention I am using to distinguish between the variables we use to control the effect that the user can update (like --h) and the internal variables that are only used for optimization purposes that we don’t need to change (like --_t ). In other words, the underscore is part of the variable name and has no special meaning.
We can also update the code to get the opposite effect where the duplicated text slides in from the top instead:
CodePen Embed Fallback
All we did is a small update to the text-shadow property — we didn’t touch anything else!
Hover effect #2
CodePen Embed Fallback
For this one, we will animate two properties: text-shadow and background. Concerning the text-shadow, we still have two layers like the previous example, but this time we will move only one of them while making the color of the other one transparent during the swap.
On hover, we move the white text layer to the top while changing the color of the other one to transparent. To this, we add a background-size animation applied to a gradient:
CodePen Embed Fallback
And finally, we add overflow: hidden to keep the animation only visible inside the element’s boundaries:
What we’ve done here is combine the CSS text-shadow and background properties to create a cool hover effect. Plus, we were able to use CSS variables to optimize the code.
If the background syntax looks strange to you, I highly recommend reading my previous article. The next hover effect also relies on an animation I detailed in that article. Unless you are comfortable with CSS background trickery, I’d suggest reading that article before continuing this one for more context.
In the previous article, you show us how to use only one variable to create the hover effect — is it possible to do that here?
Yes, absolutely! We can indeed use that same DRY switching technique so that we’re only working with a single CSS custom property that merely switches values on hover:
This hover effect is nothing but a combination of two effects we’ve already made: the secondhover effect of the previous article and the first hover effect in this article.
.hover-3 {
/* the color */
--c: #1095c1;
/* the height */
--h: 1.2em;
/* The first hover effect in this article */
line-height: var(--h);
color: #0000;
overflow: hidden;
text-shadow:
0 calc(-1 * var(--_t, 0em)) var(--c),
0 calc(var(--h) - var(--_t, 0em)) #fff;
/* The second hover effect from the previous article */
background:
linear-gradient(var(--c) 0 0) no-repeat
calc(200% - var(--_p, 0%)) 100% / 200% var(--_p, .08em);
transition: .3s var(--_s, 0s), background-position .3s calc(.3s - var(--_s, 0s));
}
.hover-3:hover {
--_t: var(--h);
--_p: 100%;
--_s: .3s
}
All I did was copy and paste the effects from those other examples and make minor adjustments to the variable names. They make for a neat hover effect when they’re combined! At first glance, such an effect may look complex and difficult but, in the end, it’s merely two relatively easy effects made into one.
Optimizing the code with the DRY switching variable technique should also be an easy task if we consider the previous optimizations we’ve already done:
This hover effect is an improvement of the second one. First, let’s introduce a clip-path animation to reveal one of the text layers before it moves:
CodePen Embed Fallback
Here’s another illustration to better understand what is happening:
Initially, we use inset(0 0 0 0) which is similar to overflow: hidden in that all we see is the actual text. On hover, we update the the third value (which represent the bottom offset) using a negative value equal to the height to reveal the text layer placed at the bottom.
From there, we can add this to the second hover effect we made in this article, and this is what we get:
CodePen Embed Fallback
We are getting closer! Note that we need to first run the clip-path animation and then everything else. For this reason, we can add a delay to all of the properties on hover, except clip-path:
transition: 0.4s 0.4s, clip-path 0.4s;
And on mouse out, we do the opposite:
transition: 0.4s, clip-path 0.4s 0.4s;
The final touch is to add a box-shadow to create the sliding effect of the blue rectangle. Unfortunately, background is unable to produce the effect since backgrounds are clipped to the content area by default. Meanwhile, box-shadow can go outside the content area.
If you look closely at the box-shadow, you will see it has the same values as the white text layer inside text-shadow. This is logical since both need to move the same way. Both will slide to the top. Then the box-shadow goes behind the element while text-shadow winds up on the top.
Here is a demo with some modified values to visualize how the layers move:
CodePen Embed Fallback
Wait, The background syntax is a bit different from the one used in the second hover effect!
Good catch! Yes, we are using a different technique with background that produces the same effect. Instead of animating the size from 0% to 100%, we are animating the position.
If we don’t specify a size on our gradient, then it take up the full width and height by default. Since we know the height of our element (--h) we can create a sliding effect by updating the position from 0 var(--h) to 0 0.
We could have used the background-size animation to get the same effect, but we just added another trick to our list!
In the demos, you also used inset(0 0 1px 0)… why?
I sometimes add or remove a few pixels or percentages here and there to refine anything that looks off. In this case, a bad line was appearing at the bottom and adding 1px removed it.
What about the DRY switch variable optimization?
I am leaving this task for you! After those four hover effects and the previous article, you should be able to update the code so it only uses one variable. I’d love to see you attempt it in the comments!
Your turn!
Let me share one last hover effect which is another version of the previous one. Can you find out how it’s done without looking at the code? It’s a good exercise, so don’t cheat!
CodePen Embed Fallback
Wrapping up
We looked at a bunch of examples that show how one element and few lines of CSS are enough to create some pretty complex-looking hover effects on text elements — no pseudo elements needed! We were even able to combine techniques to achieve even more complex animations with a small amount of effort.
If you’re interested in going deeper than the four text-shadow hover effects in this article, check my collection of 500 hover effects where I am exploring all kinds of different techniques.
Braydon Coyer recently launched a monthly CSS art challenge. He actually had reached out to me about donating a copy of my book Move Things with CSS to use as a prize for the winner of the challenge — which I was more than happy to do!
The first month’s challenge? Spring. And when thinking of what to make for the challenge, Slinkys immediately came to mind. You know Slinkys, right? That classic toy you knock down the stairs and it travels with its own momentum.
A slinking Slinky
Can we create a Slinky walking down stairs like that in CSS? That’s exactly the sort of challenge I like, so I thought we could tackle that together in this article. Ready to roll? (Pun intended.)
Setting up the Slinky HTML
Let’s make this flexible. (No pun intended.) What I mean by that is we want to be able to control the Slinky’s behavior through CSS custom properties, giving us the flexibility of swapping values when we need to.
Here’s how I’m setting the scene, written in Pug for brevity:
Those inline custom properties are an easy way for us to update the number of rings and will come in handy as we get deeper into this challenge. The code above gives us 10 rings with HTML that looks something like this when compiled:
We’re going to need some styles! What we want is a three-dimensional scene. I’m mindful of some things we may want to do later, so that’s the thinking behind having an extra wrapper component with a .scene class.
Let’s start by defining some properties for our “infini-slinky” scene:
These properties define the characteristics of our Slinky and the scene. With the majority of 3D CSS scenes, we’re going to set transform-style across the board:
Now we need styles for our .scene. The trick is to translate the .plane so it looks like our CSS Slinky is moving infinitely down a flight of stairs. I had to play around to get things exactly the way I want, so bear with the magic number for now, as they’ll make sense later.
.container {
/* Define the scene's dimensions */
height: var(--scene-size);
width: var(--scene-size);
/* Add depth to the scene */
transform:
translate3d(0, 0, 100vmin)
rotateX(-24deg) rotateY(32deg)
rotateX(90deg)
translateZ(calc((var(--depth) + var(--stack-height)) * -1))
rotate(0deg);
}
.scene,
.plane {
/* Ensure our container take up the full .container */
height: 100%;
width: 100%;
position: relative;
}
.scene {
/* Color is arbitrary */
background: rgb(162 25 230 / 0.25);
}
.plane {
/* Color is arbitrary */
background: rgb(25 161 230 / 0.25);
/* Overrides the previous selector */
transform: translateZ(var(--depth));
}
There is a fair bit going on here with the .container transformation. Specifically:
translate3d(0, 0, 100vmin): This brings the .container forward and stops our 3D work from getting cut off by the body. We aren’t using perspective at this level, so we can get away with it.
rotateX(-24deg) rotateY(32deg): This rotates the scene based on our preferences.
rotateX(90deg): This rotates the .container by a quarter turn, which flattens the .scene and .plane by default, Otherwise, the two layers would look like the top and bottom of a 3D cube.
translate3d(0, 0, calc((var(--depth) + var(--stack-height)) * -1)): We can use this to move the scene and center it on the y-axis (well, actually the z-axis). This is in the eye of the designer. Here, we are using the --depth and --stack-height to center things.
rotate(0deg): Although, not in use at the moment, we may want to rotate the scene or animate the rotation of the scene later.
To visualize what’s happening with the .container, check this demo and tap anywhere to see the transform applied (sorry, Chromium only. 😭):
CodePen Embed Fallback
We now have a styled scene! 💪
CodePen Embed Fallback
Styling the Slinky’s rings
This is where those CSS custom properties are going to play their part. We have the inlined properties --index and --ring-count from our HTML. We also have the predefined properties in the CSS that we saw earlier on the :root.
The inline properties will play a part in positioning each ring:
Take note of how we are calculating the --origin-z value as well as how we position each ring with the transform property. That comes after positioning each ring with position: absolute .
It is also worth noting how we’re alternating the color of each ring in that last ruleset. When I first implemented this, I wanted to create a rainbow slinky where the rings went through the hues. But that adds a bit of complexity to the effect.
Now we’ve got some rings on our raised .plane:
CodePen Embed Fallback
Transforming the Slinky rings
It’s time to get things moving! You may have noticed that we set a transform-origin on each .ring like this:
Why that transform-origin? Well, we need the ring to look like is moving off-center. Playing with the transform of an individual ring is a good way to work out the transform we want to apply. Move the slider on this demo to see the ring flip:
CodePen Embed Fallback
Add all the rings back and we can flip the whole stack!
CodePen Embed Fallback
Hmm, but they aren’t falling to the next stair. How can we make each ring fall to the right position?
Well, we have a calculated --origin-z, so let’s calculate --destination-z so the depth changes as the rings transform. If we have a ring on top of the stack, it should wind up at the bottom after it falls. We can use our custom properties to scope a destination for each ring:
But that’s only because we aren’t using animation-delay. All the rings are, um, slinking at the same time. Let’s introduce an animation-delay based on the --index of the ring so they slink in succession.
OK, that is indeed “better.” But the timing is still off. What sticks out more, though, is the shortcoming of animation-delay. It is only applied on the first animation iteration. After that, we lose the effect.
CodePen Embed Fallback
At this point, let’s color the rings so they progress through the hue wheel. This is going to make it easier to see what’s going on.
Back to the issue. Because we are unable to specify a delay that’s applied to every iteration, we are also unable to get the effect we want. For our Slinky, if we were able to have a consistent animation-delay, we might be able to achieve the effect we want. And we could use one keyframe while relying on our scoped custom properties. Even an animation-repeat-delay could be an interesting addition.
This functionality is available in JavaScript animation solutions. For example, GreenSock allows you to specify a delay and a repeatDelay.
But, our Slinky example isn’t the easiest thing to illustrate this problem. Let’s break this down into a basic example. Consider two boxes. And you want them to alternate spinning.
CodePen Embed Fallback
How do we do this with CSS and no “tricks”? One idea is to add a delay to one of the boxes:
But, that won’t work because the red box will keep spinning. And so will the blue one after its initial animation-delay.
CodePen Embed Fallback
With something like GreenSock, though, we can achieve the effect we want with relative ease:
import gsap from 'https://cdn.skypack.dev/gsap'
gsap.to('.box', {
rotate: 360,
/**
* A function based value, means that the first box has a delay of 0 and
* the second has a delay of 1
*/
delay: (index) > index,
repeatDelay: 1,
repeat: -1,
ease: 'power1.inOut',
})
And there it is!
CodePen Embed Fallback
But how can we do this without JavaScript?
Well, we have to “hack” our @keyframes and completely do away with animation-delay. Instead, we will pad out the @keyframes with empty space. This comes with various quirks, but let’s go ahead and build a new keyframe first. This will fully rotate the element twice:
It’s like we’ve cut the keyframe in half. And now we’ll have to double the animation-duration to get the same speed. Without using animation-delay, we could try setting animation-direction: reverse on the second box:
The rotation is the wrong way round. We could use a wrapper element and rotate that, but that could get tricky as there are more things to balance. The other approach is to create two keyframes instead of one:
It would make for an interesting addition for sure!
So, we need keyframes for all those rings?
Yes, that is, if we want a consistent delay. And we need to do that based on what we are going to use as the animation window. All the rings need to have “slinked” and settled before the keyframes repeat.
This would be horrible to write out by hand. But this is why we have CSS preprocessors, right? Well, at least until we get loops and some extra custom property features on the web. 😉
Today’s weapon of choice will be Stylus. It’s my favorite CSS preprocessor and has been for some time. Habit means I haven’t moved to Sass. Plus, I like Stylus’s lack of required grammar and flexibility.
Good thing we only need to write this once:
// STYLUS GENERATED KEYFRAMES BE HERE...
$ring-count = 10
$animation-window = 50
$animation-step = $animation-window / $ring-count
for $ring in (0..$ring-count)
// Generate a set of keyframes based on the ring index
// index is the ring
$start = $animation-step * ($ring + 1)
@keyframes slink-{$ring} {
// In here is where we need to generate the keyframe steps based on ring count and window.
0%, {$start * 1%} {
transform
translate3d(-50%, -50%, var(--origin-z))
translateZ(0)
rotateY(0deg)
}
// Flip without falling
{($start + ($animation-window * 0.75)) * 1%} {
transform
translate3d(-50%, -50%, var(--origin-z))
translateZ(0)
rotateY(180deg)
}
// Fall until the cut-off point
{($start + $animation-window) * 1%}, 100% {
transform
translate3d(-50%, -50%, var(--origin-z))
translateZ(var(--destination-z))
rotateY(180deg)
}
}
Here’s what those variables mean:
$ring-count: The number of rings in our slinky.
$animation-window: This is the percentage of the keyframe that we can slink in. In our example, we’re saying we want to slink over 50% of the keyframes. The remaining 50% should get used for delays.
$animation-step: This is the calculated stagger for each ring. We can use this to calculate the unique keyframe percentages for each ring.
Here’s how it compiles to CSS, at least for the first couple of iterations:
The last thing to do is apply each set of keyframes to each ring. We can do this using our markup if we want by updating it to define both an --indexand a --name:
Timing is everything. So we’ve ditched the default animation-timing-function and we’re using a cubic-bezier. We’re also making use of the --speed custom property we defined at the start.
Aw yeah. Now we have a slinking CSS Slinky! Have a play with some of the variables in the code and see what different behavior you can yield.
CodePen Embed Fallback
Creating an infinite animation
Now that we have the hardest part out of the way, we can make get this to where the animation repeats infinitely. To do this, we’re going to translate the scene as our Slinky slinks so it looks like it is slinking back into its original position.
.scene {
animation: step-up var(--speed) infinite linear both;
}
@keyframes step-up {
to {
transform: translate3d(-100%, 0, var(--depth));
}
}
Wow, that took very little effort!
CodePen Embed Fallback
We can remove the platform colors from .scene and .plane to prevent the animation from being too jarring:
CodePen Embed Fallback
Almost done! The last thing to address is that the stack of rings flips before it slinks again. This is where we mentioned earlier that the use of color would come in handy. Change the number of rings to an odd number, like 11, and switch back to alternating the ring color:
CodePen Embed Fallback
Boom! We have a working CSS slinky! It’s configurable, too!
Fun variations
How about a “flip flop” effect? By that, I mean getting the Slink to slink alternate ways. If we add an extra wrapper element to the scene, we could rotate the scene by 180deg on each slink.
I like it! Of course, styling is subjective… so, I made a little app you can use configure your Slinky:
CodePen Embed Fallback
And here are the “Original” and “Flip-Flop” versions I took a little further with shadows and theming.
Final demos
CodePen Embed Fallback
CodePen Embed Fallback
That’s it!
That’s at least one way to make a pure CSS Slinky that’s both 3D and configurable. Sure, you might not reach for something like this every day, but it brings up interesting CSS animation techniques. It also raises the question of whether having a animation-repeat-delay property in CSS would be useful. What do you think? Do you think there would be some good use cases for it? I’d love to know.
Utilizing icons in user interface elements is helpful. In addition to element labeling, icons can help reinforce a user element’s intention to users. But I have to say, I notice a bit of icon misalignment while browsing the web. Even if the icon’s alignment is correct, icons often do not respond well when typographic styles for the element change.
I took note of a couple real-world examples and I’d like to share my thoughts on how I improved them. It’s my hope these techniques can help others build user interface elements that better accommodate typographic changes and while upholding the original goals of the design.
Example 1 — Site messaging
I found this messaging example on a popular media website. The icon’s position doesn’t look so bad. But when changing some of the element’s style properties like font-size and line-height, it begins to unravel.
CodePen Embed Fallback
Identified issues
the icon is absolutely positioned from the left edge using a relative unit (rem)
because the icon is taken out of the flow, the parent is given a larger padding-left value to help with overall spacing – ideally, our padding-x is uniform, and everything looks good whether or not an icon is present
the icon (it’s an SVG) is also sized in rems – this doesn’t allow for respective resizing if its parent’s font-size changes
Recommendations
Indicating the issues with aligning the icon and typography.
We want our icon’s top edge to be at the blue dashed line, but we often find our icon’s top edge at the red dashed line.
Have you ever inserted an icon next to some text and it just won’t align to the top of the text? You may move the icon into place with something like position: relative; top: 0.2em. This works well enough, but if typographic styles change in the future, your icon could look misaligned.
We can position our icon more reliably. Let’s use the element’s baseline distance (the distance from one line’s baseline to the next line’s baseline) to help solve this.
With a font-size of 1rem (16px) and line-height of 1.5, our icon will be moved 4 pixels.
baseline distance = 16px * 1.5 = 24px
icon offset = (24px – 16px) / 2 = 4px
Demo: before and after
CodePen Embed Fallback
Example 2 – unordered lists
The second example I found is an unordered list. It uses a web font (Font Awesome) for its icon via a ::before pseudo-element. There have been plenty of great articles on styling both ordered and unordered lists, so I won’t go into details about the relatively new ::marker pseudo-element and such. Web fonts can generally work pretty well with icon alignment depending on the icon used.
CodePen Embed Fallback
Identified issues
no absolute positioning used – when using pseudo-elements, we don’t often use flexbox like our first example and absolute positioning shines here
the list item uses a combination of padding and negative text-indent to help with layout – I am never able to get this to work well when accounting for multi-line text and icon scalability
Recommendations
Because we’ll also use a pseudo-element in our solution, we’ll leverage absolute positioning. This example’s icon size was a bit larger than its adjacent copy (about 2x). Because of this, we will alter how we calculate the icon’s top position. The center of our icon should align vertically with the center of the first line.
So with a font-size of 1rem (16px), a line-height of 1.6, and an icon sized 2x the copy (32px), our icon will get get a top value of -3.2 pixels.
baseline distance = 16px * 1.6 = 25.6px
icon offset = (25.6px – 32px) / 2 = -3.2px
With a larger font-size of 2rem (32px), line-height of 1.2, and 64px icon, our icon will get get a top value of -12.8 pixels.
baseline distance = 32px * 1.2 = 38.4px
icon offset = (38.4px – 64px) / 2 = -12.8px
Demo: before and after
CodePen Embed Fallback
Conclusion
For user interface icons, we have a lot of options and techniques. We have SVGs, web fonts, static images, ::marker, and list-style-type. One could even use background-colors and clip-paths to achieve some interesting icon results. Performing some simple calculations can help align and scale icons in a more graceful manner, resulting in implementations that are a bit more bulletproof.