Overview
One of my favorite components is undoubtedly cards and the reasons are very simple, they can have many uses.
In the same way that it can be used for informative content (such as brief descriptions), it can also be used to focus the user's attention momentarily (such as a post on social media) or so that the user can take an action.
When designing a card we need to pay attention to some aspects:
- All content must be contained in a single element;
- You don't need to have other auxiliary elements to give some context to the card's content;
From these small points we only need to pay attention to a few aspects in terms of the element's anatomy.
Obviously, many of the elements end up being optional and for that same reason today I'm only going to focus on just these:
- Container - contains all the elements of the card and generally these same elements dictate the size of the card however, I like to do the opposite which is to define the maximum space they can occupy;
- Thumbnail - it's an important visual element, however this article will be for stylistic reasons;
- Buttons - usually the buttons are grouped in a single space and each button corresponds to an action that the user can take;
And one of the aspects that I find interesting is that it's a component that generally has great behaviors in terms of responsiveness.
Anyway, these are just a few aspects that I take into account when working with interface elements, so I recommend reading this Material Design page which explains in detail what kind of behavior this component should have.
Today's example
In today's example we will make a simple card, but in my opinion it has a very different design when compared to many libraries and frameworks. A behavior I recommend adopting is to spend a few minutes daily to see designs that you find interesting and I tried to copy them. I hope that at the end of today's article you have a similar result:
Let's code
The framework we are going to use today is Tailwind CSS and along with this framework we are going to use other tools such as classnames and react-icons.
npm install classnames react-icons
After that we will create a file that contains the contents of the cards.
// @src/data/posts.js
export default [
{
title: "Rogue's Rise",
likes: Math.floor(Math.random() * (50 - 0) + 0),
image: "https://bit.ly/3BQdTqk",
},
{
title: "Fool's End",
likes: Math.floor(Math.random() * (50 - 0) + 0),
image: "https://bit.ly/3CQFPvv",
},
{
title: "A Greater Power",
likes: Math.floor(Math.random() * (50 - 0) + 0),
image: "https://bit.ly/3ERuyMd",
},
{
title: "2099: Oasis",
likes: Math.floor(Math.random() * (50 - 0) + 0),
image: "https://bit.ly/3CQKSwb",
},
];
Now we can start working on our card but first let's create the styles for our component:
/* @src/components/Card.module.css */
.wrapper {
@apply bg-white hover:bg-gray-800 shadow-xl hover:shadow-none cursor-pointer w-80 rounded-3xl flex flex-col items-center justify-center;
}
.wrapperAnime {
@apply transition-all duration-500 ease-in-out;
}
.header {
@apply relative mt-2 mx-2;
}
.imageWrapper {
@apply h-56 rounded-2xl overflow-hidden;
}
.image {
@apply object-cover w-full h-full;
}
.textWrapper {
@apply pt-10 pb-6 w-full px-4;
}
.text {
@apply font-medium leading-none text-base tracking-wider text-gray-400;
}
.badgeWrapper {
@apply absolute bottom-0 left-0 -mb-4 ml-3 flex flex-row;
}
.dangerBadge {
@apply h-10 w-10 flex items-center justify-center text-xl bg-white hover:bg-red-500 text-red-500 hover:text-white rounded-2xl shadow-xl;
}
.primaryBadge {
@apply h-10 w-16 ml-2 bg-white hover:bg-blue-600 flex items-center justify-center font-medium text-blue-600 hover:text-white rounded-2xl shadow-xl;
}
.counter {
@apply text-gray-800 ml-2;
}
.badgeAnime {
@apply transform-gpu translate-y-0 hover:-translate-y-1 transition-all duration-300 ease-in-out;
}
Now we can start working on our component's jsx. Our component will receive four props, which will be the title, the number of likes, the order of the array element and the image.
After that we can import our icons and we can start applying the styles of our component:
// @src/components/Card.jsx
import React from "react";
import classNames from "classnames";
import { AiFillHeart } from "react-icons/ai";
import { BsChatSquareFill } from "react-icons/bs";
import styles from "./Card.module.css";
const Card = ({ title, likes, order, image }) => {
return (
<div className={classNames([styles.wrapper, styles.wrapperAnime])}>
<div className={styles.header}>
<div className={styles.imageWrapper}>
<img src={image} className={styles.image} alt="" />
</div>
<div className={styles.badgeWrapper}>
<div
className={classNames([styles.dangerBadge, styles.badgeAnime])}
>
<AiFillHeart />
</div>
<div
className={classNames([
styles.primaryBadge,
styles.badgeAnime,
"group",
])}
>
<BsChatSquareFill />
<span
className={classNames([styles.counter, "group-hover:text-white"])}
>
{likes}
</span>
</div>
</div>
</div>
<div className={styles.textWrapper}>
<h1 className={styles.text}>{`${order}. ${title}`}</h1>
</div>
</div>
);
};
export default Card;
Last but not least we have to go to our entry file (which in this case is App.jsx) and we will have the following styles:
/* @src/App.module.css */
.section {
@apply bg-gray-100 h-full md:h-screen w-full;
}
.container {
@apply container mx-auto px-0 md:px-4 py-4;
}
.layout {
@apply grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 2xl:grid-cols-4 justify-items-center gap-4;
}
Now in our App.jsx we're going to import our data from the posts and our Card component that we created, then we're going to loop and pass in the necessary props.
// @src/App.jsx
import React from "react";
import styles from "./App.module.css";
import Card from "./components/Card";
import posts from "./data/posts";
const App = () => {
return (
<main className={styles.section}>
<section className={styles.container}>
<div className={styles.layout}>
{posts.map((element, index) => (
<Card
key={index}
title={element.title}
likes={element.likes}
order={index + 1}
image={element.image}
/>
))}
</div>
</section>
</main>
);
};
export default App;
Conclusion
As always, I hope you found it interesting. If you noticed any errors in this article, please mention them in the comments. 🧑🏻💻
Hope you have a great day! ✌️
Top comments (31)
Is there any reason why you moved the styles to separate CSS file with @apply instead of writing them directly in JSX?
It was simply because this way it's easier to read the code so I can do the coditional rendering properly and so I can make changes faster. It's just the approach I like to take when I work on styling components, in my view it's simpler and more intuitive to separate the contexts. 😊
I like this approach as well. So, when we use JIT mode, it still purges unused styles, and we have the same output as we were directly using utility classes in JSX right?
The answer is No, By default, Tailwind only removes unused classes that it generates itself, or has been explicitly wrapped in a
@layer
directive. It will not remove unused styles from third-party CSS.It's correct 💪
I highly recommend not placing any styles inside JSX, instead, write a styles constant in the same file, and outside the components return function.
That way react performs way better, since inline styles are reprocessed with every state change.
Outside-return and imported styles do not suffer from this performance impact.
sorry, what I meant is not inline styles, but Tailwind classes.
because you can use one
@apply
style in multiple time in project :)Thanks for the nice article. But it didn't worked on me. Cards are so big seems styles wasn't applied even though I followed the instructions.
Hey! I got it! just follow the official starting guide. tailwindcss.com/docs/guides/create...
Thanks 😊 Did you configure Tailwind?
I just installed tailwind using:
npm install -D tailwindcss@npm:@tailwindcss/postcss7-compat postcss@^7 autoprefixer@^9
screenshots:
imgur.com/a/gQ9EjBd
From the image you shared, I'm not sure tailwind is properly configured. I say this because the styles are not being applied. If you try to edit something, does it have any effect?
Same for me. Did you find what is going wrong?
Designing modern cards with React and Tailwind CSS is a seamless process, marrying the efficiency of React components with the utility-first approach of Tailwind. Start by structuring a React component for the card, encapsulating content and functionalities. Integrate Tailwind's extensive set of utility classes to style the card, ensuring a sleek and responsive design. Leverage dynamic data binding in React to handle diverse card content effortlessly. Tailwind's flexibility allows for easy customization, enabling the creation of modern, visually appealing cards that align with the latest design trends while benefiting from React's modular and scalable architecture.
For more details, check out: purecode.ai/blogs/tailwind-cards/
Very beautiful UI design
I'm glad you liked it 😊
Yep
Nice
Thanks 🤗
Great designs btw :)
Thanks! But I actually put together two or three designs that I saw on dribbble to get this result. 😄
quick question, will Tailwind be conflicting with bootstrap for react library? I remember in 2018 I had an issue because I installed material UI and bootstrap to use for different elements. I am now picking up coding since that time and I feel like a beginner in many ways. The goal is to deploy this project to Heroku. Thanks!
Is there a way we could write the styles in html and generate the css file seperately and clean the html from class names?
If I understand your question correctly, I think my answer would be to use css-in-js, if you want to use it with Tailwind there is a library called twin.
Very Good Work and this is best information marry me spell
Nice one! THX!
Some comments have been hidden by the post's author - find out more