HTML, CSS, and Forms

You can read this note directly if you know that a browser displays web pages. HTML gives a page its structure, CSS controls its presentation, and forms collect user input.

Beginner Mental Model

HTML and CSS run on the client side in the browser. They do not directly execute Python or change a database.

A form creates a request that a server route can process:

user enters a value
-> browser submits the form
-> Flask reads the submitted key-value pair
-> Python processes the value
-> server returns a response

For example:

HTML input name="title" -> Flask request.form["title"]

The input name and the Flask dictionary key must match exactly.

Basic HTML Document

<!doctype html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Book Search</title>
  </head>
  <body>
    <h1>Book Search</h1>
    <p>Find a book by title.</p>
  </body>
</html>
PartPurpose
<!doctype html>tells the browser to use modern HTML
<html>root element containing the document
<head>metadata and linked resources
<meta charset="utf-8">allows Unicode text to be interpreted correctly
<title>text shown on the browser tab
<body>visible page content

HTML elements form a nested tree. Correct nesting helps the browser interpret the page predictably.

Common Elements

ElementPurpose
<h1>main heading
<p>paragraph
<a>hyperlink
<img>image
<ul> / <ol>unordered / ordered list
<table>tabular data
<form>user-input form
<input>input control
<label>description associated with an input
<button>clickable button

Example table:

<table>
  <tr>
    <th>Title</th>
    <th>Status</th>
  </tr>
  <tr>
    <td>Project Report</td>
    <td>Submitted</td>
  </tr>
</table>

Forms and Input Names

A form sends user input to the URL in its action attribute using the chosen HTTP method.

<form method="post" action="/search">
  <label for="title">Title</label>
  <input id="title" type="text" name="title">
  <button type="submit">Search</button>
</form>

Important attributes:

AttributeMeaning
methodHTTP method, commonly get or post
actionroute that receives the submitted request
namekey used by Flask to read the submitted value
idunique identifier, often connected to a label or CSS rule
typeinput type such as text, radio, checkbox, or file
valuesubmitted value or initial value, depending on input type
placeholdershort hint shown inside an empty field

The for value of a label should match the input’s id:

<label for="email">Email</label>
<input id="email" name="email" type="text">

This improves clarity and makes the label clickable.

Common Input Controls

<input type="text" name="username">
 
<input type="radio" id="yes" name="choice" value="yes">
<label for="yes">Yes</label>
 
<input type="checkbox" id="news" name="news" value="subscribe">
<label for="news">Subscribe</label>
 
<select name="level">
  <option value="h1">H1</option>
  <option value="h2">H2</option>
</select>

Radio buttons that belong to one choice group share the same name. Checkboxes may submit zero, one, or several values.

GET and POST

MethodAppropriate beginner useWhere submitted values appear
GETretrieve or search for datausually in the URL query string
POSTsubmit data for processing, uploads, or state-changing actionsrequest body

A POST request does not automatically make data secure. HTTPS is still required to protect data in transit, and the server must validate and handle the data safely.

Use POST for file uploads and database inserts.

File Input

<form method="post" enctype="multipart/form-data">
  <label for="photo">Choose an image</label>
  <input id="photo" type="file" name="photo">
  <button type="submit">Upload</button>
</form>

enctype="multipart/form-data" is required because uploaded files are not sent as ordinary text fields. Flask reads them from request.files, not request.form.

CSS

CSS controls presentation, not server-side processing.

body {
  font-family: Arial, sans-serif;
}
 
.error {
  font-weight: bold;
}
 
#main-title {
  text-align: center;
}
SelectorMeaning
bodyselects elements by tag name
.errorselects elements with class="error"
#main-titleselects the element with id="main-title"

A class may be reused on several elements. An id should be unique within one page.

Linking an External Stylesheet

For a plain HTML page:

<link rel="stylesheet" href="style.css">

In a Flask template:

<link rel="stylesheet"
      href="{{ url_for('static', filename='style.css') }}">

The stylesheet normally belongs in Flask’s static/ folder.

Complete Client-to-Server Trace

HTML:

<form method="post" action="/greet">
  <input type="text" name="student_name">
  <button type="submit">Greet</button>
</form>

Flask:

@app.route("/greet", methods=["POST"])
def greet():
    student_name = request.form["student_name"]
    return render_template("greet.html", name=student_name)

Jinja template:

<p>Hello, {{ name }}</p>

The names have different roles:

NameRole
student_name in HTMLsubmitted form key
student_name in Pythonlocal Python variable
name passed to templateJinja variable name

Common Mistakes

  • Giving an input no name, so Flask cannot retrieve its value.
  • Using a form key in Flask that does not match the HTML input name.
  • Using POST in Flask but leaving the form method as GET, or vice versa.
  • Forgetting enctype="multipart/form-data" for file uploads.
  • Reading an uploaded file from request.form instead of request.files.
  • Confusing a CSS class selector (.) with an ID selector (#).
  • Expecting CSS to validate, store, or process submitted data.
  • Forgetting to link the external stylesheet.

Check Your Understanding

  1. What is stored inside the <body> element?
  2. Why does an input need a name?
  3. What is the difference between id and name?
  4. Why is multipart/form-data needed for file uploads?
  5. Does POST by itself make a submission secure?

Answers:

  1. The visible content and controls of the page.
  2. The server uses it as the key for the submitted value.
  3. id identifies an element in the page; name identifies submitted form data.
  4. It allows files and ordinary fields to be encoded as separate parts of the request.
  5. No. HTTPS and safe server-side handling are still required.