Building a To-Do Tutorial with Only HTML: A Beginner's Guide
Posted Date: 2025-10-28
Hey there, future web developer! 👋
Welcome to the blog. Today, we're going to tackle a classic beginner project: building a to-do list. But we're going to do it with a special challenge: we will use only HTML.
That's right. No CSS for styling, and definitely no JavaScript for functionality.
"But wait," you ask, "how can you make an interactive to-do list with just HTML?"
The short answer is: you can't. An HTML-only list will be static. You won't be able to add new items dynamically or have the list save your changes.
So, why are we doing this? Because it's the absolute best way to learn the semantic structure of a webpage. Before you can make things interactive (JavaScript) or pretty (CSS), you must build a strong, logical foundation (HTML). This project will teach you how to use lists, forms, labels, and other essential tags correctly.
Let's get started!
Part 1: The Basic Skeleton
First, every HTML document needs its "boilerplate." This is the minimum setup required for a browser to understand your page.
Create a new file named index.html and open it in your
favorite text editor.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>My HTML To-Do List</title>
</head>
<body>
<h1>My Awesome To-Do List</h1>
</body>
</html>
Let's break this down:
-
<!DOCTYPE html>: Tells the browser, "This is an HTML5 document." -
<html>: The root element that wraps everything. Thelang="en"attribute tells the browser the page is in English, which is great for accessibility. -
<head>: Contains the "meta" information about your page (data about the data), like the character encoding (<meta charset="UTF-8">) and the title that appears in your browser tab (<title>). -
<body>: This is where all your visible content—like text, images, and, yes, your list—will go. -
<h1>: Our main heading. It's the most important title on the page.
Save this file and open it in your web browser. You should see a blank page with the text "My Awesome To-Do List" at the top.
Part 2: The Core Element: A Single Task
What's the most important part of any to-do list? The ability to check something off. HTML has a perfect element for this!
We're going to use two tags together:
-
<input type="checkbox">: This creates the clickable checkbox. -
<label>: This provides the text description for the checkbox.
Let's add our first task inside the <body>, right
below the <h1>.
<!-- Inside the <body> tag -->
<h1>My Awesome To-Do List</h1>
<input type="checkbox" id="task1">
<label for="task1">Buy groceries</label>
This is super important, so pay close attention to
the id and for attributes:
-
The
<input>has anid="task1". Anidmust be unique on the entire page. It's like a specific name tag for that one element. -
The
<label>has afor="task1". Theforattribute tells the label which element it's describing. It "links" the label to the input with the matchingid.
Why do this? Two big reasons:
- Usability: Now, you can click either the checkbox OR the text "Buy groceries" to check the box. This gives the user a much larger click target.
- Accessibility: Screen readers (software used by visually impaired users) will now announce that the label "Buy groceries" belongs to the checkbox. This makes your page usable for everyone.
Save and refresh your browser. You should have a working checkbox!
Part 3: Making it a List
That one task looks a little lonely. A to-do list is, well, a list of things. The proper HTML way to group a list of items is with... you guessed it, a list element!
Since the order of our to-do items doesn't really matter
(it's not a set of instructions), we'll use an
unordered list (<ul>). Each item
in the list will be a list item (
<li>).
Let's wrap our task from Part 2 in these tags and add a few more.
<!-- Inside the <body> tag -->
<h1>My Awesome To-Do List</h1>
<ul>
<li>
<input type="checkbox" id="task1">
<label for="task1">Buy groceries</label>
</li>
<li>
<input type="checkbox" id="task2">
<label for="task2">Feed the cat</label>
</li>
<li>
<input type="checkbox" id="task3">
<label for="task3">Finish HTML tutorial</label>
</li>
</ul>
Look at that structure!
-
The
<ul>(unordered list) acts as the container for all the tasks. -
Each
<li>(list item) wraps a single task. -
Inside each
<li>, we have our checkbox/label pair. -
Notice: Each
idis unique (task1,task2,task3), and eachforattribute matches its correspondingid. This is crucial.
Save and refresh. Now you have a proper bulleted list of tasks, and you can check each one off.
Part 4: Organizing Your List with <fieldset>
Okay, our list is functional, but it's a bit messy. What if we have tasks for "Work" and tasks for "Home"? We can group them semantically.
For this, we'll use the <fieldset> element, which
is meant to group related controls in a form. To give the group a
name, we use the <legend> tag.
Let's refactor our code to create two different groups.
<!-- Inside the <body> tag -->
<h1>My Awesome To-Do List</h1>
<fieldset>
<legend>Home Tasks</legend>
<ul>
<li>
<input type="checkbox" id="task1">
<label for="task1">Buy groceries</label>
</li>
<li>
<input type="checkbox" id="task2">
<label for="task2">Feed the cat</label>
</li>
</ul>
</fieldset>
<fieldset>
<legend>Work Tasks</legend>
<ul>
<li>
<input type="checkbox" id="task3">
<label for="task3">Finish HTML tutorial</label>
</li>
<li>
<input type="checkbox" id="task4">
<label for="task4">Email the boss</label>
</li>
</ul>
</fieldset>
By default, the browser will even draw a nice box around our groups
with the <legend> text acting as a title for the
box. This is pure semantic HTML adding a (slight) visual style, all
without a single line of CSS!
Part 5: Advanced (but still HTML-only!) Touches
Let's add two more features to show off your HTML skills.
1. Marking a Task as "Done" by Default
What if a task is already completed? We can tell the browser to render
the checkbox as "checked" when the page first loads. We do this by
adding the boolean attribute checked.
Let's make "Feed the cat" checked by default.
<!-- Inside the "Home Tasks" fieldset -->
<li>
<input type="checkbox" id="task2" checked>
<label for="task2">Feed the cat</label>
</li>
Just add the word checked inside the
<input> tag. That's it! Save and refresh, and
you'll see the box is already ticked.
2. Adding Sub-Tasks with <details>
What if a task is complex? "Buy groceries" could have its own list. We
can use the awesome <details> and
<summary> tags to create a collapsible, expandable
section.
Let's modify our "Buy groceries" task.
<!-- Inside the "Home Tasks" fieldset -->
<li>
<input type="checkbox" id="task1">
<label for="task1">Buy groceries</label>
<details>
<summary>Grocery List</summary>
<ul>
<li>
<input type="checkbox" id="subtask1">
<label for="subtask1">Milk</label>
</li>
<li>
<input type="checkbox" id="subtask2">
<label for="subtask2">Eggs</label>
</li>
<li>
<input type="checkbox" id="subtask3">
<label for="subtask3">Bread</label>
</li>
</ul>
</details>
</li>
This is called nesting. We're putting a
<ul> inside a
<details> tag, which is inside our main
<li>.
-
<details>: The wrapper for the collapsible content. -
<summary>: The text that is always visible (e.g., "Grocery List"). Clicking this will show/hide the rest. - The rest of the content (our nested list) is hidden until you click the summary.
How cool is that? An interactive toggle with only HTML!
Part 6: The "Add Task" Form (The Illusion)
Finally, every to-do list app has a way to add new tasks. As we said, we can't make this work without JavaScript, but we can—and should—build the HTML structure for it.
The semantic way to build a section for user input is with a
<form> tag.
Let's add this at the bottom of our <body>.
<!-- At the bottom of the <body> -->
<h2>Add a New Task</h2>
<form>
<label for="new-task">Task:</label>
<input type="text" id="new-task">
<button type="submit">Add Task</button>
</form>
Let's break this down:
-
<form>: The container for our input fields. -
<label for="new-task">: The label, just like before, linked to... -
<input type="text" id="new-task">: This time, thetypeistext, which gives us an empty text box for typing. -
<button type="submit">: A button! Thetype="submit"tells the browser this button's job is to "submit" the form.
What happens when you click it?
Right now, it will just refresh the page. This is the default HTML
behavior for a form submission. To make it actually add a
task to the list, you would need JavaScript to intercept this
"submit" event, grab the text from the input box, and create new HTML
elements.
But for our HTML-only mission, simply having the structure in place is a perfect end-goal.
Final Code & What's Next
Here is our complete index.html file with everything we
built.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>My HTML-Only To-Do List</title>
</head>
<body>
<h1>My Awesome To-Do List</h1>
<fieldset>
<legend>Home Tasks</legend>
<ul>
<li>
<input type="checkbox" id="task1">
<label for="task1">Buy groceries</label>
<details>
<summary>Grocery List</summary>
<ul>
<li>
<input type="checkbox" id="subtask1">
<label for="subtask1">Milk</label>
</li>
<li>
<input type="checkbox" id="subtask2">
<label for="subtask2">Eggs</label>
</li>
<li>
<input type="checkbox" id="subtask3">
<label for="subtask3">Bread</label>
</li>
</ul>
</details>
</li>
<li>
<input type="checkbox" id="task2" checked>
<label for="task2">Feed the cat</label>
</li>
</ul>
</fieldset>
<fieldset>
<legend>Work Tasks</legend>
<ul>
<li>
<input type="checkbox" id="task3">
<label for="task3">Finish HTML tutorial</label>
</li>
<li>
<input type="checkbox" id="task4">
<label for="task4">Email the boss</label>
</li>
</ul>
</fieldset>
<h2>Add a New Task</h2>
<form>
<label for="new-task">Task:</label>
<input type="text" id="new-task" placeholder="e.g. Take out the trash">
<button type="submit">Add Task</button>
</form>
</body>
</html>
Congratulations! You just built a visually organized, accessible, and semantically rich to-do list using only HTML.
You've learned about:
- 📜 Basic HTML boilerplate
-
☑️ Checkboxes (
<input type="checkbox">) -
🏷️ The critical importance of
<label>withforandid -
📋 Lists (
<ul>and<li>) -
📦 Grouping with
<fieldset>and<legend> -
👉 Showing pre-completed tasks with the
checkedattribute -
🔽 Creating collapsable sections with
<details>and<summary> -
📝 Building input forms with
<form>,<input type="text">, and<button>
Your next steps?
- Try adding CSS! How would you remove the bullets? Change the font? Make the "Add Task" button green?
-
Try adding JavaScript! How would you make the "Add
Task" button actually add a new
<li>to the list?
You've built the perfect foundation. Happy coding!