Build a Semantic Landing Page with Vanilla JS Form Validation
Computer Science
Build a Semantic Landing Page with Vanilla JS Form Validation
Build a Semantic Landing Page with Vanilla JS Form Validation
From your solid HTML/CSS foundation, you’ll create a compact, accessible landing page and add a client‑side contact form that validates inputs and shows inline messages – all without any backend.
What you learn by building this
- Create a fully semantic HTML structure for a landing page
- Apply modern CSS selectors and layout techniques to achieve a compact design
- Write vanilla JavaScript to capture form input, validate fields, and display inline feedback
- Ensure the form is accessible and usable on all devices without server‑side code
Learning Journey
Semantic Markup Foundations
2 lessonsLay the structural groundwork for the landing page using only semantic HTML elements.
Styling the Landing Page
3 lessonsUse modern CSS selectors and layout modules to turn the markup into a compact, responsive design.
Vanilla JavaScript Form Validation
4 lessonsAdd client‑side logic that validates input, shows inline feedback, and clears the form on success.
Public lesson
Map the page layout with semantic elements
Lesson 3.1 — Give your page a semantic map
Your HTML project already demonstrated that you can build a document structure. This time, make the structure describe the page’s purpose clearly—not just how it happens to look. Keep your existing class names where possible so your CSS selectors continue to work.
Tasks
1. Map the page before changing it
Open your current index.html. On paper or in a note, match each visible area to one of these roles:
- Header — site name or logo and the main navigation
- Hero — the page’s main introduction and primary action
- Features — the repeated benefits or capabilities
- Contact — the heading, supporting text, and message form
- Footer — copyright or secondary information
Tasks
Now inspect your current markup. For each area, write down:
- Which element contains it now?
- Which semantic element should contain it?
- Which existing
classoridmust remain for your CSS?
For example:
| Page area | Semantic element | Existing selector to preserve |
|---|---|---|
| Site navigation | <header> and <nav> | your current navigation class |
| Main introduction | <section> inside <main> | your hero class |
| Repeated feature | <article> | your feature-card class |
You already know CSS selectors, so this is a chance to practice changing the HTML structure without accidentally disconnecting the styles.
Tasks
2. Build the document skeleton
In index.html, create a semantic structure like this. The comments show decisions you still need to make; do not leave them as the final page content.
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><!-- write the page title --></title>
<link rel="stylesheet" href="<!-- keep your stylesheet path -->">
</head>
<body>
<header>
<!-- site name or logo -->
<nav aria-label="Primary navigation">
<!-- add links to the page sections -->
</nav>
</header>
<main>
<section id="hero" aria-labelledby="hero-title">
<h1 id="hero-title"><!-- write the page's main heading --></h1>
<!-- add the hero description and primary action -->
</section>
<section id="features" aria-labelledby="features-title">
<h2 id="features-title"><!-- write the features heading --></h2>
<!-- create one article for each repeated feature -->
</section>
<section id="contact" aria-labelledby="contact-title">
<h2 id="contact-title"><!-- write the contact heading --></h2>
<!-- add supporting contact text -->
<form action="#" method="post">
<!-- add a labeled name field -->
<!-- add a labeled email field -->
<!-- add a labeled message field -->
<!-- add a submit button -->
</form>
</section>
</main>
<footer>
<!-- add the footer content -->
</footer>
</body>
</html>
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><!-- write the page title --></title>
<link rel="stylesheet" href="<!-- keep your stylesheet path -->">
</head>
<body>
<header>
<!-- site name or logo -->
<nav aria-label="Primary navigation">
<!-- add links to the page sections -->
</nav>
</header>
<main>
<section id="hero" aria-labelledby="hero-title">
<h1 id="hero-title"><!-- write the page's main heading --></h1>
<!-- add the hero description and primary action -->
</section>
<section id="features" aria-labelledby="features-title">
<h2 id="features-title"><!-- write the features heading --></h2>
<!-- create one article for each repeated feature -->
</section>
<section id="contact" aria-labelledby="contact-title">
<h2 id="contact-title"><!-- write the contact heading --></h2>
<!-- add supporting contact text -->
<form action="#" method="post">
<!-- add a labeled name field -->
<!-- add a labeled email field -->
<!-- add a labeled message field -->
<!-- add a submit button -->
</form>
</section>
</main>
<footer>
<!-- add the footer content -->
</footer>
</body>
</html>
Use your project’s real content rather than inventing a new page. A semantic element should describe the purpose of its content:
- Use
<header>for introductory or navigational content. - Use one
<main>for the page’s central content. - Use
<section>for each major, named area. - Use
<article>for each feature that could make sense as a separate item. - Use
<footer>for closing information. - Use
<nav>around navigation links. - Use
<form>around controls that collect information.
Tasks
Check in the browser
Save the file and refresh the page.
The page may look different if your old CSS depended on a wrapper such as <div>. That is useful evidence, not a failure. Use the browser inspector to check:
- Is there exactly one
<main>? - Are the hero, features, and contact areas inside
<main>? - Does each section have a heading?
- Are the feature items grouped consistently?
- Is the page’s content still visible?
If a style disappeared, compare the old and new class names. Restore the class on the new semantic element instead of adding unnecessary wrapper elements.
Tasks
3. Turn the features into repeated articles
Inside the features section, create an <article> for every feature card. Each article should have its own heading.
A feature section should have this shape, adapted to your project’s content and class names:
<section class="your-features-class" id="features" aria-labelledby="features-title">
<h2 id="features-title">Your features heading</h2>
<article class="your-card-class">
<h3>First feature</h3>
<p>Description of the first feature.</p>
</article>
<!-- add the remaining feature articles -->
</section>
<section class="your-features-class" id="features" aria-labelledby="features-title">
<h2 id="features-title">Your features heading</h2>
<article class="your-card-class">
<h3>First feature</h3>
<p>Description of the first feature.</p>
</article>
<!-- add the remaining feature articles -->
</section>
Do not copy the example’s class names unless they match your project. The important relationship is:
section heading → repeated article → article heading and content
Tasks
Refresh the page and confirm that every feature still appears. In the inspector, expand the features section and count the <article> elements.
Tasks
4. Give every form control a real label
A form is more usable when someone can identify each control even without relying on placeholder text. For each control:
- Give the
<label>aforvalue. - Give the matching input or textarea the same
id. - Add a useful
namevalue. - Add
requiredwhere the field must be completed.
Tasks
Create your fields using this pattern, changing the names and wording to fit your project:
<label for="your-field-id">Your field label</label>
<input id="your-field-id" name="your-field-name" type="text" required>
<label for="your-field-id">Your field label</label>
<input id="your-field-id" name="your-field-name" type="text" required>
For the email field, use type="email". For a longer message, use <textarea> rather than a single-line <input>.
Tasks
Your form should contain:
- a name field
- an email field
- a message field
- a submit button
The form does not need a working backend yet. Its job in this lesson is to have clear, valid structure.
Tasks
Check the labels
Click each label in the browser. The corresponding input should receive focus. If it does not, the label’s for value and control’s id do not match exactly.
Tasks
5. Connect the navigation
Give each navigation link an href that points to one of your section IDs:
<a href="#your-section-id">Your link text</a>
<a href="#your-section-id">Your link text</a>
Make sure the destination ID exists exactly once. Test every link by clicking it. The browser should move to the matching section.
Tasks
6. Run the semantic validation check
Use the Nu HTML Checker at validator.w3.org/nu. Upload your index.html or validate its local source.
Tasks
Fix errors one at a time. In particular, look for:
- missing closing tags
- duplicate
idvalues - headings placed outside the intended section
- labels whose
forvalue does not match an inputid - incorrectly nested elements
- missing
alttext on images, if your page has images
Warnings are worth reading, but first get the document to zero errors.
Finished when
Your index.html meets all of these checks:
- It has one
<header>, one<main>, and one<footer>. - The hero, features, and contact areas are named
<section>elements. - Each section has an appropriate heading.
- Repeated features use
<article>. - Navigation links point to real section IDs.
- Every form control has a matching label.
- The browser still displays your page and its styles.
- The semantic validator reports no errors.
Course Outline
3 modules · 9 lessons
Semantic Markup Foundations
Styling the Landing Page
Vanilla JavaScript Form Validation
Learn by building your own version.
Remix this public project to open the workspace, follow the guided build, and let the AI mentor teach you through the work instead of doing it for you.