Watch our biggest AI launch event

Announcing Visual Copilot - Figma to production in half the time

Builder.io logo
Contact Sales
Contact Sales

Blog

Home

Resources

Blog

Forum

Github

Login

Signup

×

Visual CMS

Drag-and-drop visual editor and headless CMS for any tech stack

Theme Studio for Shopify

Build and optimize your Shopify-hosted storefront, no coding required

Resources

Blog

Get StartedLogin

‹ Back to blog

Web Development

CSS Tips for Better Web Development

February 15, 2023

Written By Yoav Ganbar

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;
}

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:

  1. animation-delay with a negative value, causes the animation to start immediately.
  2. animation-play-state set to paused initially, to only start when we want/need to.
  3. 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}`);
});

This is another demo that shows this method in practice, but in parallax style with a JS scroll listener:

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::

  1. box-shadow values are: [inset?] [top] [left] [blur] [size] [color];
  2. To get a solid fill, blur should be 0
  3. Using inset allows to “fill” our element
  4. Negative values flip top / left to bottom / right and vice versa
  5. Multiple shadows can be stacked
  6. 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:

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%);
}

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:

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:

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:

a screenshot of a console output with CSS.
// 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:

a screenshot of a console output with CSS.

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:

  1. is: Builder CEO, Steve Sewell, went into detail about this in Simpler Selectors With is:().
  2. where: Tailwind’s typography plugin uses where: to achieve formatting of almost any HTML. It can also be helpful for selecting multiple children inside a parent selector.
  3. 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.

Try Visual Copilot

Share

Twitter
LinkedIn
Facebook
Hand written text that says "A drag and drop headless CMS?"

Introducing Visual Copilot:

A new AI model to turn Figma designs to high quality code using your components.

Try Visual Copilot
Newsletter

Like our content?

Join Our Newsletter

Continue Reading
AI5 MIN
Introducing Visual Copilot 2.0: Design to Interactive
October 31, 2024
Design Systems8 MIN
Design Systems Explained
October 18, 2024
Visual Editing7 MIN
Visual editing is bridging the gap between developers and designers
October 11, 2024