Margherita
8.50 €
Nikita Goluban
Full-Stack Developer Logistics & Marketing Mechanical & Construction Skills Woodwork & Metalwork Polyglot
React.js + Next.js · Module 2. React Basics · Lesson 10
React components can be styled in several ways. Pizza Palace will use styles for menu cards, offer banners, availability messages, cart buttons, layouts, and responsive pages.
The most common approaches are:
This lesson focuses mainly on CSS Modules. They provide locally scoped class names and reduce accidental conflicts between components.
className instead of class.styles.className.REACT · 01
React does not replace CSS. Components return HTML-like JSX, and CSS controls how the resulting browser elements look.
CSS can control:
function PizzaCard() {
return (
<article>
<h2>Margherita</h2>
<p>Price: 8.50 €</p>
</article>
);
}
export default PizzaCard;
We can now connect this markup to one of React's styling approaches.
REACT · 02
The JSX style property accepts a JavaScript object.
function OfferBanner() {
return (
<p
style={{
padding: "12px 16px",
color: "white",
backgroundColor: "#b42318",
borderRadius: "8px"
}}
>
Tuesday offer: two Margheritas for 15 €!
</p>
);
}
export default OfferBanner;
The outer {} enters JavaScript inside JSX. The inner {} creates the style object.
| CSS property | React inline property |
|---|---|
background-color |
backgroundColor |
font-size |
fontSize |
margin-top |
marginTop |
border-radius |
borderRadius |
text-align |
textAlign |
Some properties accept numbers:
<div style={{ padding: 20, opacity: 0.8 }}>Pizza details</div>
React interprets padding: 20 as pixels. Properties such as opacity and lineHeight are unitless.
Inline styles are useful for small or dynamic values, but they are inconvenient for large stylesheets, pseudo-classes such as :hover, and media queries.
REACT · 03
Regular CSS keeps styling separate from the JSX.
src/
└── components/
├── OrderNotice.tsx
└── OrderNotice.css
.order-notice {
padding: 12px 16px;
color: white;
background-color: #b42318;
border-radius: 8px;
}
import "./OrderNotice.css";
function OrderNotice() {
return (
<p className="order-notice">
Delivery takes approximately 35 minutes.
</p>
);
}
export default OrderNotice;
The CSS import loads the file, while className="order-notice" assigns the class.
REACT · 04
Ordinary HTML uses class:
<article class="pizza-card">Margherita</article>
JSX uses the DOM property name className:
function PizzaCard() {
return (
<article className="pizza-card">
<h2>Margherita</h2>
<p>8.50 €</p>
</article>
);
}
className into the element's HTML class attribute.Use className for both regular CSS and CSS Modules.
REACT · 05
Classes in regular CSS files are global by default. Imagine two files with the same selector:
.button {
background-color: #2563eb;
color: white;
}
.button {
background-color: #16803c;
color: white;
}
If both files load, both rules match every element with className="button". The final result depends on the cascade, specificity, and source order.
Names such as .button, .title, .card, and .container are likely to be repeated in a large application. CSS Modules reduce this risk by generating locally scoped class names.
REACT · 06
A CSS Module is a CSS file whose class names are locally scoped when processed by the build tool. Vite recognises files ending in .module.css:
PizzaCard.module.css
Import the file as an object:
import styles from "./PizzaCard.module.css";
Then use a class from that object:
<article className={styles.card}>Margherita</article>
PizzaCard.module.css and CartSummary.module.css can both define .card; Vite generates different class names for them.
REACT · 07
Create a delivery notice with locally scoped styles.
src/
└── components/
├── DeliveryNotice.tsx
└── DeliveryNotice.module.css
.notice {
padding: 12px 16px;
background-color: #475467;
border-radius: 8px;
color: white;
}
import styles from "./DeliveryNotice.module.css";
export default function DeliveryNotice() {
return (
<p className={styles.notice}>
Free delivery for orders over 25 €.
</p>
);
}
import DeliveryNotice from "./components/DeliveryNotice";
export default function App() {
return (
<main>
<h1>Pizza Palace</h1>
<DeliveryNotice />
</main>
);
}
DeliveryNotice.module.css.REACT · 08
This import:
import styles from "./DeliveryNotice.module.css";
provides an object that maps source class names to generated class names. Vite may create a mapping conceptually similar to:
const styles = {
notice: "_notice_a1b2c_1"
};
Therefore:
<p className={styles.notice}>Free delivery</p>
becomes HTML conceptually similar to:
<p class="_notice_a1b2c_1">Free delivery</p>
The generated name is implementation-dependent and can change. Never hard-code it in your component.
REACT · 09
No. The local variable name is the developer's choice.
import styles from "./DeliveryNotice.module.css";
<p className={styles.notice}>Free delivery</p>
This is also valid:
import css from "./DeliveryNotice.module.css";
<p className={css.notice}>Free delivery</p>
The important rule is consistency. If you import the module as styles, use styles.notice, not css.notice.
styles is a common convention because it clearly communicates the object's purpose.
REACT · 10
One module can provide all classes needed by its component.
.card {
padding: 20px;
border: 1px solid #e4e7ec;
border-radius: 12px;
background-color: white;
}
.title {
color: #9f1d16;
font-size: 24px;
}
.price {
color: #16803c;
font-size: 20px;
font-weight: 700;
}
import styles from "./PizzaCard.module.css";
export default function PizzaCard() {
return (
<article className={styles.card}>
<h2 className={styles.title}>Pepperoni</h2>
<p>Tomato, mozzarella, pepperoni</p>
<p className={styles.price}>10.50 €</p>
</article>
);
}
REACT · 11
Both components can define .card without affecting each other.
.card {
background-color: #fff3dc;
border-left: 5px solid #d92d20;
padding: 20px;
}
.card {
background-color: #e8f2ff;
border-left: 5px solid #2563eb;
padding: 20px;
}
// PizzaCard.tsx
import styles from "./PizzaCard.module.css";
export function PizzaCard() {
return <article className={styles.card}>Margherita · 8.50 €</article>;
}
// CartSummary.tsx
import styles from "./CartSummary.module.css";
export function CartSummary() {
return <aside className={styles.card}>Your cart · 1 item</aside>;
}
.card, but each component keeps its own appearance.REACT · 12
CSS Modules are a build-tool feature, not a native browser feature.
PizzaCard.module.css
↓
Vite processes the module
↓
Unique class names are generated
↓
JavaScript receives a class-name mapping
↓
React assigns the generated class
↓
The browser applies the CSS
Source CSS:
.card {
padding: 20px;
}
Illustrative generated CSS:
._card_a1b2c_1 {
padding: 20px;
}
Illustrative mapping:
{ card: "_card_a1b2c_1" }
The browser receives the generated string returned by styles.card.
REACT · 13
CSS Modules support pseudo-classes, media queries, CSS variables, animation, Flexbox, and Grid.
.button {
padding: 10px 20px;
border: 0;
border-radius: 8px;
background-color: #d92d20;
color: white;
cursor: pointer;
}
.button:hover {
background-color: #9f1d16;
}
@media (max-width: 600px) {
.button {
width: 100%;
}
}
import styles from "./OrderButton.module.css";
export default function OrderButton() {
return (
<button className={styles.button}>
Add Margherita to cart
</button>
);
}
Move the pointer over the buttons on the real page to see the hover state.
REACT · 14
Both approaches can be used in one application.
body {
margin: 0;
font-family: Arial, sans-serif;
background-color: #fffaf5;
}
img {
max-width: 100%;
}
.card {
padding: 20px;
border-radius: 12px;
background-color: white;
}
src/
├── components/
│ ├── PizzaCard.tsx
│ ├── PizzaCard.module.css
│ ├── OrderButton.tsx
│ └── OrderButton.module.css
├── App.tsx
├── index.css
└── main.tsx
| Global CSS | CSS Modules |
|---|---|
| Global selectors | Locally scoped class names |
| Good for resets, fonts, and page foundations | Good for component-specific styles |
| Naming conflicts are possible | Naming conflicts are reduced |
| Imported for its side effect | Imported as a class-name mapping |
Use global CSS intentionally for truly shared rules and modules for component details.
REACT · 15
The same component can style available and sold-out pizzas differently.
.card {
padding: 20px;
border: 1px solid #e4e7ec;
border-radius: 12px;
background-color: white;
}
.title {
margin-bottom: 8px;
font-size: 22px;
}
.ingredients {
color: #475467;
}
.price {
color: #16803c;
font-size: 20px;
font-weight: 700;
}
.available {
color: #16803c;
}
.unavailable {
color: #b42318;
}
import styles from "./PizzaCard.module.css";
interface PizzaCardProps {
name: string;
ingredients: string;
price: number;
available: boolean;
}
export default function PizzaCard(props: PizzaCardProps) {
const availabilityClass = props.available
? styles.available
: styles.unavailable;
return (
<article className={styles.card}>
<h2 className={styles.title}>{props.name}</h2>
<p className={styles.ingredients}>{props.ingredients}</p>
<p className={styles.price}>{props.price.toFixed(2)} €</p>
<p className={availabilityClass}>
{props.available ? "Available" : "Sold out"}
</p>
</article>
);
}
import PizzaCard from "./components/PizzaCard";
export default function App() {
return (
<main>
<h1>Pizza Palace Menu</h1>
<PizzaCard
name="Margherita"
ingredients="Tomato, mozzarella, basil"
price={8.5}
available={true}
/>
<PizzaCard
name="Truffle Pizza"
ingredients="Mushrooms, mozzarella, truffle cream"
price={14}
available={false}
/>
</main>
);
}
REACT · 16
Incorrect: <article class="card">
Correct: <article className="card">
If you write import styles from "./PizzaCard.module.css", use styles.card, not css.card.
Incorrect:
<article className="styles.card">
This assigns the literal class name styles.card.
Correct:
<article className={styles.card}>
PizzaCard.css is regular global CSS. Use PizzaCard.module.css when you want a CSS Module.
This does not provide the styles object:
import "./PizzaCard.module.css";
Use:
import styles from "./PizzaCard.module.css";
For .pizza-card, use styles["pizza-card"]. A simpler module convention is .pizzaCard, which works as styles.pizzaCard.
Do not copy a generated name such as _card_a1b2c_1 into JSX, tests, or JavaScript selectors. Use the imported mapping.
Inline styles are useful for genuinely dynamic values, but CSS Modules are clearer for hover states, media queries, reusable rules, and complete component designs.
REACT · 17
Create a Pizza Palace offer using an inline background colour, padding, and border radius.
Create DeliveryNotice.css, import it into DeliveryNotice.tsx, and apply its class with className.
Create PizzaCard.module.css with .card, .title, and .price, then use all three classes in PizzaCard.tsx.
Create .card in both PizzaCard.module.css and CartSummary.module.css. Give them visibly different designs and render both components.
Add :hover and :focus-visible rules to an order button. Ensure keyboard users can see the focus indicator.
Use a Boolean prop to choose between styles.available and styles.unavailable.
Use a media query so a three-column pizza grid becomes one column on a narrow screen.
REACT · 18
className, not class..module.css extension.styles.card.REACT · 19
Next lesson: Class composition with clsx, using Pizza Palace component props and state to select styles cleanly.