CSS has been around for 25+ years, gaining tons of features and selectors to create great websites and apps. In this article, we'll explore popular and unique tricks to make your website stand out, plus older tricks that still work.
Some may look trivial, but they offer a lot of flexibility.
We'll also examine different layout implementations, scroll snapping, image shapes, and animation tricks. Let's dive in and explore some CSS tricks and tips you should know!
Buttery smooth carousels with scroll-snap
You can create smooth snapping product carousels with scroll snap. Steve did an awesome video a while ago about this.
Learn more about it on MDN.
/* The Essential bits */
/* have the container set the sanp type and overflow */
/* for y-axis switch x->y */
.x-slide {
scroll-snap-type: x mandatory;
overflow-x: auto;
}
/* set where you want the children to align (start/center/end) */
.item {
scroll-snap-align: start;
}
Here’s a demo showing how to do it on either the x-axis or y-axis:
See the Pen Apple like Scroll snap by Yoav Ganbar (@hamatoyogi) on CodePen.
Layout
Sticky header and footer
There are different ways to implement sticky headers and footers.
The primary use case for this might be that you want to have your navigation always visible to the user. Or you might want to have some sort of mobile-like bottom navigation and just have your brand always in view.
There are two straightforward approaches that I like:
1) Using position: sticky;
/* The Essential bit */
header {
position: sticky;
top: 0;
}
footer {
position: sticky;
bottom: 0;
}
See the Pen Sticky header and footer with position: sticky by Yoav Ganbar (@hamatoyogi) on CodePen.
2) With CSS grid
:
/* The Essential bit */
body {
display: grid;
grid-template-columns: 1fr;
grid-template-rows: auto 1fr auto;
grid-template-areas:
"header"
"main"
"footer";
}
header {
grid-area: header;
}
main {
grid-area: main;
}
footer {
grid-area: footer;
}
See the Pen Sticky header and footer with Grid by Yoav Ganbar (@hamatoyogi) on CodePen.
You might have an easier time understanding sticky positioning; after all, it’s in the name 😅.
However, one neat thing about the grid implementation is that if you remove the overflow: auto
property, the main content will push down the footer to the bottom.
That’s true for the sticky example as well, remove the position: sticky
from the footer, and it will do the same.
The grid way is a more robust solution in case you want to lay out your main content in a variety of ways.
2-column sticky scroll
Props to Tom for making this concept easy to understand and implement.
<body>
<header>
On top
</header>
<section>
<div class="title-section">
On Left
</div>
<div class="c-sections">
<div class="content-section">content</div>
<div class="content-section">on right side</div>
<div class="content-section">to scroll through</div>
</div>
</section>
<footer></footer>
</body>
/* The Essential bits */
/* make the content cover the viewport */
header,
.title-section,
.content-section {
height: 100vh;
}
/* create 2 colums for the section element */
section {
display: grid;
grid-template-columns: 1fr 1fr;
}
/* make the left side stick to the top which allows the right side to scroll */
.title-section {
position: sticky;
top: 0;
}
See the Pen sticky scroll by Yoav Ganbar (@hamatoyogi) on CodePen.
Animation with CSS Custom properties
There are some caveats to using CSS vars in your design systems. But overall, they have been a game changer in web development.
Defining a variable is pretty straightforward, and is done like this:
:root {
--color-primary: dodgerblue;
}
/* Then we could use it, like so: */
.my-class {
color: var(--color-primary);
}
/* We could also add a fallback.
Here, we have forgot to declare --color-secondary,
so the background color would be hotpink.
*/
.my-class {
background-color: var(--color-secondary, hotpink);
}
The animation-delay
trick
You can do awesome things with CSS variables like control complex animations.
In the demo below, the secret ingredient is threefold:
-
animation-delay
with a negative value, causes the animation to start immediately. -
animation-play-state
set topaused
initially, to only start when we want/need to. -
The
--progress
custom property to allow control over the animation.
/* The essentials */
.my-element {
/* Setup */
animation-name: spin;
animation-timing-function: linear;
/* Here's the magic */
animation-play-state: paused;
animation-duration: 1s;
animation-delay: calc(var(--progress) * -1s);
/* These clean up some weirdness */
animation-iteration-count: 1;
animation-fill-mode: both;
}
Note, that in the below demo, there are more custom properties that control the color changes of the box and, of course, a little JavaScript to set our --progress
style on the element:
const root = document.querySelector("main");
const input = root.querySelector("input");
const animated = root.querySelector("#animated");
input.addEventListener("input", (event) => {
const min = Number(event.target.getAttribute("min"));
const max = Number(event.target.getAttribute("max"));
const value = Number(event.target.value);
const progress = (value - min) / max;
animated.style.setProperty("--progress", `${progress}`);
});
See the Pen Controlling complex animations with custom properties by Yoav Ganbar (@hamatoyogi) on CodePen.
This is another demo that shows this method in practice, but in parallax style with a JS scroll listener:
See the Pen Bind CSS keyframe animation to scroll by Scott Kellum (@scottkellum) on CodePen.
The drag-and-drop headless CMS
Builder.io is a headless CMS that lets you drag and drop with your components.
// Dynamically render your components
export function MyPage({ json }) {
return <BuilderComponent content={json} />
}
registerComponents([MyHero, MyProducts])
Visually build with your components
Builder.io is a headless CMS that lets you drag and drop with your components.
// Dynamically render your components
export function MyPage({ json }) {
return <BuilderComponent content={json} />
}
registerComponents([MyHero, MyProducts])
Hover effects with box-shadow
There are a few keys to this trick::
box-shadow
values are:[inset?] [top] [left] [blur] [size] [color];
- To get a solid fill,
blur
should be0
- Using
inset
allows to “fill” our element - Negative values flip
top
/left
tobottom
/right
and vice versa - Multiple shadows can be stacked
- When animating multiple shadows, to achieve a smooth animation - keep the same number of shadows on hover/focus as non-hover/focus.
Here’s how a “fill on hover” animation looks like:
<button class="fill">Fill In</button>
.fill {
--color: #a972cb;
--hover: #cb72aa;
}
.fill:hover,
.fill:focus {
box-shadow: inset 0 0 0 2em var(--hover);
}
button {
color: var(--color);
transition: 0.25s;
}
button:hover, button:focus {
border-color: var(--hover);
color: #fff;
}
button {
background: none;
border: 2px solid;
font: inherit;
line-height: 1;
margin: 0.5em;
padding: 1em 2em;
}
Check out some more cool animations with this method here:
See the Pen Button hover effects with box-shadow by Giana (@giana) on CodePen.
Fixing nested border-radius with calculations
Syntax.FM’s Wes Bos has been doing some great videos over on TikTok. In one of his vids, he fixed Twitter’s new profile pics border radius for nested elements with this nifty trick:
.card-outer {
--radius: 20px;
--border: 5px;
/* Outer = Inner + space between */
border-radius: calc(var(--radius) + var(--border));
}
.card-inner {
margin: var(--border);
border-radius: var(--radius);
}
Centering an element both horizontally and vertically
Funny enough, this is still one of the most popular questions on stack overflow. Even Dan Abramov struggled with this in his mock interview with Ben Awad.
The old way
Before the introduction of flex-box
the way to achieve this was by using absolute positioning.
The general trick is so:
<div class="parent">
<div class="child">Center me</div>
</div>
.parent {
position: relative;
}
.child {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
}
See the Pen Centering an element: absolute positioning by Yoav Ganbar (@hamatoyogi) on CodePen.
The (now) common way
Flexbox is the most robust way to achieve centering nowadays, it’s even the example you get If you look at MDN.
<div class="parent">
<div class="child">Center me</div>
</div>
.parent {
display: flex;
justify-content: center;
align-items: center;
}
See the Pen Centering with flexbox by Yoav Ganbar (@hamatoyogi) on CodePen.
Flexbox works by laying out its children. All we had to do to center it is use the properties that tell it to position the child in the horizontal center with justify-content
and vertical center with align-items
.
This is a much cleaner approach than the old way, but there’s a newer way.
Hipster-centering using grid
This might be the shortest way to achieve this feat.
<div class="parent">
<div class="child">Center me</div>
</div>
.parent {
display: grid;
place-content: center;
}
See the Pen Centering an element with Grid by Yoav Ganbar (@hamatoyogi) on CodePen.
Once the parent is declared as a grid we have this nifty property (place-content
) that just tells the browser to put the child in the center. If we’d add a sibling next to our <div>
it would also be centered and placed right under it. Grid layout is supported on all major browsers nowadays, so why don’t you give it a try?
Backdrop filters
Backdrop filters are cool and can be used for neomorphisim glass effect with backdrop-filter: blur
which is apparently not cool anymore.
Check out the documentation for backdrop-filter to learn more.
See the Pen backdrop-filter demo by Robin Rendle (@robinrendle) on CodePen.
Handling image styles
Images on the web have come a long way, but a few good properties are a must-know:
object-fit
: helps with not stretching out images, as in this Wes Bos TikTok.aspect-ratio
: as the name suggests, sets the preferred aspect ratio for boxes. You can get16:9
or3:4
boxes without the padding aspect ratio hack. Again, Wes Bos has a great video showcasing this and more.
Shapes
There are a few ways to create shapes in CSS. The most common way is with borders and using the :before
and :after
pseudo classes.
You can use clip-path
to cut out shapes on any element.
For example, you can make a star shape with borders:
#star-five {
margin: 50px 0;
position: relative;
display: block;
color: red;
width: 0px;
height: 0px;
border-right: 100px solid transparent;
border-bottom: 70px solid red;
border-left: 100px solid transparent;
transform: rotate(35deg);
}
#star-five:before {
border-bottom: 80px solid red;
border-left: 30px solid transparent;
border-right: 30px solid transparent;
position: absolute;
height: 0;
width: 0;
top: -45px;
left: -65px;
display: block;
content: '';
transform: rotate(-35deg);
}
#star-five:after {
position: absolute;
display: block;
color: red;
top: 3px;
left: -105px;
width: 0px;
height: 0px;
border-right: 100px solid transparent;
border-bottom: 70px solid red;
border-left: 100px solid transparent;
transform: rotate(-70deg);
content: '';
}
Or you can achieve the same with clip-path
:
#star-five {
clip-path: polygon(50% 0%, 61% 35%, 98% 35%, 68% 57%, 79% 91%, 50% 70%, 21% 91%, 32% 57%, 2% 35%, 39% 35%);
}
When I find myself in need of a shape, I usually use this CSS clip-path maker by Bennett Feely.
svg
has an equivalent <clipPath>
element that you can use for the same results, as in this demo:
See the Pen SVG image clip-path + regular clip-path by Yoav Ganbar (@hamatoyogi) on CodePen.
Debugging CSS
Knowing how to approach debugging is paramount. In fact, this tweet is what inspired this post:
Console.log in CSS
The key ways that came from the responses were:
/* 1 */
.element {
border: 1px solid red;
}
/* 2 */
.element {
background-color: red;
}
/* 3 */
.element {
outline: 1px solid red;
}
I use 2 and 3, as the first does add a pixel to the element's size. Why red? ¯\_(ツ)_/¯
DevTools du jour
The devtools in all major browsers have improved immensely since the days of Firebug. You can debug z-index
issues with the layers pane. Changing CSS in the browser has never been easier, along with auto-complete. A few more cool features are:
- Color picker
- Flexbox editing
- Clip path editor
- Animation editor
- Device toolbar
Each deserves a post of its own, but if you’d like to learn more check out the Google Chrome Devtools docs.
Bonus: style your console.log()
Did you know you can add CSS properties to your JavaScript console.log()
?
This is how you do it:
console.log(
// What comes after %c is what the styles will apply to
"This is %cMy stylish message",
// you can add multiple properties:
"color: yellow; font-style: italic; background-color: blue;padding: 2px"
);
Outputs:
// You could also style different parts of the console with multipule %c's:
console.log(
"Multiple styles: %cred %corange",
// style for first %c
"color: red",
// style for second %c
"color: orange",
// for every %c you can add more styles with ","
"Additional unformatted message"
);
Outputs:
For more information, see the MDN console
documentation.
Selectors
Lately, some really awesome new selectors have been added to the CSS spec.
The three most popular and exciting are:
is:
Builder CEO, Steve Sewell, went into detail about this in Simpler Selectors With is:().where:
Tailwind’s typography plugin useswhere:
to achieve formatting of almost any HTML. It can also be helpful for selecting multiple children inside a parent selector.has:
- A highly anticipated selector function you can use as the elusive parent selector. We’re still waiting for Firefox on this one.
Other than the latest and greatest, it’s useful to remember some more handy selectors such as the attribute selector, :nth-child
, which can group selectors with ”,
" and understand descedant combinators.
MDN features many more to add to your repertoire.
Just remember that there could be a performance cost depending on the selectors you choose to use.
Some more points to remember
100vh
is not really all the viewport on mobile. When the new viewport units get support, that will be a great choice.- You’re probably going to need JavaScript for animations. Just as we’ve seen in some of the animation examples. For a clean API and great perf with a small bundle check out Motion One (the vanilla JS equivalent to React's Framer-Motion — fun fact: they were written by the same person, Matt Perry).
- If you get good with CSS you can create awesome stuff like these hovering cards below, which are nothing more than highly crafted CSS by someone who knows their stuff.
See the Pen Card Hover Interactions by Ryan Mulligan (@hexagoncircle) on CodePen.
Conclusion
Knowing CSS well is a superpower. From my experience, when you get stuck with CSS, it’s much more time-consuming than a JavaScript bug.
There’s always way more to learn and understand deeply, and if you do find the time, I suggest you focus on the fundamentals. The new stuff can wait.
I hope something from all these tricks stuck with you and you might have learned something new.
If you do want to keep up with some great resources and blogs that cover CSS, here are some that I enjoy:
About me
Hi! I’m Yoav, I do DevRel and Developer Experience at Builder.io.
We make a way to drag + drop with your components to create pages and other CMS content on your site or app, visually.
You can read more about how this can improve your workflow here.
You may find it interesting and useful!
Introducing Visual Copilot: convert Figma designs to high quality code in a single click.