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
- Create a
tailwind.config.js
file
module.exports = {
mode: 'jit',
purge: ['./public/**/*.html', './src/**/*.{astro,js,jsx,ts,tsx,vue}'],
};
- Modify
astro.config.js
file
devOptions: {
tailwindConfig: './tailwind.config.js';
}
- Add the Tailwind styles to the
global.css
file
@tailwind base;
@tailwind components;
@tailwind utilities;
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'}
];
---
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>
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 />
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;
---
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>
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." />
And that will return our header like so:
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;
---
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>
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>
Way cleaner, right?
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;
---
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 || '#'}
>
<
</a>
</li>
<li>
<a
class="flex mx-2 justify-center items-center h-12 w-12 rounded bg-pink-200"
href={data.url.next || '#'}
>
>
</a>
</li>
</ul>
</footer>
);
}
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} />
And that gives us pagination like this:
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!
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)