Two Tailwind-Inspired Techniques for Vanilla CSS

Two Tailwind-Inspired Techniques for Vanilla CSS

I recently worked on a project with one of my trainees—a product landing page for a bag company. For this project, I decided to challenge myself by using only vanilla HTML and CSS. Along the way, I discovered two game-changing techniques inspired by Tailwind CSS: utility components and immediate responsiveness.

In this post, I’ll show how these methods simplified my CSS workflow and enhanced my designs.

A quick note on Tailwind CSS:
Tailwind is a fantastic utility-first CSS framework. It makes building layouts and adding responsive styles effortless by providing ready-to-use utility classes. Inspired by this approach, I adapted a similar concept to vanilla CSS—a technique I call Utility Components.

Utility Components: Reusable CSS Classes

Tailwind CSS is great for quickly prototyping designs. For instance, if you need a button, you can write:

<button class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded">
  Click me
</button>

In React, you could turn this into a reusable button component and use it anywhere in your project. Updating the button’s design would only require a single change in the component.

However, with vanilla HTML and CSS, using Tailwind’s utility classes directly means you’d have to copy and paste this block everywhere you need the button. If you later need to tweak its design, you’d have to track down every instance and update them individually—a tedious and error-prone process.

This is where utility components come in. Instead of repeating the same group of classes, you define a reusable class in your CSS:

.btn {
  font-weight: bold;
  padding: 0.5rem 1rem;
  border-radius: 0.25rem;
}

.btn-primary {
  background-color: #3490dc;
  color: white;
}

.btn-primary:hover {
  background-color: #2779bd;
}

And use it in your HTML:

<button class="btn btn-primary">
  Click me
</button>

With this approach, your HTML stays clean, and updates are centralized. A single change to .btn or .btn-primary updates all buttons in your project instantly. This keeps your workflow efficient, maintainable, and scalable—qualities we often take for granted in frameworks but can easily replicate with vanilla CSS.

This is not even a very new technique. It’s just really nice to discover it again after spending so much time using Tailwind.

Now, let’s move on to Immediate Responsiveness.

Immediate Responsiveness: Efficient Mobile-First Design

When you first tackled responsive design, you likely built for desktop screens first—it’s what’s in front of you, after all. However once the desktop layout was done, figuring out how to adapt it for tablets and mobile devices often became a challenge, leading to messy CSS or repetitive code. Responsiveness has long been a pain point for developers.

Tailwind's mobile-first approach tackles this by encouraging you to address responsiveness as you build. Instead of finishing the entire layout and then retrofitting it for smaller screens, you design each component to work across all screen sizes from the start.

For example, here's a responsive header built with Tailwind:

<header class="bg-blue-600 text-white py-4 px-6">
  <div class="max-w-7xl mx-auto flex flex-wrap items-center justify-between">
    <!-- Logo -->
    <div class="flex items-center">...</div>

    <!-- Navigation -->
    <nav class="hidden md:flex space-x-6">...</nav>

    <!-- Mobile Menu Button -->
    <button class="md:hidden text-2xl">
      <i class="ri-menu-line"></i>
    </button>
  </div>
</header>

This header is mobile-first:

  • Content is centered and constrained using max-w-7xl mx-auto.

  • The navigation is hidden on small screens (hidden) and visible on medium and larger screens (md:flex).

  • The mobile menu button is the reverse: visible on small screens, hidden on medium and larger screens.

Now, let’s translate this into vanilla HTML and CSS using utility components to keep it clean and reusable:

<header class="header">
  <div class="header-container">
    <!-- Logo -->
    <div class="logo">...</div>

    <!-- Navigation -->
    <nav class="nav">...</nav>

    <!-- Mobile Menu Button -->
    <button class="mobile-menu-btn">
      <i class="ri-menu-line"></i>
    </button>
  </div>
</header>

And here’s how the CSS evolves step by step:

/* Step 1: Style the Header and Logo */
.header {
  background-color: #3182ce; /* Blue background */
  color: white;
  padding: 1rem 1.5rem;
}

.header-container {
  max-width: 112rem; /* 7xl equivalent */
  margin: 0 auto;
  display: flex;
  justify-content: space-between;
  align-items: center;
  flex-wrap: wrap;
}

.logo {
  display: flex;
  align-items: center;
}

At this point, we’ve styled the general structure of the header and ensured the logo is aligned properly. Next, we tackle the navigation.

/* Step 2: Add Styles for Navigation (Existing styles omitted for brevity) */
.nav {
  display: none;
}

/* Make the navigation visible on larger screens */
@media (min-width: 768px) {
  .nav {
    display: flex;
    gap: 1.5rem;
  }
}

The navigation is hidden by default for mobile users and becomes visible with proper spacing on screens 768px and above. Now we add styles for the mobile menu button.

/* Step 3: Style the Mobile Menu Button (Existing styles omitted for brevity) */
.mobile-menu-btn {
  font-size: 1.5rem;
  display: block;
}

/* Hide the mobile menu button on larger screens (Existing styles omitted for brevity) */
@media (min-width: 768px) {
  .mobile-menu-btn {
    display: none;
  }
}

With these incremental steps, the header evolves into a fully responsive component. By styling each part and immediately adding the necessary responsive adjustments, you avoid repetitive work later.

The key takeaway:
Always factor responsiveness into your CSS as you build, not after. This way, your designs are mobile-first, efficient, and ready for all screen sizes.

Putting It All Together

Frameworks and well-structured projects are some of the best ways to learn how to build effectively. They don’t just teach you what to do—they show you why certain approaches work. I remember struggling with JavaScript until I worked with React. Suddenly, everything clicked, and going back to practice with plain JavaScript made so much more sense.

The same idea applies to styling. Combining utility components with responsive design gives you a powerful way to create scalable and maintainable websites without depending on CSS frameworks. This approach keeps your HTML clean and readable, makes your styles reusable, and handles responsiveness from the start. It also speeds up development and simplifies maintenance because everything is modular and easy to adjust.

While frameworks like Tailwind are great, learning these techniques with plain CSS makes you a more flexible and confident developer. Try it in your next project, and if you do, connect with me on Twitter—I’d love to see what you’ve built!