DEV Community

Simon Conrad
Simon Conrad

Posted on

Mobile first design with Tailwind CSS

Building UI was one of the first things to click for me in my web development journey. It’s a tricky, nit-picky business that requires a keen eye for detail. Early on, small UI bugs really bothered me. I spent hours troubleshooting inconsistencies, which, in turn, deepened my understanding of CSS.

Call me a glutton for punishment, but I enjoy chasing down a good UI bug. The moment where everything clicks into place is incredibly satisfying.

I first encountered Tailwind CSS while building my first production app for a client this summer. It’s a utility-first CSS framework that allows the developer to write styles directly into HTML tags as a class. This keeps styles and html structure contained in one place and allows for fast and intuitive development of responsive design.

Tailwind CSS is loaded with features. I was particularly impressed by the ease of styling for various screen sizes by configuring breakpoints and using them to render specific styles.

This tutorial will show my approach to styling a responsive layout for multiple screen sizes and walk through a simple example of creating a blog.

Using this tutorial

I present these concepts with code blocks, images and screen recordings. For your reference, the finished code can be found in my tailwind demo repository (main branch).

If you choose to, you may code alongside. Fork and clone the repository according to README instructions and checkout to the starter branch by running git checkout starter

Instructions for forking and cloning the repository can be found in the README.

This tutorial assumes familiarity with:

  • Basic CSS and HTML concepts
  • React
  • Git
  • Tailwind CSS - if you are completely new to Tailwind’s functionality, I suggest spending a little bit of time with their documentation.

When you are ready to build from scratch, configure your project according to the Installation section of the docs.

Summary of starter code and setup

The focus of this tutorial is using Tailwind CSS to create a layout that works on multiple screen sizes and devices, maintaining the same style and feel across all devices.

I’m beginning with a mobile display already styled to demonstrate mobile first design. This is a React app, a mobile-first blog layout with a simple structure.

To follow along:

  • first clone the code down according to instructions in the README
  • Run npm run dev to start the development server
  • Open a tab in your browser (this tutorial will use Chrome) and navigate to localhost:5173
  • Enter dev tools with Command + Option + I (Mac) or Ctrl + Shift + I (Windows)and click the ‘toggle device toolbar’ icon. It’s in the top left corner.

https://res.cloudinary.com/ddgt67wcb/image/upload/v1739475690/blog/tailwind-css/Screenshot_2025-02-13_at_2.40.38_PM_wzxg7z.png

  • Select a mobile device eg. iPhone SE.

Welcome to… T*he Bacterium Blog - exploring the world of tiny titans.* Your source for fun facts in microbiology. It should look like this.

https://res.cloudinary.com/ddgt67wcb/image/upload/v1739476040/blog/tailwind-css/Screenshot_2025-02-13_at_2.46.41_PM_rbue8t.png

The page consists of 3 main components written in src/App.tsx and a 4th ‘Blog Post’ component found in the components directory.

  • Nav - a fixed navigation bar with a hamburger menu icon
  • Header - a banner section featuring a circular blog logo (the beautiful pink and purple bacterium)
  • BlogFeed - the container for blog posts which are written as a component in src/components/BlogPost.tsx and given content in src/components/BlogPosts.tsx

Before we proceed any further, let me illustrate the problem I am trying to solve here. As is, my UI fits nicely on a small screen, but as the window grows to fill larger and larger screens, it starts to look clunky and stupid. That’s bad. I don’t want that.

https://res.cloudinary.com/ddgt67wcb/image/upload/v1739371604/blog/tailwind-css/Screenshot_2025-02-11_at_8.30.04_AM_jogaix.png

With my simple, one column mobile display as a starting point, I can proceed to creating a dynamic layout. My priorities are to

  1. Make the banner fill the space on larger screens by applying styles with Tailwind’s breakpoints
  2. Use CSS grid to make the blog feed a readable column of information

Mobile first design and breakpoints

The core concept of my approach to responsive design is Tailwind’s breakpoints. By default, un-prefixed styles will render on all screen sizes while styles prefixed with sm:, md: , lg: etc. will only take effect on screens larger than the specified breakpoint.

For this reason, in developing the UI with Tailwind, it’s best to begin by developing the mobile styles. As the design expands to larger screens, we can progressively add enhancements and layout adjustments where needed.

These are the default Tailwind breakpoints that can be customized if desired. I generally choose to work with the defaults.

// tailwind.config.js
module.exports = {
  theme: {
    screens: {
      'sm': '640px',
      // => @media (min-width: 640px) { ... }

      'md': '768px',
      // => @media (min-width: 768px) { ... }

      'lg': '1024px',
      // => @media (min-width: 1024px) { ... }

      'xl': '1280px',
      // => @media (min-width: 1280px) { ... }
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Using the Tailwind defaults, a screen larger than 640px wide will render the styles prefixed with sm:. It’s important to note that those styles will apply to any screen greater than that size unless another breakpoint is used.

For example, if I give the banner a background color of ‘blue’ by default, and change it to red at the small breakpoint with sm:bg-red-400 it will render as red for screens of 640px and greater.

const Header = () => {
  return (
    <div className="bg-blue-300 text-lg grid pt-14 pb-5 sm:bg-red-400">
            {/* Header html here */}
    </div>
  )
}
Enter fullscreen mode Exit fullscreen mode

Let’s say I give a green background at the xl breakpoint. Now, on mobile, the banner will be blue, on screens between 640px and 1280px, it will be red, and screens 1280px and above will render green.

So the code now looks like this:

const Header = () => {
  return (
    <div className="bg-blue-300 text-lg grid pt-14 pb-5 sm:bg-red-400 xl:bg-green-400">
            {/* Header html here */}
    </div>
  )
}
Enter fullscreen mode Exit fullscreen mode

And the resulting styles appear like this:

This step was to demonstrate the behavior of breakpoints, but it’s also a valuable trick for designing and debugging. I use background colors like this all the time to visualize how breakpoints are behaving.

With this understanding of breakpoints, I’ll make adjustments to the Header component so it fills the space nicely on larger screens.

Styling the Header component

Currently, my header is a stack of 3 elements

  • a div containing my ‘bacterium graphic’
  • an h1 containing the blog title
  • an h5 containing my blog subtitle

To fill the space better, I’m going to add some CSS grid styles to the sm: breakpoint to adjust how the Header is laid out.

Instead of a stack, I’ll create a 2 x 3 grid (2 columns, 3 rows) where my graphic occupies the entire first column, spanning 3 rows and my title and subtitle occupy rows 2 and 3 of the second column respectively.

To do that, I first add sm:row-span-3 sm:col-start-1 to the className of the div containing my graphic. I add sm:row-start-2 sm:col-start-2 to my h1 tag holding my title and sm:row-start-3 sm:col-start-2.

On a small screen, I now have a banner that looks like this (with dev-tools grid-lines for reference):

https://res.cloudinary.com/ddgt67wcb/image/upload/v1739371603/blog/tailwind-css/Screenshot_2025-02-11_at_10.43.37_AM_w89drf.png

That’s more or less what I’m going for, but I want it a little more snug. Using ‘justify’ I’ll bring all my items to hug the column line 2 for a snug layout. My graphic gets sm:justify-end and my ‘h’ tags get sm:justify-start. I’ll give my graphic a little boost in size while I’m at it.

Using tailwinds built in text-sizing, I can make it pretty big with text-9xl but that’s the limit. I want to be even a little bigger, so I’ll pass in a custom size, like this text-[10rem] . I’ll give my headers a little boost in size too so my header fills its container nicely.

One last thing, I’ll add some extra padding to the top and bottom of the parent container for the header like this py-20.

My small screen page now looks like this:

https://res.cloudinary.com/ddgt67wcb/image/upload/v1739371604/blog/tailwind-css/Screenshot_2025-02-11_at_11.09.36_AM_ndfvso.png

I’m feeling better already about publishing my favorite microbe facts to the internet. Here is the current state of my code. Now would be a good time to commit and push my changes.

const Header = () => {
  return (
    <div className="bg-gray-300 text-lg grid pt-14 pb-5 sm:py-20">
      <div className="flex justify-center sm:justify-end m-4 sm:row-span-3 sm:col-start-1">
        <div className="border rounded-full bg-pink-200">
          <FaBacterium className="text-8xl sm:text-[10rem] text-gray-700 translate-x-2 translate-y-[-3px]" />
        </div>
      </div>
      <h1 className="flex justify-center sm:justify-start font-cursive text-5xl sm:text-6xl sm:row-start-2 sm:col-start-2">
        The Bacterium Blog
      </h1>
      <h5 className="flex justify-center sm:justify-start italic text-md sm:text-lg sm:row-start-3 sm:col-start-2">
        Exploring the World of Tiny Titans
      </h5>
    </div>
  )
}
Enter fullscreen mode Exit fullscreen mode

Something interesting to note here is that I never had to define how many grid columns and rows I wanted in my parent container. They were auto generated when I specified the row and column I wanted to place my items.

Full screen styles

Now I need to think about how I want this to look on a full screen desktop. I want to create a centered column to contain the BlogFeed column so it’s more readable. I’ll apply the same breakpoints and css grid concepts as before.

Creating the BlogFeed column

I want to create a grid with 4 columns for my large screen and position a container for my blog posts inside of that. I start by making the container a grid by adding grid .

I want small screens to be a column that fills the whole page, so I’ll define columns with the lg: breakpoint. I want space to the left and right so I’ll create 4 columns with lg:grid-cols-4 and place my container with lg:col-start-2 lg:col-span-2 .

This looks quite a bit better on the full screen window on my 13in macbook now.

https://res.cloudinary.com/ddgt67wcb/image/upload/v1739371606/blog/tailwind-css/Screenshot_2025-02-11_at_12.30.22_PM_whdvj9.png

My blog posts are a nice, readable column down the middle of the page and I’ve created space to the left and right for a table of contents, calls to action, announcements, whatever needs I may have for my Bacterium Blog as I scale it.

Here’s what the code looks like:

const BlogFeed = () => {
  return (
    <div className="bg-gray-50 font-serif text-lg p-2 pt-4 grid lg:grid-cols-4">
      <div className="lg:col-start-2 lg:col-span-2">
        <Post1 />
        <Post2 />
        <Post3 />
      </div>
    </div>
  )
}
Enter fullscreen mode Exit fullscreen mode

Final touches

This is much better, but I think a little cramped on the midsize screens. To finish things off, I’m going to create a slightly better progression from medium to full screen.

For medium screens, there are no columns and there don’t need to be. I’m just going to add some padding to give it a similar effect. I add md:px-10 to give padding on the x-axis (padding-left and padding-right).

Keeping that same padding, I’ll make my large screen text column just a little wider so it’s not so cramped. I’ll try this out: lg:col-start-1 lg:col-span-3 .

I liked my previous styles for the full screen view, so I’ll keep them and switch the breakpoint to xl: like this xl:col-start-2 xl:col-span-2.

const BlogFeed = () => {
  return (
    <div className="bg-gray-50 font-serif text-lg p-2 pt-4 grid lg:grid-cols-4">
      <div className="md:px-10 lg:col-start-1 lg:col-span-3 xl:col-start-2 xl:col-span-2">
        <Post1 />
        <Post2 />
        <Post3 />
      </div>
    </div>
  )
}
Enter fullscreen mode Exit fullscreen mode

The last thing I’ll do is make my Header grow bigger as the window size increases. I’ll add the following:

  • lg:text-[13rem] xl:text-[14rem] to my bacterium graphic container
  • lg:text-7xl xl:text-9xl to my h1 tag
  • lg:text-xl xl:text-2xl to my h5 subtitle and
  • lg:py-24 xl:py-28 to my grid container to give it some extra padding
const Header = () => {
  return (
    <div className="bg-gray-300 text-lg grid pt-14 pb-5 sm:py-20 lg:py-24 xl:py-28">
      <div className="flex justify-center sm:justify-end m-4 sm:row-span-3 sm:col-start-1">
        <div className="border rounded-full bg-pink-200">
          <FaBacterium className="text-8xl sm:text-[10rem] lg:text-[13rem] xl:text-[14rem] text-gray-700 translate-x-2 translate-y-[-3px]" />
        </div>
      </div>
      <h1 className="flex justify-center sm:justify-start font-cursive text-5xl sm:text-6xl lg:text-7xl xl:text-9xl sm:row-start-2 sm:col-start-2">
        The Bacterium Blog
      </h1>
      <h5 className="flex justify-center sm:justify-start italic text-md sm:text-lg sm:row-start-3 sm:col-start-2 lg:text-xl xl:text-2xl ">
        Exploring the World of Tiny Titans
      </h5>
    </div>
  )
}
Enter fullscreen mode Exit fullscreen mode

So, my new and improved Bacterium Blog responsive UI now looks like this:

Conclusion & next steps

I hope this was an informative tour of Tailwind’s breakpoints! You’ve now seen the foundation of my UI development strategy. This was a simple, quirky example, but the principles of responsive design apply to the most complex layouts. Breaking a design into components and placing them on a grid clarifies the vision and brings everything into alignment. This process can be done in neat iterations with Tailwind’s breakpoints and mobile-first approach.

I have found Tailwind to be a powerful tool for developing responsive layouts. It was a game changer for me and I highly recommend putting in the time to learn it.

The best way to grow as a designer is to stay inspired, imagine interesting components and bring them to life.

Happy coding friends - as always, have fun building and be easy on yourself and others

Some final notes:

Tailwind classes can be used in tandem with a css stylesheet if, for example, I wanted to use the breakpoints for defining grid placement, but not font colors and formatting.


Other useful features to explore:

  • dark mode implementation
  • hidden to conditionally render elements depending on screen size

Their documentation is excellent. Here it is one more time: tailwind docs

Image of Datadog

Master Mobile Monitoring for iOS Apps

Monitor your app’s health with real-time insights into crash-free rates, start times, and more. Optimize performance and prevent user churn by addressing critical issues like app hangs, and ANRs. Learn how to keep your iOS app running smoothly across all devices by downloading this eBook.

Get The eBook

Top comments (0)

A Workflow Copilot. Tailored to You.

Pieces.app image

Our desktop app, with its intelligent copilot, streamlines coding by generating snippets, extracting code from screenshots, and accelerating problem-solving.

Read the docs