The skeleton — every HTML file starts with this
<!DOCTYPE html>

Document type declaration

Tells the browser "this file is written in modern HTML." It's not a tag — it's a declaration. Every HTML file ever made starts with this exact line.

the cover page of a document that tells you what format it's in.
<html lang="en"> ... </html>

The root element

Everything on your page lives inside this tag. The lang="en" tells the browser (and screen readers) the page is written in English — useful for accessibility and search engines.

<head> ... </head>

The head — invisible settings

Contains information about the page rather than content that appears on it. The browser reads this section but doesn't display it to the visitor.

the settings menu of your page — the user never sees it but it affects everything.
<meta charset="UTF-8"/>

Character encoding

Tells the browser which character set to use when reading your file. UTF-8 supports virtually every character in every language — including accented letters, em dashes, and emoji. Without it, special characters might show up as garbled symbols.

<meta name="viewport" content="width=device-width, initial-scale=1.0"/>

Viewport — makes your site work on phones

The viewport is the visible area of the browser window. Without this line, phones would pretend to have a wide desktop screen and shrink everything down to fit — making your text tiny and unreadable. This line says: "use the real screen width, at normal zoom."

telling a phone "don't zoom out to show a desktop version — just show my page at normal size."
<title>Your Name</title>

Page title

The text that appears in the browser tab and in search engine results. It's not visible on the page itself. Change this to your actual name.

<body> ... </body>

The body — everything visitors actually see

All your visible content — the sidebar, the tabs, the About section, everything — lives inside the body tag. If it's in the head, it's invisible. If it's in the body, it shows up on the page.

the actual page of a book, as opposed to its cover and copyright info.
The style block — how things look
<style> ... </style>

Where your CSS lives

Everything between these tags is CSS — the language that controls how your page looks. HTML is the structure; CSS is the design. The browser reads the style block first, then applies those rules to the HTML below.

HTML is the furniture in a room. CSS is the paint, fabric, and lighting.
* { box-sizing: border-box; margin: 0; padding: 0; }

The universal reset

The * means "apply this to every element." Browsers add their own default spacing to things like headings and lists — this line removes all of that so you start from a clean, predictable base. box-sizing: border-box means padding and borders are included inside an element's width, not added on top of it.

body { font-family: Georgia, serif; font-size: 16px; line-height: 1.7; color: #222; background: #fff; }

Page-wide defaults

Sets the font (Georgia, a classic serif), base text size (16px), and line spacing (1.7× the text height — loose enough to read comfortably). #222 is near-black for text. #fff is white for the background. All other elements on the page inherit these unless you override them.

a { color: #2563eb; text-decoration: none; } a:hover { text-decoration: underline; }

Link styles

All links are blue with no underline by default. The underline appears only on hover. The :hover part is called a pseudo-class — a rule that applies only when something is in a certain state (in this case, when the mouse is over it).

h2 { font-size: 1rem; text-transform: uppercase; letter-spacing: .08em; color: #555; border-bottom: 1px solid #e5e5e5; }

Section headings

The About, News, Projects, and Education headings. They're kept the same size as body text (1rem) but stand out through uppercase letters and wider letter spacing. The thin grey line underneath each one acts as a divider. #555 is a mid-grey — softer than the black body text so headings don't shout.

.wrapper { display: flex; max-width: 960px; margin: 0 auto; }

The outer container

display: flex is the key instruction — it makes the sidebar and main content sit side by side instead of stacking on top of each other. max-width: 960px stops the layout getting too wide on large monitors. margin: 0 auto centres the whole page horizontally.

a shelf that holds two boxes side by side, centred in the room, with a maximum width.
.sidebar { width: 240px; position: sticky; top: 0; height: 100vh; }

Sticky sidebar

width: 240px locks the sidebar to a fixed width. position: sticky; top: 0 is what makes it stay in place as you scroll — it "sticks" to the top of the viewport. height: 100vh makes it exactly as tall as the screen (vh = viewport height, so 100vh = 100% of the screen height).

.avatar { width: 140px; height: 140px; border-radius: 50%; background: #dbeafe; }

The circular photo

Equal width and height make it a square. border-radius: 50% then rounds the corners all the way into a circle — 50% means "round by half the element's size", which always produces a perfect circle. #dbeafe is the light blue placeholder background, visible only if no image is set.

cutting a square photo into a circle with scissors.
.sidebar-links li::before { content: "→"; color: #aaa; }

The arrow before each link

::before is a CSS trick that inserts content automatically before an element — without you having to type it in the HTML. Here it inserts a grey arrow before each link in the sidebar. If you wanted a bullet, dot, or any other character instead, you'd just change the content value.

.main { flex: 1; padding: 0 40px 80px; }

The main content area

flex: 1 means "take up all the remaining width after the sidebar." So if the page is 960px wide and the sidebar is 240px, the main area automatically fills the remaining 720px. padding adds breathing room inside — none at the top, 40px on the sides, 80px at the bottom.

.tabs { display: flex; justify-content: center; position: sticky; top: 0; background: #fff; z-index: 10; }

The sticky tab bar

justify-content: center centres the tabs horizontally. position: sticky; top: 0 keeps the bar pinned to the top as you scroll. background: #fff gives it a solid white background — without this, content would scroll visibly through the transparent bar. z-index: 10 ensures the bar sits on top of everything else when content scrolls underneath it.

z-index is like the order of layers in a stack of papers — higher number = on top.
.tabs a { border-radius: 99px; transition: background 0.15s, color 0.15s; }

Pill-shaped tabs with smooth hover

border-radius: 99px is a deliberately large number — large enough to always produce fully rounded ends regardless of how wide the tab is, creating a pill shape. transition makes the background and text colour change animate smoothly over 0.15 seconds on hover, instead of snapping instantly.

.section { padding-top: 40px; scroll-margin-top: 60px; }

Section spacing and scroll behaviour

padding-top: 40px adds breathing room at the top of each section so content doesn't start right at the tab bar. scroll-margin-top: 60px is specifically for when you click a tab — without it, the browser scrolls the section heading right to the top of the viewport, hiding it behind the sticky tab bar. This adds a 60px offset so the heading lands just below the bar.

.news-date { white-space: nowrap; width: 90px; flex-shrink: 0; }

The date column in News

white-space: nowrap stops the date from ever wrapping onto two lines. width: 90px fixes the column at exactly that width. flex-shrink: 0 tells the flex layout never to make it narrower, even if space is tight — the date column always stays 90px wide and the text beside it adjusts.

.project-list li strong { display: block; }

Project titles on their own line

strong is normally an inline element — it flows within a line of text like a bold word in a sentence. display: block changes it to a block element, which forces it onto its own line, pushing the description text underneath it. This is a very common CSS pattern for turning inline elements into their own rows.

@media (max-width: 640px) { .wrapper { flex-direction: column; } ... }

Media query — the mobile layout

A media query is a conditional CSS rule: "only apply these styles when the screen is 640px wide or narrower." Inside it, the layout switches from side-by-side to stacked (flex-direction: column), the sidebar stretches to full width, and sticky positioning is turned off so everything scrolls normally. This is how one HTML file can look great on both a desktop and a phone.

like an if statement: "if the screen is small, use these rules instead."
The HTML structure — what visitors see
<aside class="sidebar"> ... </aside>

The sidebar element

aside is an HTML tag for content that sits beside the main content — a sidebar. The class="sidebar" connects this element to the .sidebar CSS rules you wrote above. Every HTML tag can have a class, and every CSS rule that starts with a dot targets elements with that class name.

<div class="avatar">🧑‍💻</div>

The avatar placeholder

div is a generic container — a box with no built-in meaning, used to group and style things. The emoji inside it is just a placeholder. When you're ready, you'll replace this whole line with an <img> tag pointing to your photo — see Step B in the workshop guide.

<nav class="tabs"> <a href="#about">About</a> ... </nav>

The tab navigation

nav is the HTML tag for a navigation menu. Each <a href="#about"> is an anchor link — the # means "scroll to the element on this page with id="about"." No new page loads, the browser just jumps to that section. This is the simplest kind of navigation you can build.

<div class="section" id="about">

Section with an ID

The id="about" is the anchor target — it's what the tab link href="#about" points to. IDs must be unique on a page (no two elements can share the same ID). The class="section" applies the shared section styles (padding, scroll margin etc.) to all four content sections.

<span class="news-date">Mar 2026</span>

Span — inline styling

span is like div but inline — it wraps a piece of text within a line without breaking onto a new line. Here it wraps the date so the .news-date CSS rules (grey colour, fixed width) apply to just the date, leaving the news text beside it unstyled.

highlighting one word in a sentence with a yellow marker — same line, different style.
<strong><a href="#">Project One Title</a></strong>

Nested tags

Tags can be nested inside each other. Here a link (a) sits inside a bold tag (strong), making the project title both bold and clickable. The href="#" is a placeholder — replace the # with the actual URL of your project when you have one.