DEV Community

Cover image for Astro recipe collection website - Part 4 Styling the website
Chris Bongers
Chris Bongers

Posted on • Originally published at daily-dev-tips.com

Astro recipe collection website - Part 4 Styling the website

Our recipe website built in Astro is finally taking shape. We have our recipe collection set up, created some filter pages, and today we'll look at styling and ordering the components.

What we'll build today:

  • Add Tailwind to our project
  • Navigation component
  • Header component
  • Recipe component
  • Pagination component

Adding Tailwind to an Astro project

This will be the short version. If you're interested, follow my full article on integrating Tailwind in Astro.

To recap what we need to do:

  • Install tailwind
npm install -D tailwindcss
Enter fullscreen mode Exit fullscreen mode
  • Create a tailwind.config.js file
module.exports = {
  mode: 'jit',
  purge: ['./public/**/*.html', './src/**/*.{astro,js,jsx,ts,tsx,vue}'],
};
Enter fullscreen mode Exit fullscreen mode
  • Modify astro.config.js file
devOptions: {
  tailwindConfig: './tailwind.config.js';
}
Enter fullscreen mode Exit fullscreen mode
  • Add the Tailwind styles to the global.css file
@tailwind base;
@tailwind components;
@tailwind utilities;
Enter fullscreen mode Exit fullscreen mode

And this allows us to use the latest Tailwind version on our Astro website.

Creating a navigation component in Astro

By now, we didn't have an excellent way to navigate the website, so let's go ahead and create a Nav.astro component in our components directory.

We'll make use of the Frontmatter (---) part of creating an array that we can use to create the menu.

---
const menu = [
  { url: '/', text: 'Home' },
  { url: '/recipes', text: 'Find recipes'},
  { url: '/recipes/all', text: 'All recipes'}
];
---
Enter fullscreen mode Exit fullscreen mode

We do this so we can loop over each menu item and only have one line of class code, which makes it easier to maintain later on.

Next, create the HTML part of our header.

<header class="border-b-2 border-gray-100">
  <nav class="flex justify-between items-center  max-w-7xl mx-auto px-4 py-8">
    <h1 class="text-pink-400 text-4xl uppercase tracking-narrow">
      <a href="/">Recipe blog</a>
    </h1>
    <ul class="flex">
      {menu.map(item => (
      <li>
        <a
          class="mx-4 block font-sans font-bold uppercase tracking-wider text-black no-underline "
          href="{item.url}"
          >{item.text}</a
        >
      </li>
      ))}
    </ul>
  </nav>
</header>
Enter fullscreen mode Exit fullscreen mode

This code will serve as our menu.
This menu will show a title on the left and the actual menu button on the right.

We use CSS Flex to align items correctly. We want them to space between the logo part and the navigation and use justify-between for that.

To use this component, we can use the following syntax.

---
import Nav from '../components/Nav.astro';
---
<!-- HTML Content -->
<Nav />
Enter fullscreen mode Exit fullscreen mode

Header component in Astro

Header component in Astro

The next component we want to create is a header component. The header is a recurring component that has two properties being a title and a text.

We create this component, so it's easier to share between pages.

Go ahead and create a Header.astro file inside your components directory.

---
export interface Props {
  title: string;
  text: string;
}
const { title, text } = Astro.props;
---
Enter fullscreen mode Exit fullscreen mode

As you can see, we define the properties that this component will have.

Now let's create the HTML part for the header.

<section class="bg-pink-400 p-8 text-center">
  <h1 class=" text-white text-5xl mb-4">{title}</h1>
  <p class="text-pink-100 text-xl">{text}</p>
</section>
Enter fullscreen mode Exit fullscreen mode

Now we can use this in our pages like so:

---
import Header from '../components/Header.astro';
---
<!-- HTML Content -->
<Header title="Our latest recipes" text="These are latest six recipes added to our blog." />
Enter fullscreen mode Exit fullscreen mode

And that will return our header like so:

Header component in Astro

Creating a reusable recipe component

As you may have seen before we loop our recipes on multiple pages. This caused duplicate code, so let's clean this up by moving the recipes into their own component.

Create a Recipe.astro file in the components directory.

As we saw in the navigation component, we can define the properties for the data set.

---
export interface Props {
  recipe: {
      url: string;
      title: string;
      image: string;
  };
}
const { recipe } = Astro.props;
---
Enter fullscreen mode Exit fullscreen mode

We can then use this data in our HTML below like this:

<article class="px-2 py-4 text-center">
  <a href="{recipe.url}" class="flex flex-col hover:opacity-60">
    <img class="object-cover h-[200px]" src="{recipe.image}" alt="{recipe.title}" />
    <h3 class="text-2xl leading-tighter pt-2">{recipe.title}</h3>
  </a>
</article>
Enter fullscreen mode Exit fullscreen mode

To use this recipe, we can modify our recipes loop, for instance, on the homepage:

---
import Recipe from '../components/Recipe.astro';
---
<!-- HTML Content -->
<section class="grid grid-cols-3 py-8">
    {recipes.map(recipe => (<Recipe recipe={recipe} />))}
</section>
Enter fullscreen mode Exit fullscreen mode

Way cleaner, right?

Recipe component in Astro

Astro pagination component

Another recurring element is the pagination component. We used this on three different pages now, so let's also make a component for that.

We can go ahead and create the Pagination.astro component in the components directory.

The pagination has quite the data set, but we only use the previous and next elements for our simple pagination.

---
export interface Props {
  recipe: {
      url: {
          prev: number,
          next: number
      };
  };
}
const { data } = Astro.props;
---
Enter fullscreen mode Exit fullscreen mode

Then let's render our HTML. I'm using JSX conditional rendering for this particular element to see if we have more than just one page of items.

{
  data.last !== 1 && (
    <footer class="text-center py-8">
      <h2 class="text-xl text-bold uppercase mb-4">More recipes</h2>
      <ul class="flex justify-center">
        <li>
          <a
            class="flex mx-2 justify-center items-center h-12 w-12 rounded bg-pink-200"
            href={data.url.prev || '#'}
          >
            &#60;
          </a>
        </li>
        <li>
          <a
            class="flex mx-2 justify-center items-center h-12 w-12 rounded bg-pink-200"
            href={data.url.next || '#'}
          >
            &#62;
          </a>
        </li>
      </ul>
    </footer>
  );
}
Enter fullscreen mode Exit fullscreen mode

The footer will only render if there is more than one page. If so, it will show the previous and next buttons.

To use this component, we can use the following syntax:

---
import Pagination from '../../components/Pagination.astro';
---
<!-- HTML Content -->
<Pagination data={page} />
Enter fullscreen mode Exit fullscreen mode

And that gives us pagination like this:

Astro pagination component

Conclusion

To wrap this article up, I've replaced all the code examples we build so far into these components and add some small styling elements.

I'm not a superb designer, but this should show you how to style your website with ease!

Astro recipe collection website - Part 4 Styling the website

I've also uploaded the complete code to GitHub.

And you can view the result on the actual Recipe demo website.

Thank you for reading, and let's connect!

Thank you for reading my blog. Feel free to subscribe to my email newsletter and connect on Facebook or Twitter

Top comments (0)