Build a Festival Website with HTML & CSS
Build a responsive festival page with semantic HTML, CSS, media, and forms.
Introduction
30 Second Summary
A page can look polished in a wide window but become awkward when the screen narrows or the keyboard replaces the mouse. A dependable page keeps every section and control easy to reach.
In this project, you will build a colorful one-page festival website with semantic HTML and responsive CSS. The finished page supports section navigation, media playback, form validation, keyboard focus, and reduced-motion preferences.
What You'll Build
Your finished PixelWave page lets visitors explore a festival schedule, play captioned media, test an RSVP form, and use the experience comfortably in narrow windows.
By the end of this project, you'll have:
- Direct section navigation that jumps visitors to About, Schedule, Media, and RSVP from one menu.
- A responsive festival schedule that keeps wide table content inside its own horizontal scroller on narrow screens.
- An accessible RSVP experience with playable audio, captioned video, native form validation, visible keyboard focus, and reduced-motion support.
- Secret Mission: Audit the page using only the keyboard, a narrow window, and Windows reduced-motion settings.
Are there any prerequisites?
Basic familiarity with HTML and CSS is enough. You need Visual Studio Code and Microsoft Edge on Windows.
Keep an internet connection available for sample media playback.
Before We Start
First, let's lock in what you're creating before the hands-on work begins. You'll build one PixelWave festival landing page to review semantic HTML, navigation, CSS, tables, forms, audio, and video.
Set Up the Festival Workspace
Every part of the festival will live in one HTML document. A separate CSS file will control its presentation.
A small two-file workspace keeps this review focused on browser fundamentals. You will use Visual Studio Code to create the project before opening its first page in Microsoft Edge.
In this step, get ready to:
- Create the pixelwave-festival folder on your Desktop.
- Create the two project files in Visual Studio Code.
- Render the starter festival heading in Microsoft Edge.
Why plain HTML and CSS?
A static HTML and CSS page opens directly in a browser. Skipping package managers keeps your attention on the page itself.
Create the project folder and files
Visual Studio Code treats an opened folder as a workspace. Its Explorer view keeps every project file together.
- Press the Windows key to open Windows search.
- Type Visual Studio Code into the search field.
- Press Enter.
You will see the Visual Studio Code window. The next actions give it a named folder to manage.
Opening a folder can trigger a Workspace Trust prompt. This folder is safe because you are creating every file yourself.
- Select File in the top menu.
- Select Open Folder....
- Select Desktop in the folder picker.
- Select New Folder.
- Enter pixelwave-festival as the folder name.
- Press Enter.
- Select Select Folder.
- Select Yes, I trust the authors if the Workspace Trust prompt appears.
You should see pixelwave-festival at the top of the Explorer view. Your project now has a dedicated home on the Desktop.
- Select New File... in the Explorer view.
- Enter index.html as the file name.
- Press Enter.
- Select New File... in the Explorer view again.
- Enter styles.css as the file name.
- Press Enter.
You should see index.html beside styles.css in the Explorer view. That is the complete workspace for this project.
Files missing from the Explorer?
- Confirm that the Explorer heading shows pixelwave-festival.
- Check that both file names include their extensions.
- Still stuck? Help me create the PixelWave project files in Visual Studio Code.
Build the starter HTML document
A valid HTML document gives the browser a language, character encoding, page title, responsive viewport, and content structure. It also links the empty stylesheet that you will fill later.
- Select index.html in the Explorer view.
- Paste the following document into the editor:
<!doctype html>
<html lang="en-US">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width" />
<title>PixelWave Web Festival</title>
<link rel="stylesheet" href="styles.css" />
</head>
<body id="top">
<h1>PixelWave Web Festival</h1>
</body>
</html>
What does this document do?
- The doctype declaration tells the browser to use modern HTML rules.
- The lang="en-US" attribute identifies the document language.
- The utf-8 value supports a broad range of characters.
- The width=device-width viewport value lets the page match the device width.
- The stylesheet link connects styles.css to the page.
- The h1 element gives Microsoft Edge a visible heading to render.
- Save index.html by pressing Ctrl+S.
- Select styles.css in the Explorer view.
- Save styles.css by pressing Ctrl+S.
The editor for styles.css should contain no text. The file is linked and ready for the styling step.
✔️ Awesome, I've got everything!
Your HTML document is saved. Your linked stylesheet is empty and ready.
ⓧ I'd like to double check the full code
- Compare your index.html file with this complete starter document:
<!doctype html>
<html lang="en-US">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width" />
<title>PixelWave Web Festival</title>
<link rel="stylesheet" href="styles.css" />
</head>
<body id="top">
<h1>PixelWave Web Festival</h1>
</body>
</html>
What should match?
Your document should include every line shown above. Matching indentation makes the structure easier to inspect.
Your styles.css file should remain completely empty.
HTML not matching the example?
- Check that the opening html element includes lang="en-US".
- Confirm that the stylesheet file name is exactly styles.css.
- Make sure each opening element has the matching closing element shown in the reference.
- Still stuck? Help me compare my starter HTML with the PixelWave reference.
Open the starter page in Microsoft Edge
A browser can read this local HTML file without a server. The empty stylesheet stays connected while the heading proves that the document renders.
- Press the Windows key to open Windows search.
- Type Microsoft Edge into the search field.
- Press Enter.
Before you choose the file, what heading do you expect Microsoft Edge to render?
- Press Ctrl+O to open a file from your computer.
- Select Desktop in the file picker.
- Open the pixelwave-festival folder.
- Select index.html.
- Select Open.
You will see the heading PixelWave Web Festival on a plain page. Your first browser check works.
Heading missing in Microsoft Edge?
- Confirm that you selected index.html inside the Desktop pixelwave-festival folder.
- Return to Visual Studio Code from earlier to save index.html with Ctrl+S.
- Check that the file name ends with .html.
- Still stuck? Help me open my local PixelWave page in Microsoft Edge.
Your two-file workspace is ready. Next up, you will turn this starter heading into the semantic structure of the festival page.
Build the Semantic Festival Page
Your two-file workspace is ready. Microsoft Edge can already render the starter heading.
Meaningful Semantic HTML gives navigation a clear map of the page. Assistive technology can use the same landmarks before CSS changes how the page looks.
In this step, get ready to:
- Build the semantic page structure and navigation.
- Add the festival hero and About content.
- Test every in-page navigation link.
Build the page structure
A semantic structure divides the page into recognizable areas such as the header, navigation, main content, sections, and footer. Each navigation link uses a document fragment to target a section with a matching id value.
- In Visual Studio Code, select index.html in the Explorer view.
- Add id="top" to the opening body tag.
- Select the temporary <h1>PixelWave Web Festival</h1> element.
- Replace the temporary heading with this header and navigation:
<header class="hero">
<nav class="site-nav">
<a class="brand" href="#top">PixelWave</a>
<ul>
<li><a href="#about">About</a></li>
<li><a href="#schedule">Schedule</a></li>
<li><a href="#media">Media</a></li>
<li><a href="#rsvp">RSVP</a></li>
</ul>
</nav>
</header>
What does this navigation do?
- The header element identifies the introductory area of the page.
- The nav element identifies the page's primary navigation.
- Each href begins with # to point to an element on the same page.
- Save index.html.
- Switch back to Microsoft Edge.
- Click Refresh in the toolbar.
You will see the PixelWave brand followed by links for About, Schedule, Media, and RSVP.
Navigation links missing?
- Check that the nav element sits inside header.hero.
- Confirm that each link has both an opening <a> tag and a closing </a> tag.
- Still stuck? Help me fix the PixelWave header and navigation markup.
The links need matching destinations before they can move through the page. The main element now groups four sections with IDs that match the navigation fragments.
- Place your cursor below the closing </header> tag.
- Add the main section shells by pasting this code:
<main id="main-content">
<section id="about" class="panel">
</section>
<section id="schedule" class="panel">
<div class="section-heading">
<p class="eyebrow">Festival schedule</p>
<h2>Pick your learning track</h2>
</div>
</section>
<section id="media" class="panel">
<div class="section-heading">
<p class="eyebrow">Media stage</p>
<h2>Test native browser playback</h2>
</div>
</section>
<section id="rsvp" class="panel">
<div class="section-heading">
<p class="eyebrow">RSVP practice</p>
<h2>Test an accessible form</h2>
</div>
</section>
</main>
How do the fragments connect?
- The navigation value #schedule targets the section with id="schedule".
- The same relationship connects #about, #media, and #rsvp to their sections.
- Each placeholder heading gives you a visible destination to confirm during testing.
- Save index.html.
- Switch back to Microsoft Edge.
- Click Refresh in the toolbar.
- Select the Schedule navigation link.
You will move to the Festival schedule area. The heading Pick your learning track confirms that #schedule found its matching section.
Link does not reach the section?
- Check that the link uses href="#schedule".
- Check that the destination uses id="schedule" with the same spelling.
- Still stuck? Help me troubleshoot my document fragment links.
A footer closes the document with event context and a return path. Its #top link targets the ID you added to the opening body tag.
- Place your cursor below the closing </main> tag.
- Add the footer by pasting this code:
<footer>
<p>PixelWave is a fictional learning event.</p>
<a href="#top">Back to top</a>
</footer>
What does the footer add?
- The footer element marks the closing information for the page.
- The Back to top link provides a direct route to body id="top".
- Save index.html.
- Switch back to Microsoft Edge.
- Click Refresh in the toolbar.
- Select Back to top at the bottom of the page.
You will return to the navigation at the top of the page.
Back to top link not working?
- Confirm that the footer link uses href="#top".
- Confirm that the opening body tag includes id="top".
- Still stuck? Help me repair the Back to top link.
Add the hero and About content
The structure now has working destinations. The hero gives visitors the event name, a short promise, a schedule link, and a festival pass they can recognize immediately.
- In index.html, place your cursor after the closing </nav> tag.
- Add the hero content by pasting this code:
<div class="hero-content">
<div>
<p class="eyebrow">A one-day online festival</p>
<h1>PixelWave Web Festival</h1>
<p class="hero-copy">
Build boldly, learn openly, and celebrate the web with
<strong>practical sessions</strong>, <em>learner-made demos</em>, and
<mark>free entry</mark>.
</p>
<a class="button-link" href="#schedule">Explore the schedule</a>
</div>
<div class="ticket-badge">
<span>Festival pass</span>
<strong>PW-01</strong>
</div>
</div>
What does the hero demonstrate?
- The h1 supplies the page's primary heading.
- The strong element marks practical sessions as important.
- The em element adds emphasis to learner-made demos.
- The mark element highlights free entry as relevant information.
- Save index.html.
- Switch back to Microsoft Edge.
- Click Refresh in the toolbar.
You will see the PixelWave Web Festival heading, the event description, the Explore the schedule link, and the PW-01 festival pass.
Hero content in the wrong place?
- Check that div.hero-content appears after nav.site-nav.
- Check that both elements remain inside header.hero.
- Still stuck? Help me fix the PixelWave hero structure.
The About section explains what the festival reviews through text-level semantics. It also gives visitors a descriptive route to MDN Web Docs for further learning.
- Find the empty section with id="about".
- Place your cursor between its opening and closing tags.
- Add the About content by pasting this code:
<div class="section-heading">
<p class="eyebrow">About the event</p>
<h2>A compact tour of the modern web</h2>
</div>
<p>
PixelWave reviews how <abbr title="HyperText Markup Language">HTML</abbr>
gives content meaning and how CSS turns that structure into a responsive
visual experience.
</p>
<ul class="feature-list">
<li>Semantic content and useful navigation</li>
<li>Responsive layouts, tables, and media</li>
<li>Accessible forms, focus states, and motion</li>
</ul>
<blockquote>
“The best frontend reviews end with something you can open, test, and
show.”
</blockquote>
<p>
Continue exploring standards and examples at
<a href="https://developer.mozilla.org/en-US/">MDN Web Docs</a>.
</p>
What does the About markup demonstrate?
- The abbr element expands HTML to HyperText Markup Language through its title attribute.
- The feature list groups three related festival topics.
- The blockquote element identifies a longer quotation.
- The final anchor uses descriptive link text to name its MDN Web Docs destination.
- Save index.html.
- Switch back to Microsoft Edge.
- Click Refresh in the toolbar.
You will see the About heading, the expanded HTML abbreviation, a three-item feature list, a quotation, and the MDN Web Docs link.
About content not rendering correctly?
- Check that the About content sits between the opening and closing tags for section#about.
- Check that every paragraph, list, quotation, and link has a matching closing tag.
- Still stuck? Help me find the HTML nesting problem in my About section.
Test the fragment links
The page now has meaningful content behind every navigation destination. Before you test the navigation, do you expect each link to land on a visible matching heading?
- Save index.html.
- Switch back to Microsoft Edge.
- Click Refresh in the toolbar.
- Select About in the navigation.
- Select Schedule in the navigation.
- Select Media in the navigation.
- Select RSVP in the navigation.
- Select Back to top in the footer.
Each link moves to its matching About, Schedule, Media, or RSVP heading. Back to top returns you to the PixelWave navigation.
✔️ Awesome, I've got everything!
Your semantic festival page is in place. Every fragment link now has a matching destination.
ⓧ I'd like to double check the full code
The full index.html for this step appears below.
<!doctype html>
<html lang="en-US">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width" />
<title>PixelWave Web Festival</title>
<link rel="stylesheet" href="styles.css" />
</head>
<body id="top">
<header class="hero">
<nav class="site-nav">
<a class="brand" href="#top">PixelWave</a>
<ul>
<li><a href="#about">About</a></li>
<li><a href="#schedule">Schedule</a></li>
<li><a href="#media">Media</a></li>
<li><a href="#rsvp">RSVP</a></li>
</ul>
</nav>
<div class="hero-content">
<div>
<p class="eyebrow">A one-day online festival</p>
<h1>PixelWave Web Festival</h1>
<p class="hero-copy">
Build boldly, learn openly, and celebrate the web with
<strong>practical sessions</strong>, <em>learner-made demos</em>, and
<mark>free entry</mark>.
</p>
<a class="button-link" href="#schedule">Explore the schedule</a>
</div>
<div class="ticket-badge">
<span>Festival pass</span>
<strong>PW-01</strong>
</div>
</div>
</header>
<main id="main-content">
<section id="about" class="panel">
<div class="section-heading">
<p class="eyebrow">About the event</p>
<h2>A compact tour of the modern web</h2>
</div>
<p>
PixelWave reviews how <abbr title="HyperText Markup Language">HTML</abbr>
gives content meaning and how CSS turns that structure into a responsive
visual experience.
</p>
<ul class="feature-list">
<li>Semantic content and useful navigation</li>
<li>Responsive layouts, tables, and media</li>
<li>Accessible forms, focus states, and motion</li>
</ul>
<blockquote>
“The best frontend reviews end with something you can open, test, and
show.”
</blockquote>
<p>
Continue exploring standards and examples at
<a href="https://developer.mozilla.org/en-US/">MDN Web Docs</a>.
</p>
</section>
<section id="schedule" class="panel">
<div class="section-heading">
<p class="eyebrow">Festival schedule</p>
<h2>Pick your learning track</h2>
</div>
</section>
<section id="media" class="panel">
<div class="section-heading">
<p class="eyebrow">Media stage</p>
<h2>Test native browser playback</h2>
</div>
</section>
<section id="rsvp" class="panel">
<div class="section-heading">
<p class="eyebrow">RSVP practice</p>
<h2>Test an accessible form</h2>
</div>
</section>
</main>
<footer>
<p>PixelWave is a fictional learning event.</p>
<a href="#top">Back to top</a>
</footer>
</body>
</html>
The linked styles.css file remains empty at this stage.
That is the semantic foundation locked in. Next up, you will add the festival schedule plus browser-controlled audio and video.
Add the Schedule and Media Stage
Your HTML structure now gives every navigation link a meaningful destination. The Schedule and Media links still lead to empty sections, so visitors cannot explore the timetable or test browser playback.
This step fills those sections with an accessible timetable and native media players. A four-column schedule can challenge narrow layouts, so you will give it a deliberate fixed width and observe how Microsoft Edge handles it before the responsive repair.
In this step, get ready to:
- Build a captioned schedule with scoped column headings.
- Embed an audio sample with a transcript.
- Add a multi-source video with English subtitles.
Add the accessible schedule
A timetable is tabular data because each session connects a time, format, and room. The caption names the table while scoped headers identify what each column represents.
- Switch back to index.html in Visual Studio Code.
- Locate <section id="schedule" class="panel">.
- Place your cursor below the existing section-heading closing tag.
- Add the festival timetable by pasting this code:
<table class="schedule-table" style="min-width: 760px">
<caption>PixelWave session timetable</caption>
<thead>
<tr>
<th scope="col">Time</th>
<th scope="col">Session</th>
<th scope="col">Format</th>
<th scope="col">Room</th>
</tr>
</thead>
<tbody>
<tr>
<td><time datetime="10:00">10:00</time></td>
<td>Semantic HTML that explains itself</td>
<td>Workshop</td>
<td>Markup Hall</td>
</tr>
<tr>
<td><time datetime="11:30">11:30</time></td>
<td>Responsive CSS without layout panic</td>
<td>Live build</td>
<td>Style Stage</td>
</tr>
<tr>
<td><time datetime="13:30">13:30</time></td>
<td>Forms people can finish</td>
<td>Review</td>
<td>Input Lab</td>
</tr>
<tr><td><time datetime="15:00">15:00</time></td><td>Accessible media and motion</td><td>Demo</td><td>Media Deck</td></tr>
</tbody>
</table>
What does this table structure do?
- The caption gives the timetable a visible description.
- Each scope="col" value associates a heading with the cells beneath it.
- The <time> elements give the session times machine-readable values.
- The temporary style="min-width: 760px" value keeps all four columns wide enough to expose the narrow-layout problem.
- Save index.html.
- Return to the local page in Microsoft Edge.
- Refresh the page.
- Select the Schedule navigation link.
You will see the PixelWave session timetable with four labeled columns and four session rows. That is the schedule working as structured tabular content.
Schedule missing or outside its section?
- Confirm that the table sits inside section#schedule before its closing </section> tag.
- Check that every opening table tag has its matching closing tag.
- Still stuck? Help me check my PixelWave schedule table structure.
Embed the audio sample
The browser can provide playback controls when an audio element includes the controls attribute. This player streams its sample from MDN Web Docs and keeps a download link available as fallback content.
- Locate <section id="media" class="panel"> in index.html.
- Place your cursor below the existing section-heading closing tag.
- Add the media grid with its audio card by pasting this code:
<div class="media-grid">
<article class="media-card">
<h3>Audio sound check</h3>
<audio controls preload="metadata">
<source
src="https://github.com/mdn/learning-area/raw/refs/heads/main/html/multimedia-and-embedding/tasks/media-embed/media/audio.mp3"
type="audio/mpeg" />
<a
href="https://github.com/mdn/learning-area/raw/refs/heads/main/html/multimedia-and-embedding/tasks/media-embed/media/audio.mp3">
Download the audio sample
</a>
</audio>
<p>Transcript: A short demonstration sound plays.</p>
</article>
</div>
How does the audio card work?
- The controls attribute asks Edge to display its native playback controls.
- The source element identifies the MP3 file and its media type.
- The fallback link gives visitors a direct route to the sample when embedded playback is unavailable.
- The transcript describes the short sound demonstration in text.
- Save index.html.
- Return to Microsoft Edge.
- Refresh the page.
- Select the Media navigation link.
- Start the audio using its browser controls.
You will hear the short sample play. You will also see its transcript directly below the player.
Audio not playing?
- Confirm that your internet connection is available because the sample streams from the MDN repository.
- Check that the complete audio URL remains inside the src attribute.
- Still silent? Help me troubleshoot the PixelWave audio player.
Add the captioned video and test overflow
Multiple video sources let the browser choose a supported format. A subtitle track adds timed text that visitors can enable from the native controls.
- Locate the closing </div> for .media-grid.
- Place your cursor above that closing tag.
- Add the video card by pasting this code:
<article class="media-card">
<h3>Video stage preview</h3>
<video controls width="320" height="240" muted preload="metadata">
<source
src="https://github.com/mdn/learning-area/raw/refs/heads/main/html/multimedia-and-embedding/tasks/media-embed/media/video.mp4"
type="video/mp4" />
<source
src="https://github.com/mdn/learning-area/raw/refs/heads/main/html/multimedia-and-embedding/tasks/media-embed/media/video.webm"
type="video/webm" />
<track
kind="subtitles"
src="https://raw.githubusercontent.com/mdn/learning-area/refs/heads/main/html/multimedia-and-embedding/tasks/media-embed/media/subtitles_en.vtt"
srclang="en"
label="English" />
<a
href="https://github.com/mdn/learning-area/raw/refs/heads/main/html/multimedia-and-embedding/tasks/media-embed/media/video.mp4">
Download the MP4 video
</a>
</video>
</article>
How does the video choose its content?
- Edge tries the MP4 source before the WebM source.
- The track element supplies English subtitles through a WebVTT file.
- The muted attribute starts the player without audio output.
- The fallback link lets visitors download the MP4 when embedded playback is unavailable.
- Save index.html.
- Return to Microsoft Edge.
- Refresh the page.
- Start the video using its browser controls.
- Enable the English subtitle track from the video controls.
You will see the sample video play with its English subtitle track available. Your media stage now demonstrates both native audio and multi-source video playback.
Video or subtitles unavailable?
- Confirm that your internet connection is available for the MP4, WebM, and subtitle files.
- Check that the track element remains inside the video element.
- Need another pair of eyes? Help me troubleshoot the PixelWave video and subtitles.
✔️ Awesome, I've got everything!
Great work. Save index.html once more before the final narrow-window check.
Your linked styles.css file remains empty at this point.
ⓧ I'd like to double check the full code
Compare your cumulative index.html with this version. Your linked styles.css file should still be empty.
<!doctype html>
<html lang="en-US">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width" />
<title>PixelWave Web Festival</title>
<link rel="stylesheet" href="styles.css" />
</head>
<body id="top">
<header class="hero">
<nav class="site-nav">
<a class="brand" href="#top">PixelWave</a>
<ul>
<li><a href="#about">About</a></li>
<li><a href="#schedule">Schedule</a></li>
<li><a href="#media">Media</a></li>
<li><a href="#rsvp">RSVP</a></li>
</ul>
</nav>
<div class="hero-content">
<div>
<p class="eyebrow">A one-day online festival</p>
<h1>PixelWave Web Festival</h1>
<p class="hero-copy">
Build boldly, learn openly, and celebrate the web with
<strong>practical sessions</strong>, <em>learner-made demos</em>, and
<mark>free entry</mark>.
</p>
<a class="button-link" href="#schedule">Explore the schedule</a>
</div>
<div class="ticket-badge">
<span>Festival pass</span>
<strong>PW-01</strong>
</div>
</div>
</header>
<main id="main-content">
<section id="about" class="panel">
<div class="section-heading">
<p class="eyebrow">About the event</p>
<h2>A compact tour of the modern web</h2>
</div>
<p>
PixelWave reviews how <abbr title="HyperText Markup Language">HTML</abbr>
gives content meaning and how CSS turns that structure into a responsive
visual experience.
</p>
<ul class="feature-list">
<li>Semantic content and useful navigation</li>
<li>Responsive layouts, tables, and media</li>
<li>Accessible forms, focus states, and motion</li>
</ul>
<blockquote>
“The best frontend reviews end with something you can open, test, and
show.”
</blockquote>
<p>
Continue exploring standards and examples at
<a href="https://developer.mozilla.org/en-US/">MDN Web Docs</a>.
</p>
</section>
<section id="schedule" class="panel">
<div class="section-heading">
<p class="eyebrow">Festival schedule</p>
<h2>Pick your learning track</h2>
</div>
<table class="schedule-table" style="min-width: 760px">
<caption>
PixelWave session timetable
</caption>
<thead>
<tr>
<th scope="col">Time</th>
<th scope="col">Session</th>
<th scope="col">Format</th>
<th scope="col">Room</th>
</tr>
</thead>
<tbody>
<tr>
<td><time datetime="10:00">10:00</time></td>
<td>Semantic HTML that explains itself</td>
<td>Workshop</td>
<td>Markup Hall</td>
</tr>
<tr>
<td><time datetime="11:30">11:30</time></td>
<td>Responsive CSS without layout panic</td>
<td>Live build</td>
<td>Style Stage</td>
</tr>
<tr>
<td><time datetime="13:30">13:30</time></td>
<td>Forms people can finish</td>
<td>Review</td>
<td>Input Lab</td>
</tr>
<tr>
<td><time datetime="15:00">15:00</time></td>
<td>Accessible media and motion</td>
<td>Demo</td>
<td>Media Deck</td>
</tr>
</tbody>
</table>
</section>
<section id="media" class="panel">
<div class="section-heading">
<p class="eyebrow">Media stage</p>
<h2>Test native browser playback</h2>
</div>
<div class="media-grid">
<article class="media-card">
<h3>Audio sound check</h3>
<audio controls preload="metadata">
<source
src="https://github.com/mdn/learning-area/raw/refs/heads/main/html/multimedia-and-embedding/tasks/media-embed/media/audio.mp3"
type="audio/mpeg" />
<a
href="https://github.com/mdn/learning-area/raw/refs/heads/main/html/multimedia-and-embedding/tasks/media-embed/media/audio.mp3">
Download the audio sample
</a>
</audio>
<p>Transcript: A short demonstration sound plays.</p>
</article>
<article class="media-card">
<h3>Video stage preview</h3>
<video controls width="320" height="240" muted preload="metadata">
<source
src="https://github.com/mdn/learning-area/raw/refs/heads/main/html/multimedia-and-embedding/tasks/media-embed/media/video.mp4"
type="video/mp4" />
<source
src="https://github.com/mdn/learning-area/raw/refs/heads/main/html/multimedia-and-embedding/tasks/media-embed/media/video.webm"
type="video/webm" />
<track
kind="subtitles"
src="https://raw.githubusercontent.com/mdn/learning-area/refs/heads/main/html/multimedia-and-embedding/tasks/media-embed/media/subtitles_en.vtt"
srclang="en"
label="English" />
<a
href="https://github.com/mdn/learning-area/raw/refs/heads/main/html/multimedia-and-embedding/tasks/media-embed/media/video.mp4">
Download the MP4 video
</a>
</video>
</article>
</div>
</section>
<section id="rsvp" class="panel">
<div class="section-heading">
<p class="eyebrow">RSVP practice</p>
<h2>Test an accessible form</h2>
</div>
</section>
</main>
<footer>
<p>PixelWave is a fictional learning event.</p>
<a href="#top">Back to top</a>
</footer>
</body>
</html>
Before you resize, predict what happens to the four-column schedule inside a narrow page.
- Drag the edge of the Microsoft Edge window inward until the page becomes narrow.
You will see the fixed-width schedule push beyond the page. This visible overflow is the intended limitation of the current version.
Your festival page now has a structured timetable plus playable audio and captioned video. Next, you will contain that overflow and turn the plain page into a responsive animated design.
Make the Experience Responsive and Animated
Your festival page now has a working schedule plus playable media. A narrow Microsoft Edge window also exposes the schedule pushing the entire page sideways.
This step uses responsive CSS to contain that overflow. You will also turn the plain document into a cohesive festival design with responsive layouts plus motion that respects the visitor's preferences.
In this step, get ready to:
- Contain the wide schedule inside its own horizontal scroller.
- Style the festival for wide screens plus narrow screens.
- Animate the festival pass while respecting reduced motion.
Contain the schedule overflow
The schedule needs a minimum width so every column remains readable. A scroll wrapper contains that width inside the schedule panel instead of letting it stretch the whole page.
- Switch back to index.html in the Explorer view of Visual Studio Code.
- Find <table class="schedule-table" style="min-width: 760px"> by pressing Ctrl+F.
- Remove only style="min-width: 760px" from the opening table tag.
- Type <div class="schedule-scroll"> on the line immediately above the table.
- Type </div> on the line immediately after the closing </table> tag.
- Save index.html.
- Refresh the page in Edge.
- Narrow the Edge window until the schedule exceeds its panel.
You can now scroll the timetable inside the schedule area. The rest of the page stays within the narrow window.
Why does the wrapper fix overflow?
The table keeps enough width for its four columns. The surrounding .schedule-scroll element becomes the boundary that handles any extra width.
This preserves readable table cells without making the entire document horizontally scrollable.
Does the whole page still scroll sideways?
- Check that the opening wrapper appears immediately before <table class="schedule-table">.
- Check that the closing </div> appears immediately after </table>.
- Still stuck? Help me check the schedule wrapper in my PixelWave HTML..
✔️ Awesome, I've got everything!
Great. Your schedule now has its own boundary for narrow-screen overflow.
ⓧ I'd like to double check the full code
- Compare your current index.html with this reference:
<!doctype html>
<html lang="en-US">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width" />
<title>PixelWave Web Festival</title>
<link rel="stylesheet" href="styles.css" />
</head>
<body id="top">
<header class="hero">
<nav class="site-nav">
<a class="brand" href="#top">PixelWave</a>
<ul>
<li><a href="#about">About</a></li>
<li><a href="#schedule">Schedule</a></li>
<li><a href="#media">Media</a></li>
<li><a href="#rsvp">RSVP</a></li>
</ul>
</nav>
<div class="hero-content">
<div>
<p class="eyebrow">A one-day online festival</p>
<h1>PixelWave Web Festival</h1>
<p class="hero-copy">
Build boldly, learn openly, and celebrate the web with
<strong>practical sessions</strong>, <em>learner-made demos</em>, and
<mark>free entry</mark>.
</p>
<a class="button-link" href="#schedule">Explore the schedule</a>
</div>
<div class="ticket-badge">
<span>Festival pass</span>
<strong>PW-01</strong>
</div>
</div>
</header>
<main id="main-content">
<section id="about" class="panel">
<div class="section-heading">
<p class="eyebrow">About the event</p>
<h2>A compact tour of the modern web</h2>
</div>
<p>
PixelWave reviews how <abbr title="HyperText Markup Language">HTML</abbr>
gives content meaning and how CSS turns that structure into a responsive
visual experience.
</p>
<ul class="feature-list">
<li>Semantic content and useful navigation</li>
<li>Responsive layouts, tables, and media</li>
<li>Accessible forms, focus states, and motion</li>
</ul>
<blockquote>
“The best frontend reviews end with something you can open, test, and
show.”
</blockquote>
<p>
Continue exploring standards and examples at
<a href="https://developer.mozilla.org/en-US/">MDN Web Docs</a>.
</p>
</section>
<section id="schedule" class="panel">
<div class="section-heading">
<p class="eyebrow">Festival schedule</p>
<h2>Pick your learning track</h2>
</div>
<div class="schedule-scroll">
<table class="schedule-table">
<caption>
PixelWave session timetable
</caption>
<thead>
<tr>
<th scope="col">Time</th>
<th scope="col">Session</th>
<th scope="col">Format</th>
<th scope="col">Room</th>
</tr>
</thead>
<tbody>
<tr>
<td><time datetime="10:00">10:00</time></td>
<td>Semantic HTML that explains itself</td>
<td>Workshop</td>
<td>Markup Hall</td>
</tr>
<tr>
<td><time datetime="11:30">11:30</time></td>
<td>Responsive CSS without layout panic</td>
<td>Live build</td>
<td>Style Stage</td>
</tr>
<tr>
<td><time datetime="13:30">13:30</time></td>
<td>Forms people can finish</td>
<td>Review</td>
<td>Input Lab</td>
</tr>
<tr>
<td><time datetime="15:00">15:00</time></td>
<td>Accessible media and motion</td>
<td>Demo</td>
<td>Media Deck</td>
</tr>
</tbody>
</table>
</div>
</section>
<section id="media" class="panel">
<div class="section-heading">
<p class="eyebrow">Media stage</p>
<h2>Test native browser playback</h2>
</div>
<div class="media-grid">
<article class="media-card">
<h3>Audio sound check</h3>
<audio controls preload="metadata">
<source
src="https://github.com/mdn/learning-area/raw/refs/heads/main/html/multimedia-and-embedding/tasks/media-embed/media/audio.mp3"
type="audio/mpeg" />
<a
href="https://github.com/mdn/learning-area/raw/refs/heads/main/html/multimedia-and-embedding/tasks/media-embed/media/audio.mp3">
Download the audio sample
</a>
</audio>
<p>Transcript: A short demonstration sound plays.</p>
</article>
<article class="media-card">
<h3>Video stage preview</h3>
<video controls width="320" height="240" muted preload="metadata">
<source
src="https://github.com/mdn/learning-area/raw/refs/heads/main/html/multimedia-and-embedding/tasks/media-embed/media/video.mp4"
type="video/mp4" />
<source
src="https://github.com/mdn/learning-area/raw/refs/heads/main/html/multimedia-and-embedding/tasks/media-embed/media/video.webm"
type="video/webm" />
<track
kind="subtitles"
src="https://raw.githubusercontent.com/mdn/learning-area/refs/heads/main/html/multimedia-and-embedding/tasks/media-embed/media/subtitles_en.vtt"
srclang="en"
label="English" />
<a
href="https://github.com/mdn/learning-area/raw/refs/heads/main/html/multimedia-and-embedding/tasks/media-embed/media/video.mp4">
Download the MP4 video
</a>
</video>
</article>
</div>
</section>
<section id="rsvp" class="panel">
<div class="section-heading">
<p class="eyebrow">RSVP practice</p>
<h2>Test an accessible form</h2>
</div>
</section>
</main>
<footer>
<p>PixelWave is a fictional learning event.</p>
<a href="#top">Back to top</a>
</footer>
</body>
</html>
This reference includes the finished schedule wrapper. The RSVP section remains a shell for the next step.
Build the responsive visual system
The stylesheet starts with shared colors plus global layout rules. Building the design in layers keeps each visual change easy to test.
- Switch back to the empty styles.css file.
- Add the color system plus page foundation by pasting this code:
:root {
--page: #081120;
--panel: #101f38;
--panel-light: #172b4d;
--text: #f6f8ff;
--muted: #b9c5da;
--accent: #62e6c8;
--accent-dark: #062b25;
--highlight: #ffd166;
--line: #385173;
}
* {
box-sizing: border-box;
}
body {
margin: 0;
color: var(--text);
background: var(--page);
font-family: system-ui, sans-serif;
line-height: 1.6;
}
What does this CSS do?
- The custom properties hold the colors used throughout the festival page.
- Global border-box sizing keeps padding inside each element's declared width.
- The body rules create the dark canvas plus readable default typography.
- Save styles.css.
- Refresh Edge.
You should see a dark navy background with light text across the page.
Still seeing a white page?
- Check that styles.css is saved inside pixelwave-festival.
- Check that the file begins with :root.
- Still stuck? Help me connect my PixelWave stylesheet..
- Add link styling plus visible focus foundations below the body rule by pasting:
a {
color: var(--accent);
}
a:hover {
text-decoration-thickness: 0.2rem;
}
:focus-visible {
outline: 3px solid var(--highlight);
outline-offset: 4px;
}
.skip-link {
position: absolute;
top: 0.75rem;
left: 0.75rem;
z-index: 10;
padding: 0.7rem 1rem;
color: var(--page);
background: var(--highlight);
transform: translateY(-160%);
}
How do these interaction styles help?
- Links use the accent color so visitors can distinguish them from body text.
- The :focus-visible outline makes keyboard focus easy to follow.
- The skip-link rules prepare a hidden shortcut that the next step adds to the page.
- Save styles.css.
- Refresh Edge.
- Press Tab to move focus through the existing links.
You should see mint links plus a yellow outline around the focused link.
Can't see the focus outline?
- Use the keyboard to move focus instead of selecting a link with the mouse.
- Check that the selector includes the leading colon in :focus-visible.
- Still stuck? Help me debug the PixelWave focus outline..
- Add the hero plus navigation layout below the skip-link rule by pasting:
.skip-link:focus {
transform: translateY(0);
}
.hero {
padding: 1.25rem clamp(1rem, 4vw, 4rem) 4rem;
background: linear-gradient(135deg, #142a4f, #4d1d6d);
}
.site-nav {
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: space-between;
gap: 1rem;
max-width: 70rem;
margin: 0 auto 4rem;
}
.site-nav ul {
display: flex;
flex-wrap: wrap;
gap: 1rem;
padding: 0;
margin: 0;
list-style: none;
}
How is the header arranged?
- The hero uses responsive padding plus a diagonal color gradient.
- Flexbox places the brand beside the navigation when space permits.
- Wrapping keeps the navigation links inside narrower windows.
- Save styles.css.
- Refresh Edge.
You should see the page header become a purple-blue hero with a flexible navigation row.
Is the navigation still a vertical list?
- Check that display: flex is inside .site-nav ul.
- Check every closing brace around the hero plus navigation rules.
- Still stuck? Help me repair the PixelWave navigation CSS..
- Style the brand plus hero content below the navigation list by pasting:
.site-nav a {
color: var(--text);
}
.brand {
font-size: 1.35rem;
font-weight: 800;
text-decoration: none;
}
.hero-content {
display: grid;
gap: 2rem;
align-items: center;
max-width: 70rem;
margin: 0 auto;
}
What changes in the hero?
- Navigation links become light enough to stand out against the gradient.
- The brand gains a stronger visual weight.
- The hero content becomes a grid that can gain a second column later.
- Save styles.css.
- Refresh Edge.
You should see a larger PixelWave brand plus consistent spacing between the hero copy and festival pass.
Does the hero look cramped?
- Check that gap: 2rem is inside .hero-content.
- Check that the navigation rules close before .hero-content begins.
- Still stuck? Help me check the PixelWave hero layout..
- Add the festival typography below the hero content rules by pasting:
h1,
h2,
h3,
p {
margin-top: 0;
}
h1 {
max-width: 12ch;
margin-bottom: 1rem;
font-size: clamp(2.7rem, 8vw, 5.5rem);
line-height: 0.95;
}
.hero-copy {
max-width: 42rem;
color: var(--muted);
font-size: 1.15rem;
}
.eyebrow {
margin-bottom: 0.5rem;
color: var(--accent);
font-size: 0.8rem;
font-weight: 800;
letter-spacing: 0.14em;
text-transform: uppercase;
}
How does the type scale work?
- The heading size grows with the viewport while staying inside minimum plus maximum limits.
- The hero copy uses a muted color to preserve the heading's hierarchy.
- Eyebrow text uses compact uppercase lettering as a section label.
- Save styles.css.
- Refresh Edge.
You should see a large responsive festival heading plus mint uppercase labels above each section title.
Is the main heading still small?
- Check that the heading selector is h1.
- Check that the clamp() value includes all three sizes.
- Still stuck? Help me debug the PixelWave typography..
- Style highlighted text plus buttons below the eyebrow rules by pasting:
mark {
padding: 0.05em 0.25em;
color: var(--page);
background: var(--highlight);
}
.button-link,
button {
display: inline-block;
border: 0;
border-radius: 999px;
padding: 0.8rem 1.2rem;
color: var(--accent-dark);
background: var(--accent);
font: inherit;
font-weight: 800;
cursor: pointer;
}
Why share the button style?
The schedule link already behaves like a call-to-action. The shared selector gives the future RSVP button the same shape plus visual weight.
- Save styles.css.
- Refresh Edge.
You should see free entry highlighted in yellow plus a rounded mint schedule button.
Does the schedule link still look plain?
- Check that the selector begins with .button-link.
- Check that the comma remains between the two selectors.
- Still stuck? Help me fix the PixelWave button styles..
- Turn the festival pass into a ticket badge below the button rules by pasting:
.ticket-badge {
justify-self: start;
min-width: 11rem;
padding: 1.25rem;
border: 2px dashed var(--page);
border-radius: 1rem;
color: var(--page);
background: var(--highlight);
box-shadow: 0.65rem 0.65rem 0 var(--accent);
animation: 1.8s ease-in-out infinite alternate float-pass;
}
.ticket-badge span,
.ticket-badge strong {
display: block;
}
.ticket-badge strong {
margin-top: 0.25rem;
font-size: 2rem;
}
What creates the ticket effect?
- A dashed border plus offset shadow gives the pass a printed-ticket shape.
- Block display places the pass label above its code.
- The animation declaration connects the badge to the keyframes added later in this step.
- Save styles.css.
- Refresh Edge.
You should see Festival pass plus PW-01 inside a yellow ticket with a mint shadow.
Is the festival pass still bare text?
- Check that the class selector is .ticket-badge.
- Check that the badge rules appear after the shared button rules.
- Still stuck? Help me style the PixelWave ticket badge..
- Create the centered content area plus reusable panels below the ticket rules by pasting:
main {
width: min(70rem, calc(100% - 2rem));
margin: -2rem auto 0;
}
.panel {
margin-bottom: 1.25rem;
padding: clamp(1.25rem, 4vw, 2.5rem);
border: 1px solid var(--line);
border-radius: 1.25rem;
background: var(--panel);
}
.section-heading {
margin-bottom: 1.5rem;
}
.feature-list {
padding-left: 1.25rem;
}
How do the panels shape the page?
- The main area stays centered while retaining space beside narrow screens.
- Each panel gains internal spacing plus a subtle border.
- The negative top margin overlaps the first panel with the hero.
- Save styles.css.
- Refresh Edge.
You should see About, Schedule, Media, and RSVP inside separate rounded panels.
Are the sections still edge to edge?
- Check that the width calculation belongs to the main selector.
- Check that every section still has class="panel" in index.html.
- Still stuck? Help me debug the PixelWave panels..
- Style the quotation plus schedule container below the feature list by pasting:
blockquote {
margin: 1.5rem 0;
padding-left: 1rem;
border-left: 0.3rem solid var(--highlight);
color: var(--muted);
font-size: 1.1rem;
}
.schedule-scroll {
overflow-x: auto;
}
.schedule-table {
width: 100%;
min-width: 42rem;
border-collapse: collapse;
}
How is the schedule contained?
- The quotation gains a yellow edge plus muted text.
- The table keeps enough width to preserve readable columns.
- The wrapper becomes horizontally scrollable only when the table exceeds its width.
- Save styles.css.
- Refresh Edge.
- Narrow the Edge window.
You should see the table remain wide inside a schedule-only horizontal scroller. The full page should stay inside the window.
Is the schedule missing its own scrollbar?
- Check that overflow-x: auto belongs to .schedule-scroll.
- Check that the table still sits inside the schedule wrapper.
- Still stuck? Help me contain the PixelWave schedule overflow..
- Finish the timetable styling below the schedule table rule by pasting:
caption {
padding-bottom: 0.75rem;
color: var(--muted);
text-align: left;
}
th,
td {
padding: 0.85rem;
border-bottom: 1px solid var(--line);
text-align: left;
}
th {
color: var(--accent);
background: var(--panel-light);
}
How does the table become easier to scan?
- The caption stays visible above the timetable as its description.
- Cell padding separates each value from its borders.
- Mint column headings stand out against a lighter panel color.
- Save styles.css.
- Refresh Edge.
You should see padded timetable rows plus mint headers on a lighter background.
Are the table headings unstyled?
- Check that the heading cells use <th> in index.html.
- Check that the th rule closes after its background declaration.
- Still stuck? Help me debug the PixelWave table styling..
- Make the media stage responsive below the table styles by pasting:
.media-grid {
display: grid;
gap: 1rem;
}
.media-card {
min-width: 0;
padding: 1.25rem;
border-radius: 1rem;
background: var(--panel-light);
}
audio,
video {
display: block;
width: 100%;
max-width: 100%;
}
video {
height: auto;
border-radius: 0.75rem;
}
Why can the media now shrink?
- The media cards use a grid that begins with one column.
- The max-width: 100% rule prevents either player from exceeding its card.
- Automatic video height preserves the video's proportions as its width changes.
- Save styles.css.
- Refresh Edge.
- Narrow the Edge window.
You should see both media players stay inside their cards. The video should shrink without becoming distorted.
Does the video still escape its card?
- Check that both width: 100% plus max-width: 100% are inside the shared media rule.
- Check that height: auto is inside the video rule.
- Still stuck? Help me make the PixelWave media responsive..
- Prepare the RSVP layout below the video rule by pasting:
form {
display: grid;
gap: 1rem;
max-width: 42rem;
}
.form-field {
display: grid;
gap: 0.35rem;
}
label {
font-weight: 700;
}
input,
select,
textarea {
width: 100%;
border: 1px solid var(--line);
border-radius: 0.6rem;
padding: 0.75rem;
color: var(--text);
background: var(--page);
font: inherit;
}
Why add the form styles now?
The next step adds the RSVP controls to the existing section. These rules keep every field aligned with the page's responsive width plus color system.
- Save styles.css.
- Find .form-field in the stylesheet by pressing Ctrl+F.
You should find the saved form layout directly below the video rules.
Can't find the form rules?
- Check that you pasted the rules into styles.css.
- Check that .form-field includes its leading period.
- Still stuck? Help me check the PixelWave form CSS..
- Finish the control plus footer styling below the input rules by pasting:
textarea {
resize: vertical;
}
button {
justify-self: start;
}
.form-note {
margin-top: 1rem;
color: var(--muted);
}
footer {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
gap: 1rem;
width: min(70rem, calc(100% - 2rem));
margin: 0 auto;
padding: 1.5rem 0 3rem;
color: var(--muted);
}
What do these finishing rules cover?
- The future message field can grow vertically without stretching sideways.
- The future submit button stays aligned to the start of the form.
- The footer uses the same centered width as the main content.
- Save styles.css.
- Refresh Edge.
You should see the footer align with the panels. Its text plus Back to top link should wrap cleanly when needed.
Is the footer wider than the panels?
- Check that the footer width matches the main width.
- Check that the footer margin is 0 auto.
- Still stuck? Help me align the PixelWave footer..
The page begins as a single-column layout. A wider-screen CSS Grid rule adds columns only when the window has enough space.
- Add the wider-screen layout below the footer rule by pasting:
@media (width >= 48rem) {
.hero-content {
grid-template-columns: minmax(0, 1fr) auto;
}
.ticket-badge {
justify-self: end;
}
.media-grid {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
}
Why wait before adding columns?
The page keeps one column below 48rem. Wider windows gain a hero copy column plus a ticket column.
The media stage also gains two flexible columns. Each column can still shrink without forcing overflow.
- Save styles.css.
- Refresh Edge.
- Widen the Edge window beyond the layout breakpoint.
You should see the festival pass move beside the hero copy. The audio plus video cards should form two columns.
Do the wider layouts stay in one column?
- Check that the media query begins with @media (width >= 48rem).
- Check that both grid column declarations sit inside the media query.
- Still stuck? Help me debug the PixelWave media query..
Animate the festival pass responsibly
CSS animations can draw attention to the festival pass without changing the page's content. A reduced-motion preference turns off that movement for visitors who request a still experience.
- Add the reduced-motion override plus animation keyframes below the wider-screen media query by pasting:
@media (prefers-reduced-motion: reduce) {
.ticket-badge {
animation: none;
}
}
@keyframes float-pass {
from {
transform: translateY(0) rotate(-1deg);
}
to {
transform: translateY(-0.6rem) rotate(1deg);
}
}
How does the motion stay accessible?
- The keyframes move the pass upward slightly while rotating it by a small amount.
- The badge animation alternates between the two states.
- The reduced-motion media query removes the animation when the operating system requests less motion.
- Save styles.css.
- Refresh Edge.
You should see the yellow festival pass float gently when reduced motion is not requested.
Is the festival pass staying still?
- Check that float-pass is spelled the same way in the animation declaration plus keyframes rule.
- Check whether Windows currently requests reduced motion.
- Still stuck? Help me debug the PixelWave ticket animation..
✔️ Awesome, I've got everything!
That's the visual system complete. Your festival now responds to window size plus motion preferences.
ⓧ I'd like to double check the full code
- Compare your complete styles.css with this reference:
:root {
--page: #081120;
--panel: #101f38;
--panel-light: #172b4d;
--text: #f6f8ff;
--muted: #b9c5da;
--accent: #62e6c8;
--accent-dark: #062b25;
--highlight: #ffd166;
--line: #385173;
}
* {
box-sizing: border-box;
}
body {
margin: 0;
color: var(--text);
background: var(--page);
font-family: system-ui, sans-serif;
line-height: 1.6;
}
a {
color: var(--accent);
}
a:hover {
text-decoration-thickness: 0.2rem;
}
:focus-visible {
outline: 3px solid var(--highlight);
outline-offset: 4px;
}
.skip-link {
position: absolute;
top: 0.75rem;
left: 0.75rem;
z-index: 10;
padding: 0.7rem 1rem;
color: var(--page);
background: var(--highlight);
transform: translateY(-160%);
}
.skip-link:focus {
transform: translateY(0);
}
.hero {
padding: 1.25rem clamp(1rem, 4vw, 4rem) 4rem;
background: linear-gradient(135deg, #142a4f, #4d1d6d);
}
.site-nav {
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: space-between;
gap: 1rem;
max-width: 70rem;
margin: 0 auto 4rem;
}
.site-nav ul {
display: flex;
flex-wrap: wrap;
gap: 1rem;
padding: 0;
margin: 0;
list-style: none;
}
.site-nav a {
color: var(--text);
}
.brand {
font-size: 1.35rem;
font-weight: 800;
text-decoration: none;
}
.hero-content {
display: grid;
gap: 2rem;
align-items: center;
max-width: 70rem;
margin: 0 auto;
}
h1,
h2,
h3,
p {
margin-top: 0;
}
h1 {
max-width: 12ch;
margin-bottom: 1rem;
font-size: clamp(2.7rem, 8vw, 5.5rem);
line-height: 0.95;
}
.hero-copy {
max-width: 42rem;
color: var(--muted);
font-size: 1.15rem;
}
.eyebrow {
margin-bottom: 0.5rem;
color: var(--accent);
font-size: 0.8rem;
font-weight: 800;
letter-spacing: 0.14em;
text-transform: uppercase;
}
mark {
padding: 0.05em 0.25em;
color: var(--page);
background: var(--highlight);
}
.button-link,
button {
display: inline-block;
border: 0;
border-radius: 999px;
padding: 0.8rem 1.2rem;
color: var(--accent-dark);
background: var(--accent);
font: inherit;
font-weight: 800;
cursor: pointer;
}
.ticket-badge {
justify-self: start;
min-width: 11rem;
padding: 1.25rem;
border: 2px dashed var(--page);
border-radius: 1rem;
color: var(--page);
background: var(--highlight);
box-shadow: 0.65rem 0.65rem 0 var(--accent);
animation: 1.8s ease-in-out infinite alternate float-pass;
}
.ticket-badge span,
.ticket-badge strong {
display: block;
}
.ticket-badge strong {
margin-top: 0.25rem;
font-size: 2rem;
}
main {
width: min(70rem, calc(100% - 2rem));
margin: -2rem auto 0;
}
.panel {
margin-bottom: 1.25rem;
padding: clamp(1.25rem, 4vw, 2.5rem);
border: 1px solid var(--line);
border-radius: 1.25rem;
background: var(--panel);
}
.section-heading {
margin-bottom: 1.5rem;
}
.feature-list {
padding-left: 1.25rem;
}
blockquote {
margin: 1.5rem 0;
padding-left: 1rem;
border-left: 0.3rem solid var(--highlight);
color: var(--muted);
font-size: 1.1rem;
}
.schedule-scroll {
overflow-x: auto;
}
.schedule-table {
width: 100%;
min-width: 42rem;
border-collapse: collapse;
}
caption {
padding-bottom: 0.75rem;
color: var(--muted);
text-align: left;
}
th,
td {
padding: 0.85rem;
border-bottom: 1px solid var(--line);
text-align: left;
}
th {
color: var(--accent);
background: var(--panel-light);
}
.media-grid {
display: grid;
gap: 1rem;
}
.media-card {
min-width: 0;
padding: 1.25rem;
border-radius: 1rem;
background: var(--panel-light);
}
audio,
video {
display: block;
width: 100%;
max-width: 100%;
}
video {
height: auto;
border-radius: 0.75rem;
}
form {
display: grid;
gap: 1rem;
max-width: 42rem;
}
.form-field {
display: grid;
gap: 0.35rem;
}
label {
font-weight: 700;
}
input,
select,
textarea {
width: 100%;
border: 1px solid var(--line);
border-radius: 0.6rem;
padding: 0.75rem;
color: var(--text);
background: var(--page);
font: inherit;
}
textarea {
resize: vertical;
}
button {
justify-self: start;
}
.form-note {
margin-top: 1rem;
color: var(--muted);
}
footer {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
gap: 1rem;
width: min(70rem, calc(100% - 2rem));
margin: 0 auto;
padding: 1.5rem 0 3rem;
color: var(--muted);
}
@media (width >= 48rem) {
.hero-content {
grid-template-columns: minmax(0, 1fr) auto;
}
.ticket-badge {
justify-self: end;
}
.media-grid {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
}
@media (prefers-reduced-motion: reduce) {
.ticket-badge {
animation: none;
}
}
@keyframes float-pass {
from {
transform: translateY(0) rotate(-1deg);
}
to {
transform: translateY(-0.6rem) rotate(1deg);
}
}
This reference is the complete stylesheet for the finished PixelWave page.
Before you run the final check, what do you expect to happen to the page, schedule, media, and festival pass as the window becomes narrower?
- Refresh Edge.
- Resize the Edge window from wide to narrow.
You should see the page switch from wider columns to a single-column layout. The document should remain free of horizontal scrolling.
The schedule should scroll only inside its wrapper. The video should shrink to fit its media card.
The festival pass should move gently unless reduced motion is requested. That's the responsive experience working as one system.
Your festival now stays polished across different window sizes while respecting motion preferences. Next up, you will complete the RSVP flow plus the page's keyboard accessibility features.
Finish the RSVP Form and Accessibility Pass
Your responsive PixelWave page already keeps its schedule and media usable across wide and narrow layouts. The final gap is the visitor flow for submitting an RSVP.
You'll complete the HTML form that Microsoft Edge validates in the browser. You'll also use the existing CSS to reveal a skip link and show visible keyboard focus.
In this step, get ready to:
- Build a labeled RSVP form with required fields.
- Add a skip link and local demonstration notice.
- Test browser validation and keyboard focus.
Build the RSVP form
Each label's for value matches its control's id. This match gives the control an accessible name.
The required attributes prevent empty submissions. The type="email" field also asks Edge to check the email format.
- In the Explorer sidebar in Visual Studio Code, select index.html.
- Find <section id="rsvp" class="panel"> near the bottom of the file.
- Locate the section heading that contains Test an accessible form.
- Add the RSVP controls below that section heading by copying this form:
<form action="#registration-note" method="get">
<div class="form-field">
<label for="guest-name">Full name (required)</label>
<input id="guest-name" name="guest-name" type="text" required />
</div>
<div class="form-field">
<label for="guest-email">Email address (required)</label>
<input id="guest-email" name="guest-email" type="email" required />
</div>
<div class="form-field">
<label for="session">Most interesting session</label>
<select id="session" name="session">
<option value="semantic-html">Semantic HTML</option>
<option value="responsive-css">Responsive CSS</option>
<option value="accessible-forms">Accessible forms</option>
<option value="media-motion">Media and motion</option>
</select>
</div>
<div class="form-field">
<label for="message">What would you like to review next?</label>
<textarea id="message" name="message" rows="4"></textarea>
</div>
<button type="submit">Test my RSVP</button>
</form>
How Does Browser Validation Work?
- The matching for and id values connect each label to its control.
- The name attributes identify the submitted form values.
- The two required attributes block submission when their controls are empty.
- The email field checks that the entered value uses an email format.
- The action="#registration-note" value targets the local demonstration note after a valid submission.
- Save index.html.
- Switch back to Microsoft Edge.
- Refresh the PixelWave page.
You'll see labeled fields for a name, an email address, a session choice, and a message. You'll also see the Test my RSVP button.
Form Missing or Unstyled?
- Check that the form sits inside <section id="rsvp" class="panel">.
- Confirm that every opening <div> has a matching closing </div>.
- Still stuck? Help me check the structure of my PixelWave RSVP form.
Add the skip link and demo notice
A skip link gives keyboard visitors a fast route past repeated navigation. The stylesheet from the previous step already hides this link until it receives focus.
The local demonstration notice explains what happens after submission. It also makes clear that the page does not store registration data.
- In index.html, find the opening <body id="top"> line.
- Add the skip link directly below that line by copying this code:
<a class="skip-link" href="#main-content">Skip to main content</a>
What Does the Skip Link Do?
The link points to #main-content, which matches the existing id="main-content" value on the page's <main> element.
The existing .skip-link:focus rule moves the link into view when it receives keyboard focus.
- Return to the RSVP section in index.html.
- Find the closing </form> tag.
- Add the local demonstration notice directly below the form by copying this code:
<p id="registration-note" class="form-note">
This is a local demonstration. The form tests browser validation but
does not save registration data.
</p>
Why Add This Notice?
The registration-note identifier matches the form's action target. A successful local submission moves the visitor to this explanation.
The notice sets a clear boundary for the demonstration. No registration data is saved.
- Save index.html.
- Switch back to Microsoft Edge.
- Refresh the PixelWave page.
- Press Tab from the top of the page.
You'll see the Skip to main content link move into view with a bright focus outline.
- Press Enter while the skip link has focus.
Microsoft Edge jumps past the repeated navigation to the main festival content.
- Move to the RSVP section with the RSVP navigation link.
You'll see the local demonstration notice directly below the form.
Skip Link Not Appearing?
- Confirm that the skip link is the first element inside <body id="top">.
- Check that its destination is exactly #main-content.
- Still stuck? Help me troubleshoot the PixelWave skip link and focus styles.
✔️ Awesome, I've got everything!
Great work. Your complete index.html now includes the RSVP flow, skip link, and local demonstration notice.
ⓧ I'd like to double check the full code
- Compare your complete index.html with this reference:
<!doctype html>
<html lang="en-US">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width" />
<title>PixelWave Web Festival</title>
<link rel="stylesheet" href="styles.css" />
</head>
<body id="top">
<a class="skip-link" href="#main-content">Skip to main content</a>
<header class="hero">
<nav class="site-nav">
<a class="brand" href="#top">PixelWave</a>
<ul>
<li><a href="#about">About</a></li>
<li><a href="#schedule">Schedule</a></li>
<li><a href="#media">Media</a></li>
<li><a href="#rsvp">RSVP</a></li>
</ul>
</nav>
<div class="hero-content">
<div>
<p class="eyebrow">A one-day online festival</p>
<h1>PixelWave Web Festival</h1>
<p class="hero-copy">
Build boldly, learn openly, and celebrate the web with
<strong>practical sessions</strong>, <em>learner-made demos</em>, and
<mark>free entry</mark>.
</p>
<a class="button-link" href="#schedule">Explore the schedule</a>
</div>
<div class="ticket-badge">
<span>Festival pass</span>
<strong>PW-01</strong>
</div>
</div>
</header>
<main id="main-content">
<section id="about" class="panel">
<div class="section-heading">
<p class="eyebrow">About the event</p>
<h2>A compact tour of the modern web</h2>
</div>
<p>
PixelWave reviews how <abbr title="HyperText Markup Language">HTML</abbr>
gives content meaning and how CSS turns that structure into a responsive
visual experience.
</p>
<ul class="feature-list">
<li>Semantic content and useful navigation</li>
<li>Responsive layouts, tables, and media</li>
<li>Accessible forms, focus states, and motion</li>
</ul>
<blockquote>
“The best frontend reviews end with something you can open, test, and
show.”
</blockquote>
<p>
Continue exploring standards and examples at
<a href="https://developer.mozilla.org/en-US/">MDN Web Docs</a>.
</p>
</section>
<section id="schedule" class="panel">
<div class="section-heading">
<p class="eyebrow">Festival schedule</p>
<h2>Pick your learning track</h2>
</div>
<div class="schedule-scroll">
<table class="schedule-table">
<caption>
PixelWave session timetable
</caption>
<thead>
<tr>
<th scope="col">Time</th>
<th scope="col">Session</th>
<th scope="col">Format</th>
<th scope="col">Room</th>
</tr>
</thead>
<tbody>
<tr>
<td><time datetime="10:00">10:00</time></td>
<td>Semantic HTML that explains itself</td>
<td>Workshop</td>
<td>Markup Hall</td>
</tr>
<tr>
<td><time datetime="11:30">11:30</time></td>
<td>Responsive CSS without layout panic</td>
<td>Live build</td>
<td>Style Stage</td>
</tr>
<tr>
<td><time datetime="13:30">13:30</time></td>
<td>Forms people can finish</td>
<td>Review</td>
<td>Input Lab</td>
</tr>
<tr>
<td><time datetime="15:00">15:00</time></td>
<td>Accessible media and motion</td>
<td>Demo</td>
<td>Media Deck</td>
</tr>
</tbody>
</table>
</div>
</section>
<section id="media" class="panel">
<div class="section-heading">
<p class="eyebrow">Media stage</p>
<h2>Test native browser playback</h2>
</div>
<div class="media-grid">
<article class="media-card">
<h3>Audio sound check</h3>
<audio controls preload="metadata">
<source
src="https://github.com/mdn/learning-area/raw/refs/heads/main/html/multimedia-and-embedding/tasks/media-embed/media/audio.mp3"
type="audio/mpeg" />
<a
href="https://github.com/mdn/learning-area/raw/refs/heads/main/html/multimedia-and-embedding/tasks/media-embed/media/audio.mp3">
Download the audio sample
</a>
</audio>
<p>Transcript: A short demonstration sound plays.</p>
</article>
<article class="media-card">
<h3>Video stage preview</h3>
<video controls width="320" height="240" muted preload="metadata">
<source
src="https://github.com/mdn/learning-area/raw/refs/heads/main/html/multimedia-and-embedding/tasks/media-embed/media/video.mp4"
type="video/mp4" />
<source
src="https://github.com/mdn/learning-area/raw/refs/heads/main/html/multimedia-and-embedding/tasks/media-embed/media/video.webm"
type="video/webm" />
<track
kind="subtitles"
src="https://raw.githubusercontent.com/mdn/learning-area/refs/heads/main/html/multimedia-and-embedding/tasks/media-embed/media/subtitles_en.vtt"
srclang="en"
label="English" />
<a
href="https://github.com/mdn/learning-area/raw/refs/heads/main/html/multimedia-and-embedding/tasks/media-embed/media/video.mp4">
Download the MP4 video
</a>
</video>
</article>
</div>
</section>
<section id="rsvp" class="panel">
<div class="section-heading">
<p class="eyebrow">RSVP practice</p>
<h2>Test an accessible form</h2>
</div>
<form action="#registration-note" method="get">
<div class="form-field">
<label for="guest-name">Full name (required)</label>
<input id="guest-name" name="guest-name" type="text" required />
</div>
<div class="form-field">
<label for="guest-email">Email address (required)</label>
<input id="guest-email" name="guest-email" type="email" required />
</div>
<div class="form-field">
<label for="session">Most interesting session</label>
<select id="session" name="session">
<option value="semantic-html">Semantic HTML</option>
<option value="responsive-css">Responsive CSS</option>
<option value="accessible-forms">Accessible forms</option>
<option value="media-motion">Media and motion</option>
</select>
</div>
<div class="form-field">
<label for="message">What would you like to review next?</label>
<textarea id="message" name="message" rows="4"></textarea>
</div>
<button type="submit">Test my RSVP</button>
</form>
<p id="registration-note" class="form-note">
This is a local demonstration. The form tests browser validation but
does not save registration data.
</p>
</section>
</main>
<footer>
<p>PixelWave is a fictional learning event.</p>
<a href="#top">Back to top</a>
</footer>
</body>
</html>
What Should Match?
This reference contains the complete semantic page from the earlier steps. It also includes every new RSVP and skip-link element from this step.
Your styles.css stays exactly as it was at the start of this step. Its existing form, skip-link, and focus rules support the completed HTML.
Test the complete page
A final pass checks each visitor flow in the environment where people use it. This catches broken fragment targets, unavailable media, incomplete validation, and missing focus feedback.
- Select the About navigation link.
- Select the Schedule navigation link.
- Select the Media navigation link.
- Select the RSVP navigation link.
- Play the audio sample with its browser control.
- Play the video with its browser control.
- Enable the video's English subtitle track.
- Select the Back to top link in the footer.
Each section link jumps to its matching heading. Both media elements play through Microsoft Edge's built-in controls.
What Has This Pass Proven?
The fragment links connect navigation labels to real page sections. The media controls provide playback without custom JavaScript.
The subtitle track adds a text alternative within the video player. These checks confirm the complete content flow before you test input.
Before you select the RSVP button, what do you think Edge will do with two empty required fields?
- Return to the RSVP section.
- Leave Full name (required) empty.
- Leave Email address (required) empty.
- Select Test my RSVP.
Edge blocks the submission and identifies the first required field that needs a value. This confirms that native browser validation is active.
- Enter Pixel Wave Guest in Full name (required).
- Enter guest@example.com in Email address (required).
- Select Responsive CSS from Most interesting session.
Before you submit these valid values, where do you think the page will move?
- Select Test my RSVP.
The local page accepts the test and moves to the registration notice. The notice confirms that no registration data is saved.
- Return to the top with the Back to top link.
Before you start tabbing, which page control do you expect to receive focus first?
- Press Tab repeatedly from the top of the page.
You'll see the focus outline move through the skip link, navigation, media controls, form controls, submit button, and Back to top link. The order follows the page's semantic structure.
Validation or Focus Not Working?
- Check that both required inputs still include the required attribute.
- Confirm that the email input still uses type="email".
- Confirm that styles.css still contains the global :focus-visible rule.
- Still stuck? Help me troubleshoot RSVP validation and keyboard focus on PixelWave.
That's the complete visitor flow working. Your PixelWave page now supports section navigation, native media, browser-validated input, skip navigation, and visible keyboard focus.
Secret mission
Run an Accessibility Stress Test
Can PixelWave stay usable without a mouse or a wide browser window? Test its keyboard order, narrow layout, and reduced-motion behavior. Record a Pass result for each condition.
Clean Up Your Resources
Clean Up Your Resources
PixelWave is a local HTML and CSS project with no ongoing costs. Choose the cleanup option that matches your next move.
Resources you used:
- The local pixelwave-festival folder containing index.html and styles.css.
Keep everything running
No action needed. Choose this if you want to demonstrate PixelWave again or continue improving it.
- Keep the pixelwave-festival folder unchanged.
- Return to the finished page in Microsoft Edge whenever you want to demonstrate PixelWave.
- Continue editing index.html or styles.css in Visual Studio Code when you want to extend the page.
Pause - I'll come back to this later
Closing the apps ends your current session. The local files stay ready for later.
- Close the PixelWave tab in Microsoft Edge.
- Close Visual Studio Code.
- Leave the pixelwave-festival folder in its current location.
Delete - I don't want to use this again
Deleting the folder can feel final. The actions below remove only your local PixelWave project.
- Close the PixelWave tab in Microsoft Edge.
- Close Visual Studio Code.
- Open Windows search from the taskbar.
- Search for File Explorer.
- Select the File Explorer result.
- Return to the location where you created pixelwave-festival.
- Select the pixelwave-festival folder.
- Use the delete control in the File Explorer toolbar to remove the selected folder.
- Confirm that pixelwave-festival no longer appears in the folder list.
Nice Work!
Nice Work!
That is a wrap! Your PixelWave Web Festival is now a responsive single-page demo in Microsoft Edge.
You've learned how to:
- Use semantic HTML to give the festival page clear landmarks. Connect its navigation with document-fragment links that jump directly to each section.
- Diagnose a horizontal overflow problem caused by the fixed-width schedule. Repair it by keeping horizontal scrolling inside .schedule-scroll. Use responsive CSS to keep media inside the page at narrow widths.
- Play audio plus captioned video through native media controls. Test a labeled RSVP form through browser validation. Preserve visible keyboard focus plus reduced-motion support across the finished page.
- Complete the optional Secret Mission by recording Pass results for keyboard order. Repeat the audit at a narrow window width. Confirm the festival pass becomes still when Windows reduced motion is enabled.
Ready to quiz yourself?