Series Build a Website from Scratch Part 4 of 10

CSS Basics —
Giving Your Site a Visual Identity

We've built the HTML skeleton and filled it with content. Now it's time to introduce CSS and give café SOEL color, typography, and spacing. The moment you save and refresh, the transformation will feel almost magical.

Quick Recap

📖 Quick Recap
  • Completed the HTML for every section of café SOEL — header through footer
  • Added class attributes as "name tags" for CSS styling later
  • Placed images with <img> and structured shop info with <dl>

If your code from last time is displaying in the browser, you're all set to move forward.

Goals for This Part

🎯 Goals for This Part
  • Understand CSS syntax — selectors, properties, and values
  • Use CSS custom properties (variables) to manage your site's color palette
  • Learn the box model and how it affects layout
  • Give café SOEL color, font sizing, spacing, and a full-screen hero section
👀 Preview the Result
By the end of this part, your site will look like this. Feel free to preview it before we start.
See the Part 4 result →

The black text and oversized images from last time are about to get a serious makeover. We won't be arranging elements side by side yet — that's next part's Flexbox lesson — but color, type, and spacing alone will make a dramatic difference.

What Is CSS?

CSS stands for Cascading Style Sheets, and it's the language that gives HTML its visual presentation.

Think of it this way: over the last three parts, we built the frame of a house (HTML). Starting today, we're choosing paint colors, laying flooring, and hanging lights. HTML decides the structure; CSS decides the look. Keeping these roles separate is one of the core principles of web development.

CSS controls an enormous range of visual details — text color and size, backgrounds, spacing, rounded corners, how images are displayed — and we'll pick them up gradually throughout this series. Today we focus on the essentials.

HTML decides what to say. CSS decides how it looks. Respecting that separation is the first step toward clean code.

Three Ways to Write CSS

There are three places you can put CSS. We'll use the third — an external file — but knowing all three helps you understand what you might encounter in the wild.

① Inline styles

Write CSS directly in an element's style attribute.

❌ Not recommended
<p style="color: red; font-size: 20px;">Red text</p>

Quick to write, but it tangles HTML and CSS together. Maintenance becomes painful as the page grows.

② A <style> tag

Place a <style> block inside <head>.

Inside index.html's head
<style>
  p {
    color: red;
    font-size: 20px;
  }
</style>

Works fine for a single file, but you can't share styles across multiple pages.

③ An external CSS file (recommended)

Write CSS in a separate .css file and link it from HTML. This is what we'll use.

Inside index.html's head
<link rel="stylesheet" href="css/style.css">

HTML and CSS stay completely separate, making your code easier to read and maintain. Multiple pages can share the same stylesheet. This is the industry standard.

💡 KANON's Tip
When I first started learning on my own, I loved changing colors with inline style attributes — it felt so immediate. But the day I wanted to change every text color at once, I had to edit dozens of elements one by one. With an external CSS file, one change propagates everywhere. Build that habit from the start.

Create and Link a CSS File

1

Create style.css

In VS Code's sidebar, right-click the css folder and choose "New File." Name it style.css.

📁 Current folder structure
cafe-soel/
├── index.html
├── css/
│   └── style.css    ← created now!
├── img/
│   ├── hero.jpg
│   ├── ...
└── js/
2

Add a link tag to index.html

Open index.html and add the following line inside <head>, just before the <title> tag.

index.html — head section
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <link rel="stylesheet" href="css/style.css">  <!-- ← add this line -->
  <title>café SOEL — そよぐ光と、ひと息つくひととき</title>
</head>

HTML and CSS are now connected. Any styles you write in style.css will automatically apply to the page. The file is empty for now, so nothing changes visually — but that's about to change fast.

CSS Syntax Basics

CSS has a beautifully simple structure. Every rule consists of three parts: which element to style, what to change, and what value to give it.

Basic CSS syntax
selector {
  property: value;
}

The selector targets which elements receive the style. The property says what to change (color, size, spacing). The value says how to change it (red, 20px, 10px). And don't forget the semicolon ; at the end of each declaration.

For example, to make all paragraphs gray:

css/style.css
p {
  color: #6b6b6b;
}

Types of Selectors

We'll use three types of selectors in this part.

Element selectors target every instance of an HTML tag.

Element selector
body {
  font-size: 16px;
}

Class selectors target elements by the class attribute we added last time. Prefix the class name with a dot (.).

Class selector
.hero-title {
  font-size: 52px;
}

Descendant selectors target elements nested inside another element. Separate selectors with a space.

Descendant selector
.menu-card-body p {
  font-size: 14px;
}

All those class attributes we carefully added last time? This is exactly why. Class selectors let you target specific elements with pinpoint accuracy.

Managing Colors with CSS Variables

The café SOEL site reuses the same colors over and over — background tones, text shades, accent hues. Writing hex codes like #2c2c2c every time would be tedious, and changing the palette later would mean hunting down every occurrence.

That's where CSS custom properties (variables) come in. Define your colors once inside the :root selector, then reference them by name throughout your stylesheet. If you ever want to change a color, update it in one place and the entire site follows.

Add the following to the very top of style.css. This sets up the color palette and layout constants for café SOEL.

css/style.css — CSS custom properties
/* ---------- CSS Custom Properties ---------- */
:root {
  --color-bg: #ffffff;
  --color-bg-soft: #faf9f7;
  --color-bg-accent: #f5f0eb;
  --color-text: #2c2c2c;
  --color-text-light: #6b6b6b;
  --color-text-muted: #999999;
  --color-accent: #8b7355;
  --color-accent-light: #c4a882;
  --color-border: #e8e4df;
  --color-white: #ffffff;
  --color-black: #1a1a1a;

  --section-padding: 120px;
  --container-width: 1200px;
  --container-padding: 24px;
}

:root is a selector that matches the top-level element of the document. Variables defined here are available everywhere. Custom property names must start with --.

To use a variable, write var(--variable-name). For instance, color: var(--color-text);. You'll see this pattern throughout the code we write next.

📌 Remember This
The biggest advantage of CSS variables is resilience to change. Want to swap the accent color from brown to teal? Update one line in :root and it propagates across the entire site. It's an essential technique in professional workflows.

Understanding the Box Model

Before we start styling sections, let's learn a fundamental concept. Every HTML element is treated as a rectangular "box" by the browser. Each box has four layers, from inside out.

▶ Live Demo — The Box Model
margin (outer spacing)
border
padding (inner spacing)
content

Content is the actual stuff inside the element — text, images, etc. Padding is the space between the content and the border. Border is the visible (or invisible) edge around the element. Margin is the space between this element and its neighbors.

Here's the catch: by default, when you set a width in CSS, it only applies to the content area. Padding and border get added on top, making the element larger than you expected. Setting box-sizing: border-box changes this so that width includes padding and border. "300px wide means 300px on screen." Intuitive, and used on virtually every modern website.

Reset and Base Styles

Browsers come with built-in "default styles." That's why <h1> is big and bold and <ul> has bullet points even without any CSS. The problem is that these defaults vary slightly between browsers.

The standard practice is to "reset" these defaults first, then build your own styles from scratch. Add the following below the CSS variables.

css/style.css — Reset & base
/* ---------- Reset & Base ---------- */
*,
*::before,
*::after {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

html {
  scroll-behavior: smooth;
  scroll-padding-top: 80px;
}

body {
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI',
               'Hiragino Kaku Gothic ProN', 'Noto Sans JP', sans-serif;
  font-weight: 400;
  font-size: 16px;
  line-height: 1.9;
  color: var(--color-text);
  background-color: var(--color-bg);
}

img {
  display: block;
  max-width: 100%;
  height: auto;
}

a {
  color: inherit;
  text-decoration: none;
}

ul, ol {
  list-style: none;
}

The * (asterisk) selector means "every element." We use it to zero out margins and padding, and to apply box-sizing: border-box universally.

scroll-behavior: smooth makes in-page navigation links scroll gently instead of jumping. Those anchor links you built last time just got a lot more pleasant.

The body font stack lists system fonts — the ones already installed on the user's operating system. The browser picks the first one it finds. On macOS you'll get Hiragino Kaku Gothic; on Windows, Noto Sans JP or Segoe UI.

Setting max-width: 100% on img prevents images from overflowing their containers. Remember those giant images from last time? This single line fixes that.

Save and check your browser. List markers should be gone, spacing reset, and images nicely constrained to the page width.

⚠️ Common Mistake
If nothing changes after saving, double-check the href in your <link> tag. The path href="css/style.css" must match your actual folder structure. If you're using Live Server, it auto-refreshes, but a stale cache might need a hard reload: Ctrl + Shift + R (Cmd + Shift + R on Mac).

Shared Section Styles

With the reset in place, let's style the reusable pieces that appear across multiple sections.

css/style.css — Utility styles
/* ---------- Utility ---------- */
.section {
  padding: var(--section-padding) var(--container-padding);
}

.section-label {
  display: inline-block;
  font-size: 14px;
  font-weight: 600;
  letter-spacing: 3px;
  text-transform: uppercase;
  color: var(--color-accent);
  margin-bottom: 16px;
}

.section-heading {
  font-size: clamp(24px, 4vw, 36px);
  font-weight: 700;
  line-height: 1.4;
  letter-spacing: 0.02em;
  color: var(--color-black);
  margin-bottom: 20px;
}

.section-header {
  text-align: center;
  max-width: 600px;
  margin: 0 auto 60px;
}

.section-desc {
  font-size: 15px;
  color: var(--color-text-light);
  line-height: 1.8;
}

clamp(24px, 4vw, 36px) is a handy function: "minimum 24px, ideally 4% of the viewport width, maximum 36px." The heading scales fluidly with screen size — a head start on responsive design.

margin: 0 auto 60px means "no top margin, auto left/right (which centers the block), 60px bottom margin." Centering a block element with auto margins is one of the most common patterns in CSS.

Styling the Header

The header is the navigation bar that stays pinned to the top of the screen. We use position: fixed to keep it visible even as the user scrolls.

css/style.css — Header
/* ---------- Header ---------- */
.header {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  z-index: 100;
  background-color: var(--color-white);
  border-bottom: 1px solid var(--color-border);
}

.header-inner {
  max-width: var(--container-width);
  margin: 0 auto;
  padding: 0 var(--container-padding);
  height: 72px;
}

.logo img {
  height: 32px;
  width: auto;
}

.nav-link {
  font-size: 15px;
  font-weight: 600;
  letter-spacing: 2px;
  text-transform: uppercase;
  color: var(--color-text);
}

position: fixed locks an element to the browser viewport. Combined with top: 0; left: 0; width: 100%;, it creates a full-width bar that stays at the top. z-index: 100 controls stacking order — a higher number means closer to the viewer, so the header overlaps the hero image and other sections as you scroll.

You'll notice the logo and nav links are stacked vertically instead of sitting side by side. That's because we haven't introduced Flexbox yet — it's coming in Part 5. The vertical stacking is expected right now.

The Hero Section — Today's Highlight

This is the centerpiece of today's work. We'll make the hero fill the entire screen, overlay a semi-transparent gradient on the image, and center the text on top.

css/style.css — Hero
/* ---------- Hero ---------- */
.hero {
  position: relative;
  height: 100vh;
  min-height: 600px;
  max-height: 1000px;
  overflow: hidden;
}

.hero-image {
  position: absolute;
  inset: 0;
  z-index: 1;
}

.hero-image img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.hero-image::after {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(
    to bottom,
    rgba(0, 0, 0, 0.15) 0%,
    rgba(0, 0, 0, 0.35) 100%
  );
}

.hero-content {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  z-index: 2;
  text-align: center;
  color: var(--color-white);
  padding: 0 var(--container-padding);
}

.hero-sub {
  font-size: 14px;
  font-weight: 600;
  letter-spacing: 4px;
  text-transform: uppercase;
  margin-bottom: 20px;
  opacity: 0.9;
}

.hero-title {
  font-size: clamp(28px, 5vw, 52px);
  font-weight: 700;
  line-height: 1.5;
  letter-spacing: 0.06em;
  margin-bottom: 40px;
  text-shadow: 0 2px 20px rgba(0, 0, 0, 0.2);
}

.hero-cta {
  display: inline-block;
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 3px;
  text-transform: uppercase;
  color: var(--color-white);
  padding: 12px 36px;
}

Let's walk through the key ideas.

height: 100vh — the vh unit equals 1% of the viewport (browser window) height. 100vh means "fill the entire screen height."

position: absolute with inset: 0 on .hero-image stretches the image container to fill its parent (.hero). inset: 0 is shorthand for top: 0; right: 0; bottom: 0; left: 0;.

object-fit: cover scales the image to fill the frame while preserving its aspect ratio — any overflow gets cropped, like fitting a photo into a picture frame.

The ::after pseudo-element creates a semi-transparent gradient overlay on top of the image. This darkens the photo just enough to make the white text readable — a staple technique for hero sections.

💡 KANON's Tip
To center .hero-content, we used position: absolute combined with transform: translate(-50%, -50%). The idea: place the element's top-left corner at 50% from the top and left of its parent, then shift it back by half its own width and height. It's a classic positioning trick. In Part 5, you'll learn that Flexbox can do this much more simply. Stay tuned!

Styling the Remaining Sections

With the hero looking great, let's add basic styles to the remaining sections. We're not using Flexbox yet, so nothing will sit side by side — but color, typography, and spacing alone make a huge difference.

css/style.css — Concept, Menu, Gallery, Access, Footer
/* ---------- Concept ---------- */
.concept {
  background-color: var(--color-bg);
}

.concept-inner {
  max-width: var(--container-width);
  margin: 0 auto;
}

.concept-image {
  border-radius: 12px;
  overflow: hidden;
}

.concept-text p {
  font-size: 15px;
  line-height: 2;
  color: var(--color-text-light);
  margin-bottom: 16px;
}

.concept-text p:last-child {
  margin-bottom: 0;
}

/* ---------- Menu ---------- */
.menu {
  background-color: var(--color-bg-soft);
}

.menu-card-body {
  padding: 24px;
}

.menu-card-title {
  font-size: 20px;
  font-weight: 700;
  letter-spacing: 1px;
  margin-bottom: 8px;
  color: var(--color-black);
}

.menu-card-desc {
  font-size: 14px;
  line-height: 1.8;
  color: var(--color-text-light);
  margin-bottom: 12px;
}

.menu-card-price {
  font-size: 18px;
  font-weight: 600;
  color: var(--color-accent);
  letter-spacing: 1px;
}

/* ---------- Gallery ---------- */
.gallery {
  background-color: var(--color-bg);
}

/* ---------- Access ---------- */
.access {
  background-color: var(--color-bg-soft);
}

.access-inner {
  max-width: var(--container-width);
  margin: 0 auto;
}

.access-image {
  border-radius: 12px;
  overflow: hidden;
}

.info-list {
  margin-top: 12px;
}

.info-item {
  padding: 16px 0;
  border-bottom: 1px solid var(--color-border);
}

.info-item:last-child {
  border-bottom: none;
}

.info-item dt {
  font-size: 13px;
  font-weight: 500;
  color: var(--color-text-muted);
  letter-spacing: 0.5px;
  margin-bottom: 4px;
}

.info-item dd {
  font-size: 15px;
  line-height: 1.8;
  color: var(--color-text);
}

.info-item dd a {
  color: var(--color-accent);
}

/* ---------- Footer ---------- */
.footer {
  background-color: var(--color-black);
  color: var(--color-white);
  padding: 60px var(--container-padding);
}

.footer-inner {
  max-width: var(--container-width);
  margin: 0 auto;
}

.footer-logo img {
  height: 24px;
  width: auto;
  filter: brightness(0) invert(1);
  opacity: 0.8;
}

.footer-nav a {
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 2px;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.5);
}

.footer-copy {
  font-size: 12px;
  color: rgba(255, 255, 255, 0.35);
  letter-spacing: 0.5px;
}

border-radius: 12px rounds corners. Paired with overflow: hidden, the image corners get clipped to match the rounded container.

In the footer, filter: brightness(0) invert(1) turns the logo image black first, then inverts it to white — a quick trick for displaying a dark logo on a dark background without needing a separate white version.

rgba(255, 255, 255, 0.5) means "white at 50% opacity." The footer nav links appear muted, keeping the visual focus on the main content above.

Check Your Progress

Save the CSS file and refresh your browser. The transformation from raw HTML should be striking.

👀 Preview the Result
Your site should now look like this.
See the Part 4 result →

The hero image fills the screen with the catchphrase centered over a soft overlay. Sections alternate between white and warm gray backgrounds. The footer has a dark, elegant look. It's starting to feel like a real website.

The header nav links are still stacked vertically, and the concept section's image and text sit in a single column. That's expected — arranging elements side by side requires layout techniques we'll cover in Part 5 with Flexbox. Does your screen match?

Below is the full code for both files at this point. If anything looks off, copy-paste these to get back on track.

index.html (Part 4 complete)
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <link rel="stylesheet" href="css/style.css">
  <title>café SOEL — そよぐ光と、ひと息つくひととき</title>
</head>
<body>
  <!-- Same HTML as Part 3, with <link> tag added in <head> -->
  <!-- Full HTML is identical to the Japanese version above -->
</body>
</html>

The HTML is identical to the Japanese version shown above — the only change from Part 3 is the addition of the <link rel="stylesheet"> tag in <head>.

The full css/style.css is also identical to the Japanese version's final code block above — CSS is the same regardless of language.

What You Learned

  • CSS is the language that gives HTML its visual presentation. Of the three ways to write it, external stylesheets are the industry standard
  • CSS syntax follows the pattern "selector { property: value; }." Selectors come in three main flavors: element, class, and descendant
  • CSS custom properties defined in :root let you manage colors and sizes in one place, making global changes effortless
  • The box model has four layers — content → padding → border → margin. box-sizing: border-box makes sizing intuitive
  • A CSS reset zeroes out browser defaults so you have full control over your design
  • The hero section uses 100vh, position, object-fit: cover, a pseudo-element overlay, and transform centering
  • Elements are still stacked vertically — side-by-side layouts arrive next part with Flexbox