Week 2 · HTML and CSS: building and styling pages
The one-minute story
A web page is a house. HTML is the bricks and rooms: it says "this is a heading, this is a list, this is a door (link), this is a picture". CSS is the paint, furniture and layout: "make the walls blue, put the sofa on the left". Later, JavaScript is the electricity: lights that switch on when you press a button.
Keep the jobs apart. HTML says what something is. CSS says how it looks. If you paint with bricks (styling inside the HTML) you will suffer when you want to repaint the whole house.
Bootstrap is a ready-made furnished interior: you pick "blue button" and "three equal columns" from a catalogue instead of painting every wall yourself.
By the end of this week you can:
- write a complete HTML page with headings, lists, links, images, tables and forms;
- tell block elements from inline elements and use semantic tags;
- write CSS with the right selector and predict which rule wins (specificity, order);
- calculate the size of a box (content, padding, border, margin);
- build a simple layout with
display: flex; - use Bootstrap through its CDN link, with grid and utility classes.
Words you need
- HTML - HyperText Markup Language: tags that describe the structure of a page.
- Element - an opening tag, content and a closing tag, such as
<p>Hello</p>. - Attribute - extra information inside the opening tag, such as
href="a.html". - CSS - Cascading Style Sheets: rules that style elements.
- Selector - the part of a CSS rule that picks which elements to style.
- Declaration - one
property: value;pair. - Specificity - the score that decides which of two clashing rules wins.
- Box model - every element is a box: content, padding, border, margin.
- Block / inline - elements that start on a new line and take the full width / elements that sit inside a line of text.
- Semantic tag - a tag whose name says its meaning (
nav,footer) rather than just its looks. - Pseudo-class / pseudo-element - a selector for a state (
:hover) / for a part of an element (::before). - Flexbox - a CSS layout mode that arranges children in a row or column.
- Bootstrap - a CSS (and JS) library of ready-made classes and components.
- CDN - a public server that hosts libraries so you can link them by URL.
Idea 1: Creating and serving an HTML document
An HTML file is plain text. Save this as index.html in a new folder:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>My first page</title>
</head>
<body>
<h1>Hello, web</h1>
<p>This is my first page.</p>
</body>
</html>
Open a terminal in that folder and serve it:
python -m http.server 8000
Visit http://localhost:8000/. The server sends index.html for /. You can also just double-click the file, but then the address starts with file:// and no server is involved.
| Part | What it does |
|---|---|
<!DOCTYPE html> | Tells the browser "this is modern HTML" |
<html lang="en"> | The root of everything; lang helps screen readers |
<head> | Information about the page (not shown): title, character set, links to CSS |
<title> | The text on the browser tab |
<body> | Everything you see on the page |
The three languages of the front end. HTML defines the content and structure, CSS defines the styling, JavaScript adds behaviour. JavaScript is an add-on for extra features, not a required part of every page. A page may load a script from a file like this:
<script src="app.js"></script>
Scripts are linked with src, stylesheets with <link rel="stylesheet" href="...">, images with <img src="...">, and page links with <a href="...">.
Elements can be nested (a list inside a body) but must close in reverse order: <p><b>Hi</b></p>, never <p><b>Hi</p></b>. Some tags have no content and no closing tag (void elements): <br>, <hr>, <img>, <input>, <meta>, <link>.
Idea 2: Text, lists, links, images and tables
<h1>Main title</h1> <!-- h1 to h6, h1 is biggest -->
<h2>Section</h2>
<p>A paragraph with <b>bold</b>, <i>italic</i> and <br> a line break.</p>
<hr> <!-- a horizontal line -->
<ul> <!-- unordered: bullets, order does not matter -->
<li>Rice</li>
<li>Dal</li>
</ul>
<ol> <!-- ordered: numbers, order matters -->
<li>Wash</li>
<li>Boil</li>
</ol>
<a href="https://example.com">An external link</a>
<a href="page2.html">A link to another file</a>
<a href="#reviews">Jump to the element with id reviews</a>
<img src="cat.png" alt="A sleeping cat" width="200">
- Lists.
ulhas bullets,olhas numbers, and both holdliitems. A recipe needsulfor ingredients (order irrelevant) andolfor the steps (order matters). This exact choice appeared in a past paper. - Links. The
hrefvalue can be a full URL, a relative file path, or#idto jump inside the page (that is the fragment from week 1). - Images.
srcis the file,altis the text for screen readers and for when the image fails to load.
Tables
<table border="1">
<thead>
<tr><th>Course</th><th>Credits</th></tr>
</thead>
<tbody>
<tr><td>MAD1</td><td>4</td></tr>
<tr><td>DBMS</td><td>4</td></tr>
</tbody>
</table>
tr is a row, th a header cell, td a data cell. A cell can span several columns with colspan="2" or rows with rowspan="2".
Block and inline elements
| Block | Inline | |
|---|---|---|
| Starts a new line? | Yes | No |
| Width | Full line by default | Only as wide as its content |
| Can you set width/height? | Yes | No |
| Examples | div, p, h1-h6, ul, li, table, form | span, a, b, i, img, input, button |
Use div (block) and span (inline) as neutral containers that carry no meaning of their own.
Semantic tags
header, nav, main, section, article, aside, footer. They look like plain boxes but say what the content is, which helps search engines, screen readers, and your own reading.
<body>
<header><h1>Study Hub</h1></header>
<nav><a href="/">Home</a> <a href="/about">About</a></nav>
<main>
<article><h2>Post</h2><p>Text...</p></article>
</main>
<footer>Copyright 2026</footer>
</body>
Write a body that shows a heading "Menu", an unordered list of two dishes, and a link "Home" that goes to index.html.
Need a hint?
Use h1, ul with li, and a href.
Show the full solution
<h1>Menu</h1>
<ul>
<li>Idli</li>
<li>Dosa</li>
</ul>
<a href="index.html">Home</a>Idea 3: Forms and input types
A form is how a browser sends data to the server. The action is the URL to send to, and method is GET or POST (week 1).
<form action="/signup" method="post">
<label for="u">Name</label>
<input type="text" id="u" name="uname" required>
<input type="email" name="mail" placeholder="you@example.com">
<input type="password" name="pwd" minlength="6">
<input type="number" name="age" min="5" max="99">
<input type="date" name="dob">
<input type="radio" name="level" value="fd"> Foundation
<input type="radio" name="level" value="dip"> Diploma
<input type="checkbox" name="agree" value="yes"> I agree
<select name="city">
<option value="che">Chennai</option>
<option value="blr">Bengaluru</option>
</select>
<textarea name="note" rows="3"></textarea>
<button type="submit">Send</button>
</form>
| Input type | Gives you |
|---|---|
text, password | One line of text (password shows dots) |
email, url, number, date | The same, plus built-in checks and a suitable keyboard |
radio | Pick one in a group (same name) |
checkbox | Tick any number |
submit / button type="submit" | Sends the form |
hidden | A value sent without being shown |
file | Upload a file (needs enctype="multipart/form-data") |
The name attribute is the key the server receives. Browsers send name=value pairs. An input with no name is not sent at all. The id is for CSS, JavaScript and <label for="...">; it is never what the server sees. In week 3 you read the values with request.form["uname"] (POST body) or request.args["uname"] (GET query string).
HTML5 validation. Attributes such as required, type="email", min, max, minlength and pattern make the browser check the data before sending. This is client-side validation. It improves the experience but a user (or a script) can bypass it, so the server must validate again (week 3).
:valid / :invalid in CSS. A field with required and no text is invalid, so a rule like input:valid { border: 2px solid red; } does not colour an empty required field, and does colour a correctly filled one. Past papers have asked this.
- No
namemeans the value is never sent. - Radio buttons are grouped by the same
name; different names make independent groups. - A form with
method="get"puts values in the URL; the default method is GET. - Client-side validation can be bypassed, so it never replaces server-side checks.
Idea 4: Adding CSS: inline, internal, external
A CSS rule has a selector and declarations:
p {
color: darkblue;
font-size: 18px;
}
Three ways to attach CSS:
<!-- 1. Inline: on one element -->
<p style="color: red;">Red text</p>
<!-- 2. Internal: a style tag in head -->
<style>
p { color: blue; }
</style>
<!-- 3. External: a separate file, linked in head (best for real projects) -->
<link rel="stylesheet" href="style.css">
External CSS is shared by many pages and is cached by the browser. In Flask the file lives in the static folder and is linked with url_for('static', filename='style.css') (week 3).
The cascade: who wins?
When several rules target one element, the browser decides in this order:
!importantbeats everything else (use rarely).- Higher specificity wins.
- If specificity ties, the rule that comes later wins.
- Some properties (colour, font) are inherited from the parent when no rule applies.
Specificity score, compare the columns from left to right:
| Selector kind | Weight | Example |
|---|---|---|
| Inline style | 1000 | style="..." |
| id | 100 | #main |
| class, attribute, pseudo-class | 10 | .note, [type="text"], :hover |
| type (tag), pseudo-element | 1 | p, ::before |
Question. What colour is the text?
<style>
p { color: blue; }
.note { color: green; }
#first { color: red; }
p.note { color: orange; }
</style>
<p id="first" class="note">Hello</p>
<p class="note">World</p>
Solution. First paragraph: scores are 1, 10, 100, 11. The id rule is highest: red. Second paragraph has no id: p.note (11) beats .note (10) and p (1): orange. If we added style="color: black" to the first paragraph, it would become black (inline = 1000), unless a rule used !important.
Idea 5: Selectors, all kinds
h1 { } /* type: every h1 */
.note { } /* class: class="note" */
#main { } /* id: id="main" (unique) */
* { } /* universal: every element */
h1, h2 { } /* group: either */
div p { } /* descendant: any p anywhere inside a div */
div > p { } /* child: p directly inside a div */
h1 + p { } /* adjacent sibling: the p right after an h1 */
h1 ~ p { } /* general sibling: all later p siblings of an h1 */
a:hover { } /* pseudo-class: mouse is over */
li:nth-child(2) { } /* li that is the 2nd child of its parent */
li:nth-child(odd) { } /* 1st, 3rd, 5th ... (even: 2nd, 4th ...) */
li:first-child { }
input:focus { }
input:valid { }
p::before { content: "Note: "; } /* pseudo-element: injected text before */
p::first-line { }
input[type="text"] { } /* attribute equals */
[class~="out"] { } /* attribute contains the WORD "out" in a space-separated list */
a[href^="https"] { } /* attribute starts with */
a[href$=".pdf"] { } /* attribute ends with */
a[href*="example"] { } /* attribute contains the text anywhere */
- One colon
:is a pseudo-class (a state), two colons::is a pseudo-element (a part). div pgoes at any depth;div > ponly one level down.[class~=out]matches an element whoseclasslist contains the wordout.[id~=out]does the same for id, so it matchesid="out".
Question. Which items turn red?
<style>
ul > li:nth-child(even) { color: red; }
li ~ li:last-child { font-weight: bold; }
</style>
<ul>
<li>A</li><li>B</li><li>C</li><li>D</li><li>E</li>
</ul>
Solution. nth-child(even) picks the 2nd and 4th: B and D are red. The second rule picks the last li that has an li sibling before it: E is bold. A, B, C and D are not bold.
Question. With these two rules, what border does each paragraph get?
<style>
[id~=out] { border: 10px solid red; }
[class~=out] { border: 5px solid yellow; }
</style>
<p id="out">One</p>
<p class="out">Two</p>
<p class="outer">Three</p>
Solution. One has the id, so it gets the red 10px border. Two has the class word out: yellow 5px border. Three has class outer, a different word, so it gets no border. The paragraph with the class does not get the id rule, and the headings get nothing.
div p {color: red}, .box p {color: blue}, p {color: green}. HTML: <div class="box"><p>Hi</p></div>. What colour?
Need a hint?
Count: div p = 1 + 1, .box p = 10 + 1, p = 1.
Show the full solution
Scores 2, 11 and 1. .box p wins: blue.
Idea 6: Box model, units and colours
Every element is a box made of four layers, from the inside out: content, padding (inside space), border, margin (outside space).
.card {
width: 200px;
padding: 20px;
border: 5px solid black;
margin: 10px;
}
With the default box-sizing: content-box, width is the content width:
- Visible box width = 200 + 20 + 20 (padding) + 5 + 5 (border) = 250px.
- Space taken in the row = 250 + 10 + 10 (margin) = 270px.
With box-sizing: border-box the 200px already includes padding and border: visible width 200px; content shrinks to 150px. Bootstrap sets border-box everywhere.
Shorthand: margin: 10px 20px; means top/bottom 10, left/right 20. Four values go clockwise: top, right, bottom, left.
| Unit | Meaning |
|---|---|
px | A fixed pixel |
% | Percent of the parent |
em | Times the current element's font size |
rem | Times the root (html) font size, usually 16px |
vw, vh | 1 percent of the window width / height |
Colours: names (red), hex (#ff0000, #f00), rgb(255, 0, 0), rgba(255, 0, 0, 0.5) (the last number is opacity) and hsl(0, 100%, 50%).
Display and position
display: block | inline | inline-block | none | flex.noneremoves the element entirely. (visibility: hiddenhides it but keeps the space.)position:static(default, normal flow),relative(shift from its normal place),absolute(placed against the nearest positioned ancestor),fixed(stays on the screen when scrolling),sticky(sticks after scrolling to a point). Offsets usetop,left,right,bottom.
Flexbox basics
<style>
.row { display: flex; justify-content: space-between; align-items: center; gap: 10px; }
.row div { flex: 1; }
</style>
<div class="row">
<div>One</div><div>Two</div><div>Three</div>
</div>
Setting display: flex on the parent makes the children flex items laid out in a row.
| Property (on the parent) | Effect |
|---|---|
flex-direction (row or column) | Main axis direction (row is the default) |
justify-content | Spacing along the main axis: flex-start, center, space-between, space-around |
align-items | Alignment along the cross axis: stretch, center, flex-start |
flex-wrap: wrap | Allow items to go to the next line |
gap | Space between items |
On a child, flex: 1 makes children share the free space equally. Above, three equal columns.
div {width: 300px; padding: 10px; border: 2px solid; margin: 15px; box-sizing: border-box;}. What is the visible width, and what space does it take in a row?
Need a hint?
With border-box the width already includes padding and border.
Show the full solution
Visible width: 300px. Space in a row: 300 + 15 + 15 = 330px. (With content-box it would be 324px visible.)
Idea 7: Bootstrap
Bootstrap gives ready classes. No install is needed: link it from a CDN in the head (and, only if you use components such as dropdowns or modals, add the script before the closing body).
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Bootstrap demo</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div class="container">
<h1 class="text-center mt-4">Courses</h1>
<div class="row">
<div class="col-12 col-md-6">
<div class="alert alert-info">Left half on medium screens and up</div>
</div>
<div class="col-12 col-md-6">
<button class="btn btn-primary">Enrol</button>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>
- Containers.
containeris centred with a maximum width;container-fluidspans the whole width. - Grid. A
rowholds 12 columns per line.col-md-6means "take 6 of 12 columns (half) from the medium breakpoint upward; below that stack full width". Breakpoints:sm576px,md768px,lg992px,xl1200px.col-4 col-4 col-4makes three equal columns. - Utilities. Small single-purpose classes: spacing
m-3,mt-2,px-4,p-0(scale 0 to 5, where3is 1rem), texttext-center,fw-bold, colourtext-danger,bg-light, displayd-flex,d-none,justify-content-between,border,rounded. - Components.
btn btn-primary,card,navbar,table table-striped,form-control,alert alert-success,list-group.
- Missing the
viewportmeta tag makes the responsive grid behave badly on phones. - Columns in a row should add up to 12; extras wrap to the next line.
col-md-6does nothing below 768px: items stack. Large-to-small is mobile first.- Forgetting the CDN link means the classes do nothing and the page looks plain.
Use one static/style.css for your own styles and Bootstrap from a CDN for layout. In the viva be ready to show one container/row/col layout, explain col-md-6, and change a button colour or a column width live. Use a semantic structure (nav, main, footer) and alt text on images.
Exam radar
The graded questions that touch this week's keywords are about 215 of 692 in Quiz 1, 128 of 624 in Quiz 2 and 252 of 1600 in the End Term (a loose keyword match, since forms and templates are counted too). The questions that really test HTML and CSS are fewer and are very predictable:
- Selectors and specificity: predict which paragraph gets which border or colour (attribute selectors such as
[class~=out],:nth-child, descendant versus child). - Which tag?
ulversusolfor a recipe; the right way to link a script (scriptwithsrc) or a stylesheet. - Forms and validation: what
requiredand:validdo; whether a form has client-side validation, server-side validation or both; thenameattribute. - Statements: "HTML defines content, CSS defines styling" (true); "JavaScript is always the core part of the frontend" (false).
- Small JavaScript: an
onclickbutton orJSON.parseon a string and showing properties throughinnerHTML. Know only the basics:getElementById,innerHTML,alert. - Box model and Bootstrap grid questions appear less often but are easy marks.
- The class word match
[class~=out]is not the same as[class*=out](outermatches*=but not~=). - More specific beats later: an id rule above beats a class rule below.
div > pis direct children only.visibility: hiddenkeeps the space;display: nonedoes not.olis numbered and ordered,ulhas bullets.
Quick quiz
A recipe page needs ingredients (order does not matter) and cooking steps (order matters). Which tags?
olfor ingredients,ulfor stepsolfor bothulfor bothulfor ingredients,olfor steps
A page has p {color: blue} in a style tag, .note {color: green}, and <p class="note" style="color: black">. The text colour is...
- blue
- green
- black
- red
Which selector picks only li elements that are direct children of a ul?
ul liul > liul ~ liul + li
> child combinator means direct child; a space means any depth.Select all TRUE statements. (select all that apply)
- HTML describes content and structure; CSS describes styling.
- An input without a
nameis not submitted with its form. - JavaScript is always required for every web page.
:hoveris a pseudo-element.
:hover has one colon so it is a pseudo-class; JavaScript is optional extra behaviour.width: 200px; padding: 20px; border: 5px solid; margin: 10px; with content-box. What is the visible width of the box?
- 200px
- 220px
- 250px
- 270px
In Bootstrap, <div class="col-md-6"> means...
- half the width at all screen sizes
- half the row from the md breakpoint upward, full width below
- 6 pixels wide
- hide on medium screens
Viva: say it out loud
What is the difference between HTML, CSS and JavaScript?
HTML gives the structure and content, CSS gives the look and layout, JavaScript adds behaviour such as reacting to clicks. I keep them in separate files where I can.
How does the browser decide which CSS rule wins?
It checks for !important, then compares specificity (inline, then id, then class, then tag), and if the score ties the later rule wins. Some properties are also inherited from the parent.
What is the box model?
Every element is a box: content, then padding, then border, then margin. The visible width adds content, padding and border. border-box makes the width include padding and border.
Why do you use semantic tags and alt text?
Semantic tags such as nav and main tell browsers, search engines and screen readers what each part is. The alt text describes an image for people who cannot see it and shows when the image fails.
Explain client-side and server-side validation in your form.
The browser checks required, type="email" and similar attributes before submit, which is quick but can be bypassed. My Flask route validates again, because only the server can be trusted.
How did you do your layout?
I used Bootstrap from a CDN: a container, row, and col-md-* columns for the grid, plus utility classes for spacing. For small custom pieces I used flexbox with justify-content and align-items.
Where do your CSS files live in your Flask project?
In the static folder, linked in the template with url_for('static', filename='style.css').
Recap
- HTML skeleton:
doctype,html,head(title, meta, links),body. Serve withpython -m http.server. - Block elements start a new line; inline elements sit in a line. Use semantic tags.
- Forms send
name=valuepairs;namematters,iddoes not. HTML5 validation is client-side only. - CSS comes inline, internal or external; the winner is decided by
!important, specificity (id 100, class 10, tag 1, inline 1000), then order. - Selectors: type, class, id, descendant,
>,+,~,:hover,:nth-child,::before,[attr]forms. - Box width = content + padding + border (+ margin for space). Flexbox:
display: flexon the parent. - Bootstrap: CDN link,
container,row, 12-columncol-md-*, utility classes, components.
- Skeleton:
<!DOCTYPE html><html><head><title></title></head><body></body></html> - Links:
<link rel="stylesheet" href="s.css">,<script src="a.js"></script>,<a href="">,<img src="" alt=""> - Specificity: inline 1000,
#id100,.class [attr] :pseudo-class10,tag ::pseudo-element1 - Visible width (content-box) = width + 2 padding + 2 border; space = + 2 margin
- Flex:
display: flex; justify-content: ...; align-items: ...; gap: ... - Bootstrap 5 CSS:
https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css - Breakpoints: sm 576, md 768, lg 992, xl 1200; grid = 12 columns
- MAD2 week 4 - Vue basics: instances, directives and reactivity: A Vue template is HTML with extra attributes (v-if, v-for, :class). Know HTML and CSS first.
Practise with real exam questions
These come straight from past papers (with the real figures and the official answer key). Open 60 real questions on this week All Quiz 1 papers