HTML Essentials¶
The skeleton of every web page and dashboard. You rarely need deep HTML — but you do need enough to build UIs around your models: result pages, feedback forms, tables of predictions, and media viewers.
Table of Contents¶
- Installation / Setup
- Core Syntax
- Common Operations
- Practical Examples
- Common Patterns
- Best Practices
- Common Mistakes
- Troubleshooting
- Related Cheat Sheets
- Official Documentation
Installation / Setup¶
HTML runs in the browser — no install. Save a .html file and open it, or serve it:
Minimal document:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>My app</title>
<link rel="stylesheet" href="style.css"> <!-- CSS -->
</head>
<body>
<h1>Hello</h1>
<script src="app.js"></script> <!-- JS at the end -->
</body>
</html>
Core Syntax¶
Document structure & semantic elements¶
| Element | Use |
|---|---|
<h1>–<h6> |
headings (one <h1> per page) |
<p> |
paragraph |
<a href="..."> |
link (target="_blank" for new tab) |
<span> / <div> |
inline / block containers |
<ul> <ol> <li> |
bullet / numbered lists |
<strong> / <em> |
bold / italic (semantic) |
<br> / <hr> |
line break / rule |
<img src alt> |
image (always alt!) |
<code> / <pre> |
inline / block code |
Forms (sending data to your model)¶
<form action="/predict" method="post">
<label for="text">Review:</label>
<input type="text" id="text" name="text" required>
<label for="model">Model:</label>
<select id="model" name="model">
<option value="v1">v1</option>
<option value="v2">v2</option>
</select>
<label for="threshold">Threshold:</label>
<input type="range" id="threshold" name="threshold" min="0" max="1" step="0.05">
<button type="submit">Predict</button>
</form>
| Input type | Use |
|---|---|
text, email, number, password |
typed entry |
range, checkbox, radio |
controls |
date, file |
dates, uploads |
hidden |
pass state without showing |
textarea |
multi-line text |
name="..." is what your backend receives: request.form["text"] in Flask, Form(...) in FastAPI.
Tables (results, metrics, comparisons)¶
<table>
<thead>
<tr><th>Model</th><th>Accuracy</th><th>Latency</th></tr>
</thead>
<tbody>
<tr><td>baseline</td><td>0.72</td><td>3 ms</td></tr>
<tr><td>xgboost</td><td>0.91</td><td>12 ms</td></tr>
</tbody>
</table>
Media¶
<img src="chart.png" alt="ROC curve" width="600">
<video controls src="demo.mp4"></video>
<audio controls src="clip.mp3"></audio>
<iframe src="https://example.com" title="Embed"></iframe>
Common Operations¶
Data-* attributes (hook for JavaScript)¶
Common elements for AI dashboards¶
<!-- Progress / status -->
<progress value="7" max="10"></progress>
<meter value="0.91" min="0" max="1"></meter>
<!-- Semantic status -->
<p role="status" aria-live="polite">Training finished</p>
<!-- Disclosure (FAQ / model cards) -->
<details><summary>Model card</summary><p>...</p></details>
Practical Examples¶
1. A minimal prediction form + result box¶
<h1>Sentiment demo</h1>
<form id="sentiment-form">
<textarea id="review" name="review" rows="4" required></textarea>
<button type="submit">Analyze</button>
</form>
<p id="result" hidden></p>
<script>
document.getElementById("sentiment-form").addEventListener("submit", async (e) => {
e.preventDefault();
const review = document.getElementById("review").value;
const res = await fetch("/predict", {
method: "POST",
headers: {"Content-Type": "application/json"},
body: JSON.stringify({ text: review }),
});
const data = await res.json(); // see json.md
document.getElementById("result").hidden = false;
document.getElementById("result").textContent =
`Sentiment: ${data.label} (${(data.score * 100).toFixed(1)}%)`;
});
</script>
2. Prediction results table rendered from JS¶
<table id="scores"><thead><tr><th>Class</th><th>Prob</th></tr></thead><tbody></tbody></table>
<script>
const rows = data.probs.map(p =>
`<tr><td>${p.label}</td><td>${(p.value * 100).toFixed(2)}%</td></tr>`).join("");
document.querySelector("#scores tbody").innerHTML = rows;
</script>
3. Embed an interactive Plotly figure¶
<div id="chart"></div>
<script src="https://cdn.plot.ly/plotly-2.35.2.min.js"></script>
<script>
Plotly.newPlot("chart", JSON.parse(document.getElementById("figure-json").textContent));
</script>
Common Patterns¶
<!-- Escape user content before injecting (XSS!) -->
<script>
function escapeHtml(s) {
return s.replace(/[&<>"']/g, c => ({"&":"&","<":"<",">":">",'"':""","'":"'"}[c]));
}
el.innerHTML = escapeHtml(userText); // never raw userText
</script>
<!-- Forms without page reload -->
<form onsubmit="handle(event); return false;">
<!-- Reusable component-ish markup -->
<template id="row-tpl">
<tr><td class="name"></td><td class="score"></td></tr>
</template>
Best Practices¶
- Use semantic elements (
main,nav,button,label) — better accessibility and SEO for free. - Every
<img>needsalt; every form input needs a<label>. - One
<h1>per page; heading levels in order. - Separate concerns: HTML for structure, CSS for looks, JavaScript for behavior.
- Never build HTML strings from user data without escaping — that's how XSS happens (see Cybersecurity).
- Validate forms with
required/types and server-side — client checks are cosmetic. - Start with plain HTML semantics and style later; frameworks add complexity you often don't need.
Common Mistakes¶
| Mistake | Consequence | Fix |
|---|---|---|
Forgetting <meta charset="utf-8"> |
Mojibake (é, 中文, emoji) | Add charset meta |
<input> without name |
Backend never receives the value | Always set name |
innerHTML with user text |
XSS vulnerability | Escape or use textContent |
Buttons without type="button" inside forms |
Accidental form submit | type="submit" only where wanted |
| Div soup instead of semantic tags | Unreadable, poor a11y | Use main/nav/section etc. |
Duplicate ids |
JS breaks silently | IDs must be unique; classes repeat |
| Unclosed tags | Browser "fixes" it unpredictably | Keep nesting tidy; validate |
Troubleshooting¶
| Symptom | Cause | Fix |
|---|---|---|
| CSS not applying | Wrong path / cache | DevTools Network; hard refresh |
| Form submits but page reloads | No preventDefault in JS |
See javascript.md events |
| Content looks unstyled in email/export | CSS support differs | Inline styles for email; check target |
| Layout differs across browsers | Missing viewport meta / old features | Add viewport; check support (caniuse) |
Accents/emoji as ? |
Wrong encoding | charset="utf-8"; server Content-Type |
Related Cheat Sheets¶
- css.md — styling: selectors, flexbox, grid
- javascript.md — DOM, events, fetch
- http.md — what happens when the form submits
- json.md — the payload your front end sends/receives
- APIs — backend for these forms
- Apps & UI — Streamlit/Gradio alternatives
- Home · Central Navigation Index
Official Documentation¶
Last Updated¶
2026-09-06 — Initial version.