HTML and CSS essentials
Build a responsive menu page for the Brew & Bean coffee shop with semantic HTML and modern CSS: the box model, Flexbox, Grid and a media query. React outputs HTML and is styled with CSS, so these basics carry straight into every React app.
In this tutorial you will build a one-page menu for Brew & Bean, a small coffee shop in Indiranagar, Bengaluru. It has a header, a grid of menu cards and a footer, and it rearranges itself on a phone. No JavaScript yet.
You need: a code editor (VS Code is free) and a browser (Chrome, Edge or Firefox). That's all. Everything you learn here is used again in React: JSX looks like HTML, and React components are styled with ordinary CSS.
Part 1: HTML gives the page structure
HTML (HyperText Markup Language) describes what each piece of content is: a heading, a paragraph, a list, a button. It does not decide how it looks; that is CSS's job.
An HTML element is usually an opening tag, content and a closing tag: <p>Freshly baked every morning.</p>. Tags can have attributes that add information: <img src="croissant.jpg" alt="Butter croissant">.
Set up the project
- 1Create a folder called
brew-and-bean-staticanywhere on your computer. - 2Open the folder in VS Code (File > Open Folder).
- 3Create two empty files inside it:
index.htmlandstyles.css. - 4Keep the two files side by side; the HTML will link to the CSS by its file name.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Brew & Bean | Menu</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<header class="site-header">
<h1>Brew & Bean</h1>
<nav>
<a href="#menu">Menu</a>
<a href="#visit">Visit us</a>
</nav>
</header>
<main>
<section id="menu">
<h2>Our menu</h2>
<div class="menu-grid">
<article class="card">
<h3>Espresso</h3>
<p class="category">Coffee</p>
<p class="price">₹120</p>
<button type="button">Add to order</button>
</article>
<article class="card">
<h3>Cappuccino</h3>
<p class="category">Coffee</p>
<p class="price">₹180</p>
<button type="button">Add to order</button>
</article>
<article class="card">
<h3>Masala Chai</h3>
<p class="category">Tea</p>
<p class="price">₹90</p>
<button type="button">Add to order</button>
</article>
<article class="card">
<h3>Butter Croissant</h3>
<p class="category">Bakery</p>
<p class="price">₹150</p>
<button type="button">Add to order</button>
</article>
</div>
</section>
<section id="visit">
<h2>Visit us</h2>
<p>Indiranagar, Bengaluru. Open 8 am to 10 pm. Pickup orders only.</p>
</section>
</main>
<footer class="site-footer">
<p>© Brew & Bean</p>
</footer>
</body>
</html>Open the file in your browser (double-click it, or drag it into a browser window). You'll see plain black text on white: correct structure, no styling yet.
What each part does
| Code | Why it is there |
|---|---|
<!doctype html> | Tells the browser to use modern standards mode |
<html lang="en"> | The page language; screen readers and translators use it |
<meta charset="utf-8"> | Lets the page show any character, including the ₹ sign |
<meta name="viewport" ...> | Makes phones use the real screen width instead of zooming out |
<link rel="stylesheet"> | Loads the CSS file |
<header>, <nav>, <main>, <section>, <article>, <footer> | Semantic elements that say what each region is |
class="card" | A label CSS uses to style many elements the same way |
id="menu" | A unique name; href="#menu" jumps to it |
Use semantic elements, not div for everything
A page built only from <div> looks the same, but screen readers, search engines and other developers lose the meaning. Use <button> for actions and <a> for navigation: they work with the keyboard for free. A clickable <div> does not. Accessibility is covered in more depth in *Performance and accessibility*.
Part 2: CSS basics and the box model
CSS (Cascading Style Sheets) is a list of rules. Each rule has a selector (which elements) and declarations (property: value;). The most common selectors are an element name (h1), a class (.card) and an id (#menu).
Every element is a rectangular box with four layers, from the inside out: content, padding (space inside the border), border, and margin (space outside the border). Most layout bugs are box-model surprises, so the first rule below makes sizes predictable.
*,
*::before,
*::after {
box-sizing: border-box;
}
:root {
--brown: #6f4e37;
--cream: #fff8f0;
--text: #2b2118;
}
body {
margin: 0;
font-family: system-ui, sans-serif;
line-height: 1.5;
color: var(--text);
background: var(--cream);
}
main {
max-width: 1000px;
margin: 0 auto;
padding: 1rem;
}
.card {
background: white;
border: 1px solid #e6d8c8;
border-radius: 12px;
padding: 1rem;
}
.price {
font-weight: 700;
color: var(--brown);
}
.category {
margin: 0;
color: #7a6a5c;
font-size: 0.9rem;
}Save and refresh the browser. The background turns cream and each item becomes a white card with rounded corners.
Lines worth understanding
| Code | What it does |
|---|---|
box-sizing: border-box | width now includes padding and border, so a 300px box stays 300px |
--brown: #6f4e37 | A CSS custom property (variable); use it with var(--brown) |
margin: 0 auto | No top/bottom margin, equal left/right margin: centres a block with a max width |
rem | A size relative to the root font size (usually 16px), so text scales if the user enlarges it |
system-ui | Uses the device's own font, which loads instantly |
Part 3: layout with Flexbox and Grid
Modern CSS has two layout tools. Flexbox arranges items in one direction (a row or a column), which suits a header with a logo on one side and links on the other. Grid arranges items in rows and columns at once, which suits a grid of cards.
.site-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 1rem;
background: var(--brown);
color: white;
}
.site-header h1 {
margin: 0;
font-size: 1.5rem;
}
.site-header nav {
display: flex;
gap: 1rem;
}
.site-header a {
color: white;
}
.menu-grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 1rem;
}
.card button {
width: 100%;
padding: 0.6rem;
border: none;
border-radius: 8px;
background: var(--brown);
color: white;
font: inherit;
cursor: pointer;
}
.card button:hover {
background: #5a3e2b;
}
.site-footer {
text-align: center;
padding: 1rem;
color: #7a6a5c;
}Refresh. The header becomes a brown bar with the name on the left and links on the right, and the four cards sit in one row.
display: flex on the header turns its children into flex items. justify-content: space-between pushes them to opposite ends of the main axis (horizontal), and align-items: center centres them on the cross axis (vertical). gap adds space between items without margins.
grid-template-columns: repeat(4, 1fr) creates four equal columns; 1fr means one share of the free space. .site-header a is a descendant selector: it matches only links inside the header. :hover is a pseudo-class that applies while the mouse is over the button.
Part 4: make it responsive
Four columns look cramped on a phone. A media query applies rules only when a condition is true, such as the screen being narrower than a given width. Many Indian users browse on mid-range Android phones, so always check the narrow layout.
@media (max-width: 800px) {
.menu-grid {
grid-template-columns: repeat(2, 1fr);
}
}
@media (max-width: 480px) {
.menu-grid {
grid-template-columns: 1fr;
}
.site-header {
flex-direction: column;
gap: 0.5rem;
}
}Below 800px the grid has two columns; below 480px it has one, and the header stacks vertically.
Test the layout on a phone size
- 1Open DevTools with
F12. - 2Click the device toolbar icon (a phone and tablet) or press
Ctrl+Shift+M(Cmd+Shift+Mon a Mac). - 3Choose a phone size from the dropdown at the top, or drag the edge of the viewport.
- 4Watch the grid change from four columns to two to one as the width shrinks.
Wide screen: a brown header bar, the heading "Our menu", then four white cards in a row (Espresso ₹120, Cappuccino ₹180, Masala Chai ₹90, Butter Croissant ₹150), each with a full-width brown "Add to order" button. Phone width: the header stacks, and the cards sit one below another.A shortcut you will use later
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)) creates as many columns as fit, each at least 200px wide, with no media query at all. Try replacing the four-column rule with it and resizing the window.
Common mistakes
Forgetting the viewport <meta> tag makes phones show a tiny zoomed-out desktop page, and media queries seem not to work. A typo in a CSS property name fails silently: the browser just ignores that line, so check the Elements panel in DevTools, which shows crossed-out or invalid declarations. And when two rules conflict, the more specific selector wins (an id beats a class, a class beats an element name); if specificity is equal, the later rule wins.
Part 5: from HTML to React
Notice how the four cards repeat the same structure with different data. In React you will write that structure once as a MenuCard component and feed it data. The CSS you wrote here works in React almost unchanged; the main difference is that JSX uses className instead of class. You will see this in *Components and JSX*.
Review this HTML and CSS for a beginner project. I'm learning web development.
Check for:
1. Missing or wrong semantic elements
2. Accessibility problems (alt text, buttons vs links, colour contrast)
3. CSS that could be simpler
4. Anything that will break on a phone
For each problem: quote the line, explain why it matters in one sentence, and show the fix. Don't rewrite the whole page.
<html>
[paste index.html]
</html>
<css>
[paste styles.css]
</css>What to expect: A useful review quotes specific lines and explains why. Check every suggestion in the browser before accepting it; AI sometimes suggests properties that don't exist.
Try it: Extend the menu
1. Add the other four items: Cold Brew (coffee, ₹220), Filter Coffee (coffee, ₹80), Banana Bread (bakery, ₹130) and Veg Grilled Sandwich (food, ₹160).
2. Add an "Opening hours" table inside the Visit us section using <table>, <tr>, <th> and <td>.
3. Make the cards lift slightly on hover using transform: translateY(-2px) and a box-shadow.
4. Check the page at phone width.
Key takeaways
- HTML describes structure and meaning; CSS describes appearance and layout.
- Use semantic elements (
header,main,section,article,button,a); they help accessibility and readability. - Every element is a box: content, padding, border, margin. Set
box-sizing: border-boxeverywhere. - Flexbox lays out in one direction; Grid lays out rows and columns.
- Media queries plus the viewport meta tag make a page responsive. Test at phone width in DevTools.
- React outputs the same HTML and uses the same CSS, with
classNameinstead ofclass.
Check yourself
Finishing the quiz marks the lesson as done.
1. You set .card { width: 300px; padding: 20px; border: 1px solid; } without box-sizing: border-box. How wide does the card appear?
2. You need a header with the shop name on the left and two links on the right, vertically centred. Which is the simplest fit?
3. Your media queries work in DevTools on desktop but a real phone shows a tiny zoomed-out page. What is most likely missing?
4. Which element should you use for "Add to order", which performs an action on the page?
5. Two rules set the colour of the same paragraph: p { color: black; } and .price { color: brown; }. The paragraph has class="price". Which colour wins and why?
Saved on this device. Create a free account to keep it on every device.
Practise this with Sam
A real mock interview, out loud, with an honest scorecard. Free to start.