Skip to content

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

HTML runs in the browser — no install. Save a .html file and open it, or serve it:

python -m http.server 8000     # serve the folder at localhost:8000

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

<header>  <nav>   <main>   <section>   <article>   <aside>   <footer>
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)

<div id="results" data-model="v2" data-count="42"></div>
const el = document.getElementById("results");
el.dataset.model;      // "v2"  — read in JS

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 => ({"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;"}[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> needs alt; 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

Official Documentation

Last Updated

2026-09-06 — Initial version.