No JS required — you can do this with CSS!

bogdanfromkyiv

Bogdan Bendziukov

Posted on September 20, 2024

No JS required — you can do this with CSS!

The less you use JS code on your site the better performance you’ll have.

Why? Here are several reasons:

  • Lesser JavaScript means lesser TBT;
  • CSS is faster to parse and execute than JavaScript;
  • When you use JS, the layout will be re-rendered, which is not the case for CSS.

Finally, if you have an error in your CSS, it won’t stop from loading your page. While an error in JS code may (and often will) break the whole page.

Disclaimer! I’m not telling you to stop using JS, of course not 😅

But in cases when you can easily replace it with CSS — please do that.

Have a look at a few examples of useful features for your site using only CSS.

Sub menu on hover

Let’s start with something simple. To make a sub menu there’s no need to use JS. We all know this pseudoclass :hover, so don’t hesitate to use it.

Here’s a simple menu markup:

<nav>
  <ul>
    <li><a href="#">Home</a></li>
    <li><a href="#">About</a></li>
    <li><a href="#">Blog 🠋</a>
      <ul>
        <li><a href="#">News</a></li>
        <li><a href="#">Interviews</a>
        <li><a href="#">Travel</a></li>
        <li><a href="#">Art</a></li>
        <li><a href="#">Tech</a></li>
        </li>
      </ul>
    </li>
    <li><a href="#">Contacts</a></li>
  </ul>
</nav>
Enter fullscreen mode Exit fullscreen mode

Generally, to make the sub menu hidden people often use display: none; method.

I prefer to use a combination of opacity and visibility CSS properties, to make the hover appearance smooth (browsers cannot animate the display CSS property, yet). The visibility: hidden; is used to make our sub menu non-focusable until it becomes visible (until its parent element hovered).

To prevent submenu from immediate disappear on hover out of menu, we should add a transition-delay to our sub-menu.

/* don't forget to set the relative position for the parent element of the sub menu */
nav > ul > li {
  position: relative;
}
/* sub menu is hidden by default */
nav > ul ul {
  position: absolute;
  opacity: 0;
  visibility: hidden;
  translate: 0 25%;
  transition: all 250ms cubic-bezier(.33,.65,.67,.81);
  transition-delay: 300ms;
}
/* make it visible on hover */
nav > ul > li:hover > ul {
  opacity: 1;
  visibility: visible;
  translate: 0 0;
}
Enter fullscreen mode Exit fullscreen mode

And here’s the final result:

Tabbed content

Combining <label> and <input type="radio"> can do magic! And all because when you click on a <label>, the browser will automatically pass the focus to its associated input. In case with the radio input, that means it will be checked.

The layout is simple: we create several pairs of input-label elements and the related number of divs for content.

<!-- Radio buttons -->
<input type="radio" id="tab1" name="tabs" checked>
<label for="tab1">Tab 1</label>

<input type="radio" id="tab2" name="tabs">
<label for="tab2">Tab 2</label>

<input type="radio" id="tab3" name="tabs">
<label for="tab3">Tab 3</label>

<!-- Content -->
<div class="tab-content" id="content1">
  <h2>Content for Tab 1</h2>
  <p>This is the content for the first tab.</p>
</div>

<div class="tab-content" id="content2">
  <h2>Content for Tab 2</h2>
  <p>This is the content for the second tab.</p>
</div>

<div class="tab-content" id="content3">
  <h2>Content for Tab 3</h2>
  <p>This is the content for the third tab.</p>
</div>
Enter fullscreen mode Exit fullscreen mode

Then the beauty of CSS:

/* Hide radio buttons */
input[type="radio"] {
  /*display: none;*/

  /* To make radio buttons accessbile by a keyboard, let's hide them visually only */
  border: 0;
  clip: rect(1px, 1px, 1px, 1px);
  clip-path: inset(50%);
  height: 1px;
  margin: -1px;
  overflow: hidden;
  padding: 0;
  position: absolute;
  width: 1px;
  word-wrap: normal !important;
}

/* Highlight the selected tab when using a keyboard */
input[type="radio"]:focus + label {
  outline: 2px solid;
}

/* Style labels as tabs */
label {
  display: inline-block;
  padding: 10px 20px;
  margin: 0;
  cursor: pointer;
  background-color: #eee;
  border: 1px solid #ccc;
  border-bottom: none;
  transition: background-color 0.3s ease;
}

/* Style active tab */
  input[type="radio"]:checked + label {
  background-color: #fff;
  border-bottom: 1px solid #fff;
  font-weight: bold;
}

/* Style content areas */
.tab-content {
  display: none;
  padding: 20px;
  border: 1px solid #ccc;
  border-top: none;
  background-color: #fff;
}

/* Show active content */
#tab1:checked ~ #content1,
#tab2:checked ~ #content2,
#tab3:checked ~ #content3 {
  display: block;
}
Enter fullscreen mode Exit fullscreen mode

CSS has a native :checked selector for the checked inputs (both radios and checkboxes).

And thanks to the ~ (tilde), which is officially called the subsequent-sibling combinator, we can toggle visibility of the content blocks. The only requirement is that content divs should be placed AFTER radio inputs.

Here’s the final result:

Snap block

Ever scrolled YouTube Shorts on a desktop? After each wheel scroll the next video snaps to the top of the page:

CSS scroll-snap in action. Even YouTube uses it!
CSS scroll-snap in action. Even YouTube uses it!

And if you think that it’s some complex JS code — you’re wrong! It’s a native CSS feature called scroll-snap.

So first, you need to set up a scroll-snap-type for the parent container. To make it work like in YouTube Shorts, we will set it to y mandatory.

.scroll-container {
  display: flex;
  flex-direction: column;
  overflow-y: scroll;
  scroll-snap-type: y mandatory;
  width: 300px;
  height: 80%;
  background-color: #ddd;
  padding: 20px;
  box-sizing: border-box;
  scroll-padding: 10px;
}
Enter fullscreen mode Exit fullscreen mode

That means the scroll container snaps in its vertical axis only and the content must snap to a snap position if it isn’t currently scrolled. Check MDN Docs for the detailed explanation of each possible value for this property.

And for each of the scrolled items we just need to provide a snap position of the item within its snap area. In other words, do we want the item to be snapped to the top (start), center or to the bottom (end). It’s possible thanks to the scroll-snap-align property:

.scroll-item {
  scroll-snap-align: start;
}
Enter fullscreen mode Exit fullscreen mode

Take a look at the demo, it’s really very cool feature and using CSS only:

Those were some tricks popped in my head, but they are just the tip of the iceberg. If you want the second part please click on the “like” button so I can see it is something you interested in 😊


If you find this article helpful — don’t hesitate to like, subscribe and leave your thoughts in the comments 😊


Read more posts on my Medium blog


Thanks for reading!

Stay safe and peace to you!

💖 💪 🙅 🚩
bogdanfromkyiv
Bogdan Bendziukov

Posted on September 20, 2024

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related