Skip to main content Scroll Top
HTML & CSS Mastery — Upskeeling
📌 Bookmarks
No bookmarks yet.
Click 🔖 on any module to save it here.
HTML Fundamentals HTML Track
0 completed
Upskeeling · Interactive Learning
HTML & CSS — Zero to Advanced
Every concept explained clearly, with live playgrounds and built-in error guides. Pick a module from the sidebar and start learning.
18
Modules
18
Playgrounds
54
Error Guides
18
Quizzes
🌐
HTML · Module 01
What is HTML?
Getting Started · Beginner
Beginner
Concept
HTML stands for HyperText Markup Language. It is the backbone of every webpage — it tells the browser what content exists and what role each piece plays.

HTML is not a programming language — it has no logic, loops, or conditions. It is a markup language: you wrap content in tags to describe its purpose. Browsers read those tags and render them visually.
🏠 Analogy: HTML is the skeleton of a house — it is the bricks, walls, and structure. CSS is the paint and décor. JavaScript is the electricity and plumbing. Without the skeleton, nothing else can exist.
Key Points
Markup language, not a programming language
Uses <tags> to describe content purpose
Browsers read HTML and convert it to what you see
Every webpage ever made starts with an HTML file
HTML5 is the current standard — adds semantic elements & media
HTML is case-insensitive but lowercase is the standard practice
Syntax Reference
HTML in Action
<h1>Page Title</h1> <p>A paragraph of text.</p> <a href="https://upskeeling.com">Visit</a> <!-- comments are ignored by browser -->
  Live Playground
Try changing the text inside <h1>. Add a new <p> below. Watch the preview update live.
Code Editor Edit & Run
Live Preview Instant render
Click ▶ Run to see preview
⚠ Common Errors & Fixes
🐛 Missing closing tag
✅ How to fix it
Every opening tag needs a matching closing tag. <p> needs </p>. Notice the forward slash. Missing closing tags cause the browser to guess — and it guesses wrong.
🐛 Typo in tag name
✅ How to fix it
Tag names must be exact. <pg> is not valid — use <p>. The browser silently ignores unknown tags, so your content disappears with no error message.
🐛 Content outside all tags
✅ How to fix it
All visible content should be inside appropriate tags. Wrap bare text in <p> for paragraphs or <span> for inline content.
🎯 Quick Check
What does HTML stand for?
📄
HTML · Module 02
HTML Boilerplate
Getting Started · Beginner
Beginner
Concept
Every HTML document must follow a standard starting structure called the boilerplate. This skeleton tells the browser how to interpret the document — what language it is in, how to handle character encoding, and how to behave on mobile devices.

Without the boilerplate, pages may render incorrectly in certain browsers, display garbled characters, or appear zoomed-out on phones.
📝 Analogy: The boilerplate is like the letterhead on official documents — it identifies what type of document this is and who it is from, before you read a single word of content.
Key Points
<!DOCTYPE html> — declares HTML5 document type
<html lang="en"> — root element, sets document language
<head> — invisible metadata (title, styles, SEO)
<meta charset="UTF-8"> — supports all world languages
name="viewport" — makes page mobile responsive
<body> — all visible content goes here
Syntax Reference
Standard HTML5 Boilerplate
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>Page Title</title> </head> <body> <!-- Your content here --> </body> </html>
  Live Playground
Change the text inside <title> — notice it sets the browser tab title. Every real page needs a unique, descriptive title for SEO.
Code Editor Edit & Run
Live Preview Instant render
Click ▶ Run to see preview
⚠ Common Errors & Fixes
🐛 Missing DOCTYPE
✅ How to fix it
Always start with <!DOCTYPE html> on line 1. Without it the browser enters "quirks mode" — it emulates old broken browser behaviour and your CSS may render completely differently.
🐛 charset not declared
✅ How to fix it
Add <meta charset="UTF-8"> inside head. Without it, special characters like ₹, é, ü, or Arabic/Hindi text may display as garbage symbols (mojibake).
🐛 viewport meta missing
✅ How to fix it
Without <meta name="viewport">, mobile browsers zoom out to show the desktop layout at 980px width. Your responsive CSS will not work. Always include it.
🎯 Quick Check
Where does the page title (shown in browser tab) go?
🏷️
HTML · Module 03
Tags, Elements & Attributes
Getting Started · Beginner
Beginner
Concept
An HTML element = opening tag + content + closing tag. Attributes provide extra information about an element — they live inside the opening tag as name="value" pairs.

Some elements are void elements — they have no content and therefore no closing tag. Common void elements: <br>, <img>, <input>, <hr>, <meta>, <link>.
🎁 Analogy: A tag is like gift wrapping. The opening tag is the paper you start wrapping with. The content is the gift inside. The closing tag is how you finish sealing it.
Key Points
Opening tag: <p> — marks the start
Closing tag: </p> — note the forward slash
Void elements: <br> <img> <input> <hr> — no closing tag
Attributes are name="value" pairs in the opening tag
id — unique identifier   class — reusable group name
href — link destination   src — image/script source
Syntax Reference
Element & Attribute Anatomy
<a href="https://upskeeling.com" target="_blank">Visit</a> ↑ open ↑ attribute ↑ value content close ↑ Void element — no closing tag: <img src="logo.png" alt="Upskeeling logo">
  Live Playground
Try adding title="I am a tooltip" to the <p> tag, then hover over the paragraph in the preview.
Code Editor Edit & Run
Live Preview Instant render
Click ▶ Run to see preview
⚠ Common Errors & Fixes
🐛 Attribute value missing quotes
✅ How to fix it
Attribute values must be quoted: href="url" not href=url. Unquoted values break when there are spaces or special characters. Always use double quotes.
🐛 Trying to close a void element
✅ How to fix it
Void elements like <br>, <img>, <hr> have no closing tag. Write <br> not <br /></br>. HTML5 does not require the self-closing slash.
🐛 Attributes outside the tag
✅ How to fix it
All attributes must be inside the opening tag: <a href="url">. Writing <a><href="url"> creates a broken second element and the link will not work.
🎯 Quick Check
Which of these is a void element with no closing tag?
📰
HTML · Module 04
Headings h1–h6
Text & Content · Beginner
Beginner
Concept
HTML provides six levels of headings from <h1> (most important) to <h6> (least). Headings create a logical document outline — like chapters and sub-chapters in a book.

Never use headings just to make text bigger. That is CSS's job. Use them to represent genuine content hierarchy. Search engines and screen readers rely on this structure.
📚 Analogy: h1 = Book title. h2 = Chapter name. h3 = Section heading. h4 = Sub-section. Would you write a book that jumps from the title straight to a sub-section? No. Same rule applies in HTML.
Key Points
Use only one <h1> per page — the main topic
Never skip levels: h1 → h3 is wrong. Use h1 → h2 → h3
Headings directly affect SEO — search engines read them
Screen reader users navigate pages by jumping between headings
Use CSS font-size to change size, not a different heading level
Each level should describe a genuine sub-topic of the level above it
Syntax Reference
Heading Hierarchy
<h1>Page Title — one per page</h1> <h2>Major Section</h2> <h3>Sub-section</h3> <h4>Minor heading</h4>
  Live Playground
All six heading levels are rendered below. Notice the browser gives them different sizes — but you should choose the level based on meaning, not size. Use CSS to change sizes.
Code Editor Edit & Run
Live Preview Instant render
Click ▶ Run to see preview
⚠ Common Errors & Fixes
🐛 Skipping heading levels
✅ How to fix it
Going from <h1> to <h3> breaks the document outline. Use <h2> between them. Screen reader users will wonder where h2 went — it implies a missing section.
🐛 Multiple h1 tags on one page
✅ How to fix it
Each page should have exactly one <h1>. Multiple h1s confuse search engines about what the page is primarily about.
🐛 Using headings for visual size
✅ How to fix it
Do not use <h4> because you want small bold text. Use a <p> with CSS. Using the wrong heading level damages document structure and SEO.
🎯 Quick Check
What is the correct heading level order?
📝
HTML · Module 05
Paragraphs & Text Formatting
Text & Content · Beginner
Beginner
Concept
The <p> tag defines a paragraph of text. HTML has many inline text elements that carry semantic meaning — they tell assistive technology and search engines something beyond just visual appearance.

Always prefer semantic elements over visual-only ones: use <strong> not <b> when the text is genuinely important.
Key Points
<p> — paragraph block (auto margin top & bottom)
<br> — line break within a paragraph
<strong> — important text (bold + semantic weight)
<em> — stressed emphasis (italic + semantic)
<mark> — highlighted / relevant text
<del> / <ins> — deleted / inserted text
<sub> / <sup> — subscript / superscript
<pre> — preserves spaces and line breaks exactly
Syntax Reference
Inline Text Formatting
<p>Normal. <strong>Important.</strong> <em>Emphasis.</em></p> <p>Price: <del>₹5000</del> <ins>₹2500</ins></p> <p>H<sub>2</sub>O   E=mc<sup>2</sup></p>
  Live Playground
Try the <pre> element at the bottom — it preserves every space and newline exactly as typed. Very useful for code samples and aligned data.
Code Editor Edit & Run
Live Preview Instant render
Click ▶ Run to see preview
⚠ Common Errors & Fixes
🐛 Using <b> instead of <strong>
✅ How to fix it
<strong> conveys importance — screen readers give it extra emphasis. <b> is purely visual with no semantic weight. Use <strong> for genuinely important text.
🐛 Using <br><br> between paragraphs
✅ How to fix it
Do not use two <br> tags to create space between paragraphs. Use separate <p> tags and control the gap with CSS margin. Double br is a sign of layout confusion.
🐛 Using <i> instead of <em>
✅ How to fix it
<em> signals stressed emphasis — screen readers change their tone. <i> is visual italic only. Use <em> when the stress changes meaning, <i> for book titles or technical terms.
🎯 Quick Check
Which element preserves spaces and line breaks exactly as typed?
🔗
HTML · Module 06
Links & Anchors
Text & Content · Beginner
Beginner
Concept
The <a> (anchor) tag creates hyperlinks. The href attribute holds the destination. Links are the foundation of the web — they connect pages, sections, email clients, and phone diallers.

Links can point to external pages, same-page sections using ID anchors, email addresses, or phone numbers.
Key Points
href="https://..." — absolute URL to external page
href="#section-id" — jump to element with that id
href="page.html" — relative link to another file
target="_blank" — opens in new tab
Always add rel="noopener noreferrer" with _blank
href="mailto:x@y.com" — opens email client
href="tel:+911234567890" — phone dial on mobile
download attribute — forces file download
Syntax Reference
Link Variants
<!-- External link (secure) --> <a href="https://upskeeling.com" target="_blank" rel="noopener noreferrer">Visit</a> <!-- Same-page anchor --> <a href="#contact">Contact Section</a> <!-- Email & phone --> <a href="mailto:hello@upskeeling.com">Email Us</a>
  Live Playground
Click "Jump to Bottom" — notice the page jumps to the element with id="bottom". This same-page anchor technique is used for navigation menus on long pages.
Code Editor Edit & Run
Live Preview Instant render
Click ▶ Run to see preview
⚠ Common Errors & Fixes
🐛 target="_blank" without rel="noopener"
✅ How to fix it
Always add rel="noopener noreferrer" to target="_blank" links. Without it, the new tab can access your page via window.opener — a security vulnerability that enables phishing attacks.
🐛 href missing on anchor tag
✅ How to fix it
An <a> without href renders as plain text — it is not a link at all. Always provide a destination. Use href="#" for placeholder links during development.
🐛 Absolute path for internal pages
✅ How to fix it
For pages on the same website, use relative paths: href="about.html" not href="https://mysite.com/about.html". Relative links work on any domain — they break if you move the site.
🎯 Quick Check
How do you open a link in a new browser tab?
🖼️
HTML · Module 07
Images
Text & Content · Beginner
Beginner
Concept
The <img> tag embeds images. It is a void element — no closing tag. The alt attribute is mandatory for accessibility and shows as fallback text when an image fails to load.

Use width and height attributes to prevent layout shift (CLS) as the page loads — the browser reserves the space before the image arrives.
Key Points
src — path or URL to the image file (required)
alt — describes what the image shows (required for accessibility)
width / height — prevents layout shift while loading
loading="lazy" — defers off-screen images (performance)
Formats: WebP (best, 30% smaller than JPEG), PNG, JPEG, SVG
Decorative images: alt="" (empty) — screen readers skip them
Syntax Reference
Image Element
<img src="photo.webp" alt="Students at Upskeeling" width="400" height="300"> <!-- Lazy loaded: defers until near viewport --> <img src="hero.webp" alt="Hero image" loading="lazy"> <!-- Decorative: empty alt --> <img src="divider.svg" alt="">
  Live Playground
Delete the src value of the first image and run it — you will see the alt text appear instead. That is exactly what visually impaired users always experience.
Code Editor Edit & Run
Live Preview Instant render
Click ▶ Run to see preview
⚠ Common Errors & Fixes
🐛 Missing alt attribute
✅ How to fix it
Always include alt. For informative images describe what you see: alt="Team photo at Upskeeling office". For decorative images use empty: alt="". Missing alt fails WCAG accessibility standards.
🐛 No width and height set
✅ How to fix it
Without explicit dimensions, the browser does not know how much space to reserve. When the image loads, the page layout shifts — a poor UX that also hurts your Google Core Web Vitals score (CLS).
🐛 Wrong image path
✅ How to fix it
Paths are case-sensitive on Linux web servers. src="Images/Photo.jpg" fails if the actual file is images/photo.jpg. Always check case and folder structure carefully.
🎯 Quick Check
What is the purpose of the alt attribute on an image?
📋
HTML · Module 08
Lists — ul, ol, dl
Text & Content · Beginner
Beginner
Concept
HTML has three list types. Choose based on the semantic meaning of the content, not visual appearance.

Unordered for items with no particular sequence. Ordered when the order matters. Description lists for term–definition pairs like glossaries.
Key Points
<ul> — unordered list (bullets)
<ol> — ordered list (numbered)
<li> — list item inside ul or ol
<dl>, <dt>, <dd> — definition list
type="A" on ol — uppercase letters   type="i" — Roman numerals
start="5" — begin numbering from a different number
Lists can be nested: put a full <ul> inside an <li>
Syntax Reference
All Three List Types
<ul> <li>HTML</li> <li>CSS</li> </ul> <ol type="1" start="1"> <li>Step one</li> </ol> <dl> <dt>HTML</dt> <dd>HyperText Markup Language</dd> </dl>
  Live Playground
The nested list inside the CSS <li> creates a sub-list. You can nest to any depth. Try adding another level inside Flexbox.
Code Editor Edit & Run
Live Preview Instant render
Click ▶ Run to see preview
⚠ Common Errors & Fixes
🐛 <li> placed outside <ul> or <ol>
✅ How to fix it
<li> must be a direct child of <ul> or <ol>. Placing it outside is invalid HTML — browsers may render it but assistive technology will not announce it as a list item.
🐛 Using <ul> for ordered content
✅ How to fix it
If order matters (steps, rankings, a recipe), use <ol>. Screen readers announce "list of 4 items" vs "numbered list of 4 items" — the distinction communicates important context to users.
🐛 Nesting list directly inside list
✅ How to fix it
Nested lists must go inside an <li>: <li>Item <ul>...</ul></li>. Placing <ul> directly inside <ul> without an <li> wrapper is invalid HTML.
🎯 Quick Check
Which list type should you use for step-by-step instructions?
📊
HTML · Module 09
Tables
Data & Structure · Beginner
Beginner
Concept
Tables display tabular data — information that naturally belongs in rows and columns. They should never be used for page layout (that was a 1990s hack — CSS replaced it).

Semantic table structure using <thead>, <tbody>, and <tfoot> improves accessibility and lets you style sections independently with CSS.
📋 Analogy: A table is a spreadsheet embedded in a page. Every cell belongs to a row, and rows belong to sections (header, body, footer). Think Excel with semantic HTML wrapped around it.
Key Points
<table> — outer wrapper for everything
<thead>, <tbody>, <tfoot> — semantic sections
<tr> — table row (goes inside thead/tbody/tfoot)
<th> — header cell, bold+centered by default
<td> — data cell
colspan — merge cells horizontally
rowspan — merge cells vertically
<caption> — table title shown above
Syntax Reference
Full Semantic Table Structure
<table> <caption>Q1 Results</caption> <thead><tr> <th scope="col">Name</th> <th scope="col">Score</th> </tr></thead> <tbody><tr> <td>Priya</td><td>95</td> </tr></tbody> </table>
  Live Playground
See how colspan="2" makes the "Class Average" cell span two columns? Try changing it to colspan="3" and see what happens to the layout.
Code Editor Edit & Run
Live Preview Instant render
Click ▶ Run to see preview
⚠ Common Errors & Fixes
🐛 <td> directly inside <table>
✅ How to fix it
<td> must go inside <tr>, which goes inside <table>. The required nesting is: table → tr → td. Skipping the tr breaks rendering in all browsers.
🐛 Using tables for page layout
✅ How to fix it
Tables are for data only. Using them for layout breaks on mobile, is inaccessible to screen readers, and creates bloated HTML. Use CSS Flexbox or Grid for all layout needs.
🐛 Missing scope on th
✅ How to fix it
Add scope="col" or scope="row" to every <th>. Screen readers use scope to correctly associate header cells with their data cells.
🎯 Quick Check
What does colspan="3" on a <td> do?
📩
HTML · Module 10
Forms
Forms & Inputs · Beginner
Beginner
Concept
Forms collect user input and send it to a server or JavaScript handler. The <form> element wraps all inputs. Every input must have a <label> linked via the for/id pair — this is critical for accessibility and clicking the label focuses the input.

HTML5 added built-in client-side validation — required, type="email", minlength, pattern — without any JavaScript.
Key Points
action — URL where data is sent on submit
method="GET" — data appended to URL (visible)
method="POST" — data in request body (secure)
<label for="id"> must match input id
required — field must not be empty
type="email" — validates email format automatically
minlength maxlength — character count limits
pattern="regex" — custom validation pattern
Syntax Reference
Form Structure
<form action="/submit" method="POST"> <label for="email">Email</label> <input type="email" id="email" name="email" required> <button type="submit">Submit</button> </form>
  Live Playground
Click "Enroll Now" without filling any fields — the browser validates automatically with no JavaScript needed. That is HTML5 built-in validation.
Code Editor Edit & Run
Live Preview Instant render
Click ▶ Run to see preview
⚠ Common Errors & Fixes
🐛 label for does not match input id
✅ How to fix it
The for on <label> must exactly match the id on the input. A mismatch means clicking the label does not focus the input — bad UX and fails accessibility requirements.
🐛 Input missing name attribute
✅ How to fix it
The name attribute is required for form submission. Without it, the input's value is not included in the submitted data. The server will never receive it.
🐛 Using GET for sensitive data
✅ How to fix it
Never use method="GET" for passwords or personal data — values appear in the URL and browser history. Always use POST for login forms, payment forms, and any sensitive data.
🎯 Quick Check
Why is the <label> element important in forms?
🎨
CSS · Module 01
What is CSS?
Foundations · Beginner
Beginner
Concept
CSS (Cascading Style Sheets) controls every visual aspect of an HTML page — colours, fonts, spacing, layout, and animations. The word cascading means styles flow from parent to child, and when rules conflict, specificity determines the winner.

CSS completely separates design from content. You can restyle an entire website by editing a single CSS file without touching HTML.
👔 Analogy: HTML is the mannequin. CSS is the entire wardrobe — every outfit, colour, and accessory. One CSS file can dress every page of a website at once.
Key Points
CSS = rules that select elements and apply styles
Three ways: inline style attr, <style> tag, external .css file
External CSS file is always best for real projects
A rule = selector { property: value; }
Multiple properties separated by semicolons inside braces
Comments: /* this is a CSS comment */
Syntax Reference
CSS Rule Anatomy
/* Element selector */ h1 { color: #d97706; font-size: 28px; font-weight: 700; } /* Class selector */ .card { background: #f9fafb; border-radius: 12px; }
  Live Playground
Try changing color: #d97706 in the h2 rule to color: #3b82f6. Notice CSS controls everything visually without changing the HTML at all.
Code Editor Edit & Run
Live Preview Instant render
Click ▶ Run to see preview
⚠ Common Errors & Fixes
🐛Missing semicolon after value
✅ Fix
Every declaration ends with a semicolon: color: red; not color: red. A missing semicolon silently breaks every property that follows it in the same rule.
🐛Curly brace not closed
✅ Fix
Every rule must have opening and closing braces { }. One unclosed brace breaks every CSS rule that follows it in the entire file.
🐛CSS file not loading
✅ Fix
Check the path in <link rel="stylesheet" href="style.css">. The path is relative to the HTML file. Open DevTools → Network tab to confirm the file loaded successfully.
🎯 Quick Check
What does Cascading mean in CSS?
🎯
CSS · Module 02
Selectors
Foundations · Beginner
Beginner
Concept
Selectors are patterns that target HTML elements to style. Choosing the right selector makes CSS efficient and predictable. The wrong selector accidentally styles unintended elements or creates specificity wars.

CSS has selectors for element types, classes, IDs, attributes, relationships, and states.
Key Points
* — universal, every element
p — element type selector
.class — class (reusable, any element)
#id — ID (unique per page, highest specificity)
p, h1 — group (comma = OR, styles both)
.parent .child — descendant (any depth)
.parent > .child — direct child only
input[type="email"] — attribute selector
Syntax Reference
Selector Types
* { box-sizing: border-box; } p { color: #374151; } .card { background: #f9fafb; } #hero { font-size: 2rem; } .card p { font-size: 13px; } /* descendant */
  Live Playground
Notice how the p inside .card has different styling than the p outside it — that is the descendant selector .card p targeting only paragraphs nested inside .card.
Code Editor Edit & Run
Live Preview Instant render
Click ▶ Run to see preview
⚠ Common Errors & Fixes
🐛ID used more than once on page
✅ Fix
IDs must be unique per page. id="hero" can only appear once. For reusable styles use classes instead. Multiple identical IDs break JavaScript selectors and are invalid HTML.
🐛Class name starting with number
✅ Fix
CSS class names cannot start with a digit. .2col is invalid. Use .two-col or .col-2 instead.
🐛Descendant selector too broad
✅ Fix
.nav a selects every link anywhere inside .nav at any depth. If you only want direct children use .nav > a. Overly broad selectors create hard-to-debug style leaks.
🎯 Quick Check
Difference between .box and #box in CSS?
📦
CSS · Module 03
The Box Model
Foundations · Beginner
Beginner
Concept
Every HTML element on screen is a rectangular box. The CSS Box Model describes four layers around that box from inside out: Content → Padding → Border → Margin.

Misunderstanding this is the root cause of most CSS sizing bugs — elements appearing wider than expected, or spacing that does not add up.
📦 Analogy: Imagine a framed photo. The photo itself = content. The white matting = padding (background shows here). The frame = border. The gap between frames on the wall = margin (always transparent).
Key Points
Content — actual text, image, or child elements
Padding — space inside the border (background colour fills this)
Border — the outline: has width, style, and colour
Margin — space outside the border (always transparent)
box-sizing: border-box — width includes padding + border
box-sizing: content-box — default: padding is added on top of width
Always add * { box-sizing: border-box; } at the top of every CSS file
Syntax Reference
Box Model Properties
.box { width: 200px; padding: 20px; border: 3px solid #d97706; margin: 16px; box-sizing: border-box; }
  Live Playground
Both boxes have width: 220px. The blue one uses content-box so its actual rendered width is 266px because padding is added on top. Always use border-box.
Code Editor Edit & Run
Live Preview Instant render
Click ▶ Run to see preview
⚠ Common Errors & Fixes
🐛Box wider than expected
✅ Fix
You forgot box-sizing: border-box. In content-box mode padding and border add to the declared width. Fix: add * { box-sizing: border-box; } at the top of your CSS.
🐛Margin vs padding confused
✅ Fix
Padding is inside the border — background colour fills it. Margin is outside — always transparent. Use padding to space content from its border, margin to push elements apart.
🐛Vertical margin collapse
✅ Fix
Two adjacent vertical margins do not add up — they collapse into the larger one. Two 20px margins = 20px gap, not 40px. This is intentional CSS behaviour, not a bug.
🎯 Quick Check
With box-sizing:border-box, width:200px and padding:20px renders at what total width?
🖍️
CSS · Module 04
Colors & Typography
Visual Styling · Beginner
Beginner
Concept
Colour and typography are the two areas that most dramatically affect how a design looks and feels. CSS provides multiple colour formats and a rich set of font properties.

Good typography — the arrangement of type — makes content measurably easier to read. A well-set page feels professional without the user knowing exactly why.
Key Points
#d97706 — hex (RRGGBB), most common
rgb(217,119,6) — Red Green Blue (0-255)
rgba(217,119,6,0.5) — with alpha (0 transparent, 1 opaque)
hsl(38,88%,44%) — Hue Saturation Lightness (easiest to adjust)
font-family — always include a generic fallback
font-weight — 100 thin to 900 black; 400=normal 700=bold
line-height: 1.6 — 1.5 to 1.8 is ideal for body text
letter-spacing — negative values tighten headings
Syntax Reference
Colour Formats and Font Properties
body { color: #374151; font-family: Inter, sans-serif; font-size: 16px; line-height: 1.6; } h1 { font-size: clamp(24px, 4vw, 48px); font-weight: 800; letter-spacing: -0.5px; }
  Live Playground
Try changing line-height: 1.8 on the paragraph to 1.0 and run — notice how much harder the compressed text is to read. Line height is one of the most impactful readability properties.
Code Editor Edit & Run
Live Preview Instant render
Click ▶ Run to see preview
⚠ Common Errors & Fixes
🐛Hex wrong digit count
✅ Fix
Hex colours need exactly 3 or 6 hex digits: #fff or #ffffff. #ffff is invalid and produces no colour output.
🐛Font-family with no fallback
✅ Fix
Always include a generic fallback: font-family: Inter, sans-serif. Without it, if the first font is unavailable the browser defaults to Times New Roman — which is rarely what you want.
🐛Low colour contrast
✅ Fix
Text needs a 4.5:1 contrast ratio against its background for WCAG AA compliance. Light grey on white fails this requirement. Use the WebAIM Contrast Checker tool before publishing.
🎯 Quick Check
Which colour format makes it easiest to adjust lightness programmatically?
CSS · Module 05
Flexbox
Layout · Intermediate
Intermediate
Concept
Flexbox is a one-dimensional layout model that arranges items in a row or column. It solved the web's hardest layout problems: vertical centering, equal-height columns, space distribution.

The parent element becomes a flex container with display: flex. Its direct children become flex items. Container properties control the group; item properties control individuals.
🧲 Analogy: Flexbox is a smart bookshelf. You tell it how to spread books out (justify-content), how to align them vertically (align-items), and whether to start a new row when the shelf is full (flex-wrap).
Key Points
display: flex — activates flexbox on the container
flex-direction: row | column — main axis direction
justify-content — aligns on the main axis
align-items — aligns on the cross axis
gap — spacing between items (no margin hacks needed)
flex-wrap: wrap — items wrap to new line when full
flex: 1 on a child — grows to fill available space
align-self — override alignment for one specific item
Syntax Reference
Flexbox Container and Items
.nav { display: flex; justify-content: space-between; align-items: center; gap: 16px; } .grow { flex: 1; }
  Live Playground
Try removing flex-wrap: wrap from .cards and narrow the preview — cards overflow instead of wrapping. That is why flex-wrap matters on responsive layouts.
Code Editor Edit & Run
Live Preview Instant render
Click ▶ Run to see preview
⚠ Common Errors & Fixes
🐛Flexbox properties on items instead of container
✅ Fix
Properties like justify-content, align-items, gap, and flex-direction go on the parent with display:flex — not on the children.
🐛justify-content and align-items confused
✅ Fix
justify-content works on the main axis (horizontal in a row). align-items works on the cross axis (vertical in a row). They swap meaning completely when flex-direction: column.
🐛Items not equal width
✅ Fix
Add flex: 1 to each item that should share space equally. Without it, items size to their content — some wider, some narrower.
🎯 Quick Check
Which property centres items vertically in a flex row container?
🔲
CSS · Module 06
CSS Grid
Layout · Intermediate
Intermediate
Concept
CSS Grid is a two-dimensional layout system — you control both rows AND columns simultaneously. It is the most powerful layout tool in modern CSS and perfect for full page layouts, dashboards, and responsive card grids.

Flexbox controls one axis at a time. Grid controls both axes together, letting you place elements precisely on a two-dimensional surface.
🗺️ Analogy: Grid is a city planner's map. You draw the streets (column and row tracks) first, then assign each building (element) to its specific block on the map.
Key Points
display: grid — activates grid on the container
grid-template-columns: repeat(3, 1fr) — 3 equal columns
gap — space between rows and columns
grid-column: 1 / 3 — span from line 1 to line 3
grid-column: 1 / -1 — span full width
grid-template-areas — named layout regions
auto-fit + minmax() — responsive without media queries
fr — fractional unit of available space
Syntax Reference
Grid Layout Patterns
.grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px; } .page { grid-template-areas: "header header" "nav main" "footer footer"; }
  Live Playground
The auto-grid uses repeat(auto-fit, minmax(90px,1fr)) — it creates as many columns as fit. Resize the preview to watch it respond without any media queries.
Code Editor Edit & Run
Live Preview Instant render
Click ▶ Run to see preview
⚠ Common Errors & Fixes
🐛grid-area name not matching template
✅ Fix
Every name in grid-area on a child must exactly match a name in the parent's grid-template-areas. Typos cause items to fall out of the named layout into auto-placement.
🐛fr unit not distributing space
✅ Fix
fr distributes remaining free space. If the container has no free space (fixed content fills it), fr cannot work. Ensure the container has room to distribute.
🐛Items overlapping
✅ Fix
Explicit grid placement with overlapping line numbers will stack items. Adjust the line numbers to avoid overlap, or use z-index to control which item appears on top.
🎯 Quick Check
What does grid-column: 1 / -1 do?
CSS · Module 07
Transitions & Animations
Motion & Effects · Intermediate
Intermediate
Concept
CSS transitions animate property changes between states. CSS animations use @keyframes to define multi-step sequences that run independently.

Use motion deliberately — too much animation causes distraction. Always respect prefers-reduced-motion media query for users with vestibular disorders.
🎬 Analogy: A transition is a light dimmer — it smoothly moves between states when triggered. An animation is a film reel — it plays through a defined sequence on its own timeline.
Key Points
transition: property duration easing
Triggered by state changes: :hover, :focus, JS class changes
@keyframes name { from {} to {} } or use percentages
animation: name duration timing iteration
Animate only transform and opacity for best performance
ease-in-out — starts and ends slowly, most natural feel
cubic-bezier() — create spring or bounce easing
animation-fill-mode: forwards — stays at end state after finishing
Syntax Reference
Transition and Keyframe
.btn { transition: all 0.3s ease; } .btn:hover { transform: translateY(-4px); box-shadow: 0 8px 24px rgba(0,0,0,.2); } @keyframes fadeUp { from { opacity:0; transform:translateY(20px);} to { opacity:1; transform:translateY(0);} }
  Live Playground
The button uses cubic-bezier(.34,1.56,.64,1) — a spring easing. Change it to ease and compare. The spring slightly overshoots before settling.
Code Editor Edit & Run
Live Preview Instant render
Click ▶ Run to see preview
⚠ Common Errors & Fixes
🐛Animation not running
✅ Fix
Check: 1) @keyframes name exactly matches animation name, 2) animation-duration is set (defaults to 0s — invisible!), 3) no typos inside the @keyframes block.
🐛Transition on display property
✅ Fix
You cannot transition the display property. Use opacity + visibility instead, or max-height for show/hide effects. Add pointer-events:none to make invisible elements non-clickable.
🐛Janky stuttering animation
✅ Fix
Only animate transform and opacity — they are GPU-composited and cost almost nothing. Animating width, height, top, or left forces full layout recalculation on every frame causing frame drops.
🎯 Quick Check
Why should you only animate transform and opacity for performance?
📱
CSS · Module 08
Responsive Design
Responsive · Intermediate
Intermediate
Concept
Responsive design makes webpages work beautifully on any screen — from a 320px phone to a 4K monitor. CSS @media rules apply styles only when conditions are met.

Mobile-first means writing base styles for small screens, then using min-width media queries to enhance for larger screens. This produces leaner CSS and better mobile performance.
📺 Analogy: Responsive CSS is like water — it fills whatever container it is poured into. The same content adapts its shape to phone, tablet, or widescreen without creating separate versions.
Key Points
@media (max-width: 768px) — at or below 768px
@media (min-width: 768px) — mobile-first approach
Common breakpoints: 480, 768, 1024, 1280px
@media (prefers-color-scheme: dark) — dark mode
clamp(min, ideal, max) — fluid size with no media queries
auto-fit + minmax() — responsive grid automatically
Viewport meta tag is required for media queries to work on mobile
Always test on real devices — browser simulators are approximate
Syntax Reference
Media Query Patterns
/* Mobile base */ .grid { grid-template-columns: 1fr; } @media (min-width: 768px) { .grid { grid-template-columns: repeat(2,1fr); } } @media (min-width: 1024px) { .grid { grid-template-columns: repeat(3,1fr); } }
  Live Playground
Resize the preview panel narrower — the 3-column grid collapses to 2, then 1 column. The heading uses clamp() so its size is always fluid without a single media query.
Code Editor Edit & Run
Live Preview Instant render
Click ▶ Run to see preview
⚠ Common Errors & Fixes
🐛Viewport meta tag missing
✅ Fix
Without <meta name="viewport" content="width=device-width,initial-scale=1">, mobile browsers zoom out to 980px and your media queries never trigger.
🐛Max-width and min-width conflicting
✅ Fix
Mixing max-width and min-width queries creates overlapping ranges that fight each other. Choose one approach: mobile-first (min-width only) or desktop-first (max-width only).
🐛Testing only in browser DevTools
✅ Fix
DevTools device emulation is approximate. Always verify on real physical devices — scroll behaviour, touch events, and font rendering can differ significantly.
🎯 Quick Check
What does mobile-first responsive design mean?
'; // Find preview container using data-pgid var container = null; var allPF = document.querySelectorAll('.preview-f'); for (var _ci = 0; _ci < allPF.length; _ci++) { if (allPF[_ci].getAttribute('data-pgid') === id) { container = allPF[_ci]; break; } } if (!container) { var pgWrap = ed.closest('.pg-wrap'); if (pgWrap) container = pgWrap.querySelector('.preview-f'); } if (!container) return; var fr = container.querySelector('iframe'); if (!fr) { container.innerHTML = ''; fr = document.createElement('iframe'); fr.setAttribute('sandbox', 'allow-scripts allow-same-origin allow-forms allow-popups allow-modals'); fr.style.width = '100%'; fr.style.minHeight = '280px'; fr.style.border = 'none'; fr.style.display = 'block'; fr.style.background = '#fff'; container.appendChild(fr); } var edH = Math.max(ed.offsetHeight || 280, 280); fr.style.minHeight = edH + 'px'; fr.srcdoc = html; if (fb) { fb.className = 'fb-bar show ok'; fb.textContent = '\u2713 Rendered'; setTimeout(function(){ fb.className = 'fb-bar'; }, 2000); } markDone(id); } function resetPg(id) { var ed = document.getElementById('ed-' + id); var fb = document.getElementById('fb-' + id); if (ed && origCodes[id]) { ed.value = origCodes[id]; runPg(id); } if (fb) fb.className = 'fb-bar'; } function toggleHint(id) { var h = document.getElementById('hint-' + id); if (h) h.classList.toggle('show'); } // Auto-run all visible playgrounds function autoRunAll() { var active = document.querySelector('.track-section.active'); if (!active) return; // Stagger runs so browser doesn't get overwhelmed var eds = active.querySelectorAll('.code-ed'); eds.forEach(function(ed, i) { var id = ed.id.replace('ed-', ''); origCodes[id] = ed.value; setTimeout(function(){ runPg(id); }, i * 60); }); } // ── TRACK SWITCH ───────────────────────────────────────── function switchTrack(t) { document.querySelectorAll('.track-section').forEach(function(s){ s.classList.remove('active'); }); var sec = document.getElementById('track-' + t); if (sec) sec.classList.add('active'); document.getElementById('btnHTML').classList.toggle('on', t === 'html'); document.getElementById('btnCSS').classList.toggle('on', t === 'css'); document.getElementById('navHTML').style.display = t === 'html' ? '' : 'none'; document.getElementById('navCSS').style.display = t === 'css' ? '' : 'none'; document.getElementById('topTitle').textContent = t === 'html' ? 'HTML Fundamentals' : 'CSS Mastery'; document.getElementById('topTrack').textContent = t === 'html' ? 'HTML Track' : 'CSS Track'; window.scrollTo({ top: 0, behavior: 'smooth' }); updateProgress(t); if (window.innerWidth < 769) closeSB(); // Run new track playgrounds setTimeout(function() { var allEds = document.querySelectorAll('#track-' + t + ' .code-ed'); allEds.forEach(function(ed, i) { var id = ed.id.replace('ed-', ''); if (!origCodes[id]) origCodes[id] = ed.value; setTimeout(function(){ runPg(id); }, i * 80); }); }, 150); } // ── ERROR TOGGLES ───────────────────────────────────────── // Handled inline via onclick on .err-toggle-head // ── QUIZ ────────────────────────────────────────────────── function doQuiz(btn, mid, chosen, correct) { var container = document.getElementById('qz-' + mid); var result = document.getElementById('qr-' + mid); if (!container || !result) return; container.querySelectorAll('.quiz-opt').forEach(function(b, i) { b.style.pointerEvents = 'none'; if (i === correct) b.classList.add('correct'); else if (i === chosen) b.classList.add('wrong'); }); var exp = result.getAttribute('data-exp') || ''; if (chosen === correct) { result.className = 'quiz-result show pass'; result.textContent = '🎉 Correct! ' + exp; } else { result.className = 'quiz-result show fail'; result.textContent = '❌ Not quite. ' + exp; } } // ── BOOKMARKS ───────────────────────────────────────────── function toggleBm(id, title) { var idx = bookmarks.indexOf(id); if (idx > -1) bookmarks.splice(idx, 1); else bookmarks.push(id); localStorage.setItem('ups_bm', JSON.stringify(bookmarks)); var btn = document.getElementById('bm-' + id); if (btn) btn.classList.toggle('on', bookmarks.includes(id)); document.getElementById('bmBadge').textContent = bookmarks.length; renderBmList(); } function renderBmList() { var list = document.getElementById('bmList'); if (!list) return; if (!bookmarks.length) { list.innerHTML = '
No bookmarks yet.
Click 🔖 on any module to save it here.
'; return; } var html = ''; bookmarks.forEach(function(id) { var mod = document.getElementById('mod-' + id); if (!mod) return; var title = mod.querySelector('.mcard-title') ? mod.querySelector('.mcard-title').textContent : id; var sub = mod.querySelector('.mcard-sub') ? mod.querySelector('.mcard-sub').textContent : ''; html += '
'; html += '
' + title + '
'; html += '
' + sub + '
'; html += '
'; }); list.innerHTML = html; } function jumpTo(id) { var el = document.getElementById('mod-' + id); if (el) { el.scrollIntoView({ behavior: 'smooth', block: 'start' }); } closeBmPanel(); } function openBmPanel() { document.getElementById('bmPanel').classList.add('open'); document.body.classList.add('no-scroll'); renderBmList(); } function closeBmPanel() { document.getElementById('bmPanel').classList.remove('open'); document.body.classList.remove('no-scroll'); } // ── PROGRESS ────────────────────────────────────────────── function markDone(id) { if (!completed.includes(id)) { completed.push(id); localStorage.setItem('ups_done', JSON.stringify(completed)); updateProgress(null); document.getElementById('cCount').textContent = completed.length; } } function updateProgress(t) { var track = t || (document.getElementById('track-html').classList.contains('active') ? 'html' : 'css'); var mods = document.querySelectorAll('#track-' + track + ' .mcard'); var total = mods.length; var done = 0; mods.forEach(function(m) { if (completed.includes(m.id.replace('mod-',''))) done++; }); var pct = total ? Math.round((done/total)*100) : 0; document.getElementById('spFill').style.width = pct + '%'; document.getElementById('spTxt').textContent = done + ' of ' + total + ' completed'; } // ── SIDEBAR SEARCH ──────────────────────────────────────── function filterNav(q) { q = q.toLowerCase(); document.querySelectorAll('.nav-item').forEach(function(item) { var txt = item.textContent.toLowerCase(); item.style.display = (!q || txt.includes(q)) ? '' : 'none'; }); } // ── SIDEBAR TOGGLE ──────────────────────────────────────── function toggleSB() { var sb = document.getElementById('sidebar'); var isOpen = sb.classList.toggle('open'); document.getElementById('hbg').classList.toggle('open', isOpen); document.getElementById('sbOverlay').classList.toggle('show', isOpen); document.body.classList.toggle('no-scroll', isOpen); } function closeSB() { document.getElementById('sidebar').classList.remove('open'); document.getElementById('hbg').classList.remove('open'); document.getElementById('sbOverlay').classList.remove('show'); document.body.classList.remove('no-scroll'); } // Nav item active highlight on scroll var observer = new IntersectionObserver(function(entries) { entries.forEach(function(e) { if (e.isIntersecting) { var id = e.target.id.replace('mod-',''); document.querySelectorAll('.nav-item').forEach(function(n){ n.classList.remove('active'); }); var ni = document.querySelector('.nav-item[href="#mod-' + id + '"]'); if (ni) { ni.classList.add('active'); ni.scrollIntoView({ block: 'nearest' }); } } }); }, { rootMargin: '-15% 0px -70% 0px' }); // ── SCROLL TOP ──────────────────────────────────────────── window.addEventListener('scroll', function() { document.getElementById('scrollTop').classList.toggle('show', window.scrollY > 400); }); // ── KEYBOARD SHORTCUTS ──────────────────────────────────── document.addEventListener('keydown', function(e) { if ((e.ctrlKey || e.metaKey) && e.key === 'k') { e.preventDefault(); document.getElementById('sbSearch').focus(); } if (e.key === 'Escape') { closeBmPanel(); closeSB(); } }); // ── INIT ────────────────────────────────────────────────── window.addEventListener('DOMContentLoaded', function() { // restore bookmark button states bookmarks.forEach(function(id) { var btn = document.getElementById('bm-' + id); if (btn) btn.classList.add('on'); }); document.getElementById('bmBadge').textContent = bookmarks.length; document.getElementById('cCount').textContent = completed.length; // run HTML track on load updateProgress('html'); document.querySelectorAll('.mcard').forEach(function(el) { observer.observe(el); }); // Run playgrounds in batches for performance var allEds = document.querySelectorAll('#track-html .code-ed'); allEds.forEach(function(ed, i) { var id = ed.id.replace('ed-', ''); origCodes[id] = ed.value; setTimeout(function(){ runPg(id); }, 80 + i * 80); }); });