Skip to main content

Welcome to a Grasp lesson

Create a free account to keep studying and create your own study path.

Lesson 4

Styling with Tailwind CSS Utilities

Recommended resources

Welcome back! In our last lesson, you built your first Svelte component, the ProfileCard, and styled it using traditional, scoped CSS. This gave you a solid understanding of a .svelte file's structure. Now, it's time to unlock the speed and power of the TailwindCSS setup we configured earlier.

Today, we will refactor that same ProfileCard component, replacing every line of custom CSS with Tailwind's utility classes. This lesson is all about hands-on application. You'll learn how to use Tailwind's utility-first approach to handle layout, typography, and color directly in your markup. This method might feel different from writing CSS in <style> blocks or using CSS-in-JS libraries you may have encountered in React, but you'll quickly see how it accelerates development and enforces a consistent design language.

By the end of this lesson, you will have transformed the ProfileCard into a component styled purely with Tailwind, and you'll be equipped to apply these fundamental utility classes across any component you build.

The Utility-First Mindset

Before we write any code, it's important to grasp the core idea behind Tailwind CSS. It's called a "utility-first" framework. Instead of giving you pre-built components like buttons or cards, it provides low-level utility classes that let you build completely custom designs without leaving your HTML.

For instance, instead of creating a CSS class named .card and defining its padding, shadow, and border-radius, you would compose those styles directly on the element like this: class="p-4 shadow-md rounded-lg".

This article from DEV Community provides a concise comparison that illustrates this shift in approach perfectly.

Tailwind CSS + Svelte: Utility-First Styling at Scale - DEV Community

This reading gives a great side-by-side comparison of styling with manual CSS versus Tailwind utility classes, which is exactly what we're about to do.

Focus on the section that begins with this sentence and compare the "Before (manual CSS)" and "After (Tailwind)" code blocks. This is the central concept for today's lesson.

Now, let's apply this concept to our ProfileCard.


Refactoring the ProfileCard with Tailwind

Open your src/lib/components/ProfileCard.svelte file. We're going to systematically replace the rules in your <style> block with Tailwind classes on the corresponding HTML elements. After we're done, we will delete the <style> block entirely.

Step 1: Styling the Main Container

Let's start with the main <div> wrapper. Your current <style> block has this rule:

div {
  border: 1px solid #e2e8f0;
  border-radius: 8px;
  padding: 1.5rem;
  max-width: 320px;
  text-align: center;
  box-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
}

We can map each of these properties to a Tailwind utility class. Update your <div> to look like this:

<div class="max-w-xs rounded-lg border border-slate-200 p-6 text-center shadow-md">
    ...
</div>

Let's break that down:

  • max-w-xs: Sets max-width to 20rem (320px).
  • rounded-lg: Applies a large border-radius (8px).
  • border: Sets a 1px solid border.
  • border-slate-200: Sets the border color. The Tailwind color palette is extensive; slate-200 is a close match to #e2e8f0.
  • p-6: Applies padding. In Tailwind's default spacing scale, 6 corresponds to 1.5rem.
  • text-center: Applies text-align: center.
  • shadow-md: Applies a medium box-shadow.

Step 2: Styling the Avatar Image

Next, the <img> element. The CSS is:

img {
  border-radius: 50%;
  width: 100px;
  height: 100px;
  margin-bottom: 1rem;
}

Here's the Tailwind equivalent. Note that since an <img> is an inline-block element by default, text-align: center on the parent won't center it. We need to add mx-auto (margin x-axis auto) to center it horizontally.

<img src={avatarUrl} alt="User avatar" class="mx-auto mb-4 h-24 w-24 rounded-full" />
  • rounded-full: Applies border-radius: 9999px to make the image a perfect circle.
  • h-24 and w-24: Sets the height and width to 6rem (96px), which is very close to our original 100px.
  • mb-4: Adds a margin-bottom of 1rem.
  • mx-auto: Centers the element horizontally within its parent.

Step 3: Styling the Typography

Finally, let's address the <h2> heading.

h2 {
  font-size: 1.25rem;
  font-weight: 600;
  margin-bottom: 0.5rem;
}

We can replace this with typography, color, and spacing utilities.

<h2 class="mb-2 text-xl font-semibold text-slate-800">{name}</h2>
  • text-xl: Sets the font-size to 1.25rem.
  • font-semibold: Sets the font-weight to 600.
  • mb-2: Adds margin-bottom of 0.5rem.
  • text-slate-800: Gives the text a dark, but not pure black, color for better aesthetics.

Let's also give the paragraph a slightly lighter text color to create some visual hierarchy:

<p class="text-slate-600">{bio}</p>

The Final Result

You can now delete the entire <style> block from your component. Your ProfileCard.svelte should look like this, clean and self-contained:

<script>
  let name = 'Alex Doe';
  let bio = 'Svelte enthusiast and future app developer. Enjoys clean code and fast websites.';
  let avatarUrl = 'https://i.pravatar.cc/150?u=alexdoe';
</script>

<div class="max-w-xs rounded-lg border border-slate-200 p-6 text-center shadow-md">
  <img src={avatarUrl} alt="User avatar" class="mx-auto mb-4 h-24 w-24 rounded-full" />
  <h2 class="mb-2 text-xl font-semibold text-slate-800">{name}</h2>
  <p class="text-slate-600">{bio}</p>
</div>

Run npm run dev and take a look. The card should look almost identical to before, but now its styling is handled entirely by a consistent, reusable set of utility classes.


Understanding the System

You've just seen how to use utility classes. It's also helpful to understand the consistent system that underpins them. Tailwind isn't just a random collection of CSS properties; it's a design system in a box.

Color Scales: Colors are organized into scales with numeric suffixes, from 50 (lightest) to 900 (darkest). This provides a predictable way to choose shades.

This image shows an example of a primary color (Indigo) broken down into a scale, from a very light shade (50) to a deep, dark one (500). This is how all of Tailwind's default colors are structured.

Typography & Spacing: Similarly, properties for font sizes, spacing, margins, and padding all follow a proportional scale. This is why p-4 is twice as big as p-2, ensuring visual consistency.

This image displays various typography options available in Tailwind, from different sizes to styles like bold, italic, and underline, all achievable through simple utility classes.

To see a fantastic explanation of how these utility classes work behind the scenes and how they relate to core CSS properties, the following video is an excellent resource.

Tailwind CSS v4 Full Course 2025 | Master Tailwind in One Hour

This video from JavaScript Mastery provides a clear, conceptual overview of Tailwind's core ideas, covering many of the classes you just used in the refactoring exercise.

Watch the segment explaining the core idea of utility classes. Then, watch the section on spacing and borders, which directly relates to the padding, margin, border, and rounded classes we just applied.


Conclusion

In this lesson, you've taken a huge step into modern front-end styling. By refactoring the ProfileCard, you've experienced the utility-first workflow firsthand and learned how to style components for layout, color, and typography using Tailwind CSS.

Key Takeaways:

  • Utility-First CSS: You style elements by composing small, single-purpose classes directly in your markup.
  • Speed and Consistency: This approach is fast for prototyping and ensures visual consistency by using a predefined design system for spacing, colors, and more.
  • Co-location: Styles live right next to the markup they apply to, making components easier to understand and maintain.
  • No <style> Block Needed: For a large portion of your styling needs, you won't need to write any custom CSS at all.

Our ProfileCard is looking great, but its content is hard-coded. In our next lesson, we'll make this component truly reusable by learning how to pass data into it from a parent component using props.