In this article, we use emotion css to design card templates in Nextjs that can be used as business cards. We demonstrate the manipulation of coloring css grids to come up with a beautiful structure flexible to any developer to use for a business venture e.g as a business card template. Based on preference, the colors can be changed and the information grid layout merged in any possible manner. Let’s begin!
The completed project is available on Codesandbox.
You can find the full codebase on my Github
Entry-level knowledge in javascript and React/Nextjs and css.
Use npx create-next-app card-design
to create a Next.js project and head to the directory using `cd card design
We begin with the backend setup. The backend involves integrating Cloudinary for media upload. We’ll use our backend code to upload generated card templates.
Use this link to access the cloudinary login page. If you don’t have an account you can create one and log into it to access your dashboard. The dashboard should contain environment variables necessary for cloudinary integration. To use these variables, go to your project root folder and create a new file names .env
.
Paste the following code and fill the blanks with your environment variables from the cloudinary dashboard.
".env"
CLOUDINARY_CLOUD_NAME =
CLOUDINARY_API_KEY =
CLOUDINARY_API_SECRET=
Use the npm run dev
command to start your project.
Head to the pages/api
directory and create a file named upload.js
. Start by pasting the following inside
"pages/upload"
var cloudinary = require("cloudinary").v2;
cloudinary.config({
cloud_name: process.env.CLOUDINARY_NAME,
api_key: process.env.CLOUDINARY_API_KEY,
api_secret: process.env.CLOUDINARY_API_SECRET,
});
The above code configures our cloudinary environment keys and libraries. We can finally introduce a handler function to execute our POST request.
"pages/upload"
export default async function handler(req, res) {
if (req.method === "POST") {
let url = ""
try {
let fileStr = req.body.data;
const uploadedResponse = await cloudinary.uploader.upload_large(
fileStr,
{
resource_type: "video",
chunk_size: 6000000,
}
);
url = uploadedResponse.url
} catch (error) {
res.status(500).json({ error: "Something wrong" });
}
res.status(200).json({data: url});
}
}
Our handler function will receive media data from the frontend, upload it and return the file’s cloudinary link as a response.
Our backend is complete. Let’s now design our card.
We will design our cards using emotion css. Therefore we begin by installing the necessary modules.
npm install @emotion/react @emotion.styled
We wil be creating the necessary components and importing them where necessary.
Using css, we will create a card component and use grids to divide the component. We can then fill specific grids with colors respective to how colorful our card should look. Our project will of course include several extra components like Title
and Forms
. The article will however focus on creating the card component.
In the pages/index
directory, replace the contents with the following
"pages/index"
export default function Home(){
function(
<>
<Card1 />
</>
)
}
To create the card component in the return function we will use forwardref which is used to automatically pass a ref
through a component to one of its children.
start by importing forwarded
and create
and declaring our ref
variable.
Inside the Home function, create your card component and reference it in the DOM element as shown
"pages/index"
import { forwardRef, createRef} from 'react';
export default function Home(){
const Card1 = forwardRef((props, ref) => (
<Card_1 ref={refOne} >
</Card_1>
));
function(
<>
<Card1 ref={refOne} />
</>
)
}
In your styles
directory, create a directory named emotion/card1.js
, and inside we start by creating a card component. Ensure to import the emotion module
before creating your component.
"styles/emotion/card1"
import styled from "@emotion/styled";
export const Card_1 = styled.div`
height: 220px;
width: 390px;
display: grid;
grid-template-columns: 2% 96% 2%;
grid-template-rows: 4% 92% 4%;
position: relative;
margin-left: 15%;
overflow: hidden;
box-shadow: 2px 5px 15px 0px #17161694;
background-color: #122529;
&:hover {
transform: scale(1.1);
transition: 0.3s;
}
`;
The above code creates a card component. I put a background color for it to be visible enough. Everything else shall be done inside the card container. Ensure to import it in the index
directory for the code to run.
Below is a preview of the above
We now introduce a color grid in which we will contain our colored designs. Add the code below to the styles/emotion/card1
directory.
"styles/emotion/card1"
export const ColorGrid = styled.div`
display: grid;
grid-template-columns: repeat(3, 150px);
grid-template-rows: repeat(8, 50px);
grid-gap: 8px;
width: 150px;
height: 400px;
transform: rotate(-45deg);
`;
After importing the code above in the index
directory, include the component inside the card component as shown below
"pages/index"
export default function Home(){
const Card1 = forwardRef((props, ref) => (
<Card_1 ref={refOne} >
<ColorGrid>
</ColorGrid>
</Card_1>
));
function(
<>
<Card1 ref={refOne} />
</>
)
}
You can view your color grid through your browser inspects section. Yor color grid should look like below:
With the color grid set up, we are ready to include our colors as we see fit. The trick is to select the specific grids and fill the background with respective colors. This article will use the color red and green for our card component.
Head to the emotion/card1
directory and add the following
"styles/emotion/card1"
export const Black = styled.div`
margin-left: 30%;
background-color: #343536;
grid-column: 2 / span 2;
grid-row: 1 / span 3;
`;
export const Red1 = styled.div`
background-color: #e45e4f;
grid-row: 2 / span 5;
`;
export const Red2 = styled.div`
background-color: #e45e4f;
grid-column: 2 / span 2;
grid-row: 4/7;
`;
export const Green = styled.div`
background-color: #007e67;
grid-column: 2/4;
grid-row: 7/9;
`;
In the code above as earlier stated. we set the property background-color
of specific grids and use the span
property to stretch the space our color occupies.
For a clearer picture of what we just did import the color components inside the index
directory and include them in your ColorGrid
. Your final card component should look like below
"pages/index"
export default function Home(){
const Card1 = forwardRef((props, ref) => (
<Card_1 ref={refOne} >
<ColorGrid>
<Red1 />
<Red2 />
<Green />
</ColorGrid>
</Card_1>
));
function(
<>
<Card1 ref={refOne} />
</>
)
}
The above code completes our card template component. It should result in a display like shown
That’s it. The template above is ready to be used to design any card, for the purpose of a business card in this article we include a demonstration.
There can be several ideas respective of the developer on how information in the card can be displayed. In our case, we will build an app that captures the template above and stores it online for future reference as well as demonstrates an example of displaying information in the card.
We will also create a second card template that can be used like the card above to help[ find a clearer picture of the css grid functionalities we’ve learned.
In our app, a user can select one of these templates which will be saved online, and explore an example of how the templates can be used through a form that feeds information to an information grid
that will be appended on top of the card.
The final card will only be for demonstration purposes considering a developer/user can have their own ideas on how information in the card will be displayed.
Use the code below to design your form
"pages/index"
export const FormContainer = styled.div`
position: relative;
width: 350px;
height: 100%;
border-radius: 20px;
padding: 40px;
background: #ecf0f3;
box-shadow: 14px 14px 20px #cbced1, -14px -14px 20px white;
`;
export const Inputs = styled.div`
text-align: left;
margin-top: 10px;
`;
export const Label = styled.h4`
display: block;
width: 100%;
padding: 0;
border: none;
outline: none;
box-sizing: border-box;
margin-bottom: 4px;
margin-top: 12px;
font-family: 'Josefin Sans', sans-serif;
`;
export const Input = styled.input`
display: block;
width: 100%;
padding: 0;
border: none;
outline: none;
box-sizing: border-box;
background: #ecf0f3;
padding: 10px;
padding-left: 20px;
height: 45px;
font-size: 14px;
border-radius: 50px;
box-shadow: inset 6px 6px 6px #cbced1, inset -6px -6px 6px white;
&:placeholder {
color: gray;
}
`;
Based on information you’d like to apper on your card you can use state hooks to feed data to your form which will allow it to appear on your card.
"pages/index"
<FormContainer>
<Inputs>
<Label>Name</Label>
<Input
id="name"
placeholder="Full Name"
onChange={(e) => {
setName(e.target.value);
}}
/>
<Label>Company / Business Name</Label>
<Input
id="brandname"
placeholder="company / brand / name"
onChange={(e) => {
setBrand(e.target.value);
}}
/>
<Label>Phone Number</Label>
<Input
id="phonenumber"
placeholder="070000000"
onChange={(e) => {
setPhoneNumber(e.target.value);
}}
/>
<Label>Email</Label>
<Input
id="email"
placeholder="example@test.com"
onChange={(e) => {
setEmail(e.target.value);
}}
/>
<Label >Location</Label>
<Input
id="location"
type="location"
placeholder="location"
onChange={(e) => {
setLocation(e.target.value);
}}
/>
<Label >Website</Label>
<Input
id="website"
type="website"
placeholder="website"
onChange={(e) => {
setWebsite(e.target.value);
}}
/>
</Inputs>
</FormContainer>
Create another card component to show your final card
display demo.
After a template is selected, the background will be uploaded to cloudinary and imported to the final card
as a background. The information grid can then be appended to this background. Use the code below to create an
information grid` sample
"styles/emotion/card1"
export const InfoGrid = styled.div`
grid-column: 2/3;
grid-row: 2/3;
z-index: 2;
width: 100%;
height: 100%;
background-color: rgba(255, 255, 255, 0.363);
box-shadow: 0px 2px 15px rgba(0, 0, 0, 0.432);
border-radius: 6px;
font-size: 0.7rem; /*12px;*/
display: grid;
grid-template-columns: 15px repeat(3, 1fr) 15px;
grid-template-rows: repeat(3, 1fr);
line-height: 1px;
margin-=top: 10px;
`;
export const Name = styled.h2`
grid-column: 1 / span 2;
grid-row: 1 / span 1;
font-size: 1.2em;
letter-spacing: 0.1rem;
margin-top: 15px;
font-size: 20px;
background: transparent;
`;
export const Brand = styled.p`
grid-column: 2 / span 2;
grid-row: 1 / span 1;
font-size: 1.2em;
letter-spacing: 0.1rem;
margin-top: 15%;
margin-left: -28%;
background: transparent;
`;
export const Address = styled.div`
grid-column: 1 / span 2;
grid-row: 4;
margin-left: -35%;
background: transparent;
`;
export const PhoneNumber = styled.div`
grid-column: 3 / span 2;
grid-row: 3;
text-align: end;
background: transparent;
`;
export const Email = styled.div`
grid-column: 3 / span 2;
grid-row: 4;
text-align: end;
background: transparent;
`;
export const CardText = styled.p`
font-size: 1.2em;
letter-spacing: 0.1rem;
`;
export const Back = styled.div`
height: 220px;
width: 390px;
box-shadow: 2px 5px 15px 0px #17161694;
margin-top: 10%;
display: flex;
flex-wrap: wrap;
`;
export const NameTag = styled.div`
margin-top: 0px;
width: 100%;
height: 40%;
box-shadow: 2px 5px 15px 0px #17161694;
margin-top: 17%;
background: rgba(255, 255, 255, 0.363)
`;
export const TextLg = styled.h1`
margin: 2px;
font-weight: 200px;
font-size: 30px;
font-family: 'Heebo', sans-serif;
`;
export const TextSm = styled.p`
margin: 2px;
font-weight: 100px;
font-size: 15px;
font-family: 'Heebo', sans-serif;
`;
The codes explained above results in the grid shown below:
That’s it! We have successfully designed our card template as well as demonstrated how it can be used to create a business card.
Let’s try another card once more for a clearer understanding. Based on the information provided above, create a card component called Card2
inside the index
directory using forwarded
like the first card.
Create the contents of card2
inside the styles/emotion/card2.js` directory by pasting the codes below
"emotion/card2.js"
import styled from '@emotion/styled';
export const Card_2 = styled.div`
box-shadow: 0 14px 28px rgba(0, 0, 0, 0.25), 0 10px 10px rgba(0, 0, 0, 0.22);
display: grid;
grid-template-columns: repeat(12, 1fr);
grid-template-rows: repeat(4, 1fr);
width: 390px;
height: 220px;
font-family: 'Trebuchet MS', sans-serif;
&:hover {
transform: scale(1.1);
transition: 0.3s;
}
`;
export const Purple = styled.div`
background-color: #871F78;
grid-column: 8 / span 5;
grid-row: 1 / span 4;
`;
export const Yellow2 = styled.div`
background-color: #F2B900;
grid-column: 1 / span 7;
grid-row: 1 / span 4;
`;
export const Pink2 = styled.div`
background-color: #fa001a;
-webkit-clip-path: polygon(0% 0%, 100% 0%, 0% 100%);
clip-path: polygon(0% 0%, 100% 0%, 0% 100%);
grid-row: 1 / span 3;
grid-column: 1 / span 11;
position: relative;
z-index: 2;
`;
export const Dots2 = styled.div`
background: radial-gradient(#fa001a 20%, transparent 19%),
radial-gradient(#fa001a 20%, transparent 19%), transparent;
background-size: 6px 6px;
background-position: 0 0, 3px 3px;
grid-column: 1 / span 12;
grid-row: 3 / span 2;
margin: 0 0 15px 20px;
z-index: 1;
`;
export const Intro = styled.div`
background: black;
color: white;
display: flex;
flex-direction: column;
grid-column: 4 / span 6;
grid-row: 2 / span 2;
justify-content: center;
text-align: center;
z-index: 3;
`;
export const FrontName = styled.p`
letter-spacing: 1px;
text-transform: uppercase;
font-size: 18px;
`;
export const FrontBrand = styled.p`
letter-spacing: 1px;
text-transform: uppercase;
font-size: 8px;
margin-top: 5px;
`;
export const Back2 = styled.div`
box-shadow: 0 14px 28px rgba(0, 0, 0, 0.25), 0 10px 10px rgba(0, 0, 0, 0.22);
margin-top: 6%;
margin-left: 6%;
display: grid;
grid-template-columns: repeat(12, 1fr);
grid-template-rows: repeat(12, 1fr);
height: 215px;
width: 100%;
&:hover {
transform: scale(1.1);
}
`;
export const BackYellow = styled.div`
background-color: #f1ef1c;
grid-column: 1 / span 9;
grid-row: 1 / span 3;
`;
export const BackTopDots = styled.div`
background: radial-gradient(#4cc9c8 20%, transparent 19%),
radial-gradient(#4cc9c8 20%, transparent 19%), transparent;
background-size: 6px 6px;
background-position: 0 0, 3px 3px;
grid-column: 8 / span 6;
grid-row: 2 / span 3;
`;
export const BackInfo = styled.div`
grid-column: 2 / span 6;
grid-row: 5 / span 6;
`;
export const BackName = styled.p`
font-size: 18px;
font-weight: bold;
letter-spacing: 1px;
text-transform: uppercase;
`;
export const BackBrand = styled.p`
font-size: 12px;
margin-bottom: 15px;
`;
export const BackText = styled.p`
font-size: 12px;
margin-bottom: 15px;
`;
export const BackDots = styled.div`
background: radial-gradient(#4cc9c8 20%, transparent 19%),
radial-gradient(#4cc9c8 20%, transparent 19%), transparent;
background-size: 6px 6px;
background-position: 0 0, 3px 3px;
grid-column: 1 / span 8;
grid-row: 11 / span 2;
z-index: 2;
`;
export const BackkPink = styled.div`
background-color: #fa001a;
grid-column: 8 / span 5;
grid-row: 10 / span 3;
`;
Import all the above components in your index
directory and include them in your card2
component like you did earlier with card1 using (<Card 1 ref={refTwo}/>
).
"pages/index"
const Card2 = forwardRef((props, ref) => (
<Card_2 ref={refTwo} onClick={card2Handler} >
<Purple />
<Yellow2 />
<Pink2 />
<Dots2 />
</Card_2>
));
The result of this is like below
.
That’s it! Ensure to try out the article process and enjoy the experience. Happy coding!