Tutorial
Adding a Page
Let's add a page together.
We'll write the Registration page from scratch. Follow along in your editor and you'll finish with a page that feels like it belongs.
You need to know HTML and CSS. Svelte is mostly ordinary HTML, and the few places it isn't are pointed out as they come up.
The shape that follows is the one the rest of the site uses, not a hard rule. Lay your page out in whatever way best suits its content.
Create the file
The directory path is the URL.
First, where should the page be located? The site has four main sections:
- Program
- Talks, tutorials, the schedule, and the call for proposals.
- Attend
- Getting to the conference and into it: venue, travel, hotels.
- Sponsor
- For the companies who fund the conference.
- About
- The conference itself: who runs it, and the policies it runs by.
Let's put the registration page at /attend/registration. To do so,
we'll create the file:
site/(main)/attend/registration/+page.svelte
A +page.svelte file becomes the page at the URL formed by the directories above it.
A page under attend/ inherits that section's navigation. To retrieve it,
open the file and start with a <script> block:
<script>
import { getContext } from '$lib/layout';
const { BreadcrumbNav, SectionNav } = getContext();
</script>Title the page
What the browser and search engines read.
Under the script block, add:
<svelte:head>
<title>Registration — PGConf.dev 2027</title>
<meta
name="description"
content="Registration types, prices, and taxes for PGConf.dev 2027 in Montréal, plus the refund deadline and complimentary registrations for speakers."
/>
</svelte:head><svelte:head>'s contents are rendered in the document's <head>. Give every page a title ending in — PGConf.dev 2027 and a description, which is the text that shows up in
search results and link previews.
Aim for a description ~140 characters long. It should be a sentence describing what's on the page, not a list of keywords. Make it specific enough that it couldn't be pasted onto another page unchanged.
Write the header
Breadcrumbs, label, title, and opening sentence.
Below the head block, add the breadcrumb trail:
<BreadcrumbNav>
<li>Registration</li>
</BreadcrumbNav>Then, add the page's header:
<header class="(main)">
...
</header>(main) is a placement class: it spans the header across the full width of the page, margins included.
Within the header, add the page's heading and lede paragraph:
<hgroup>
<p class="action">
Attend · Registration
</p>
<h1>Register for PGConf.dev 2027.</h1>
</hgroup>
<p class="lede mute text">
Three registration types, one price for the whole four days.
</p>A paragraph before the heading labels it and one after summarizes it. See Text › Headings for more information.
The other three classes are text utilities:
.lede(or.lead)- Marks the paragraph as a lede, i.e. the sentence or two that introduce what follows. It's set a step larger than body text to match that role.
.mute- Softens the text color, so the paragraph supports the heading above it rather than competing with it.
.text- Holds the paragraph to a readable line length. A
(main)block spans the full width of the page, which is far wider than comfortable prose.
Save and load /attend/registration. We have a page!
Add a table of contents
Your first block in the margin.
Below the section navigation, let's list the sections we're about to write:
<nav class=") note" aria-labelledby="on-this-page">
<h2 id="on-this-page" class="over">
On this page
</h2>
<ol>
<li>
<a class="li-number" href="#registration-types">
Registration types
</a>
</li>
<li>
<a class="li-number" href="#prices-and-taxes">
Prices & taxes
</a>
</li>
</ol>
</nav>) is a placement class that puts the nav in the right margin, where every page on the site keeps its
table of contents.
.note- Marks the nav as supporting material: a size smaller and in a muted tone.
.li-number- Numbers each item, so the list matches the numbered sections it points at.
.over- Sets the heading as an eyebrow (or overline): the small label that sits over the thing it introduces.
Use the margin
Not everything belongs in the flow of the page.
The margins aren't only for navigation. A returning visitor wants the prices without reading the page, so let's add a summary aside:
<aside class="main ) note border area" aria-labelledby="at-a-glance">
...
</aside>Two placements this time. ) puts the box in the right margin, and main drops it
into the main column when the window is too narrow for a margin, so it's on the
page at every width.
.area- Fills the block with a tinted surface, so it reads as a unit set apart from the page.
.border- Outlines the block on all sides, the unfilled counterpart to
.area.
Within the aside, write:
<h2 id="at-a-glance" class="over">At a glance</h2>
<hr />
<dl class="dedent">
<div class="p">
<dt class="over">Corporate</dt>
<dd>CAD $550 · $632.36 with tax</dd>
</div>
<div class="p">
<dt class="over">Student</dt>
<dd>CAD $75 · $86.23 with tax</dd>
</div>
</dl>A <dl> is the right element for label-and-value pairs.
.p- Gives each pair paragraph spacing, so the pairs read as separate entries rather than one run of text.
.dedent- Drops the browser's default indent on definition lists.
Write the sections
One <section> per idea, each with an id.
Now the content: a flat run of <section> elements, one per idea,
in the order the table of contents promised. Here's the second of the four:
<section id="prices-and-taxes" class="main">
...
</section>main keeps the section in the centre column. Give each one an id that matches its heading, so the link a reader
guesses is the link that works.
Within the section, write the heading and the copy:
<hgroup>
<p class="section-number">
Prices & taxes
</p>
<h2>What you actually pay.</h2>
</hgroup>
<p class="lede">
Prices are in Canadian dollars.
</p>
<p>
Quebec charges two sales taxes, totalling <strong>14.975%</strong>.
</p>.section-number numbers the section for you. See Layout › Counters for more
information.
Add a table
Rows, columns, and a caption.
The prices are tabular data, so let's put them in a table:
<table class="stripe">
<caption>
Registration prices for PGConf.dev 2027
</caption>
<thead>
<tr>
<th>Type</th>
<th>Price</th>
<th>With tax</th>
</tr>
</thead>
<tbody>
<tr>
<td>Corporate</td>
<td class="mono">$550</td>
<td class="mono">$632.36</td>
</tr>
</tbody>
</table>.stripe- Fills alternating rows, to make a longer table easier to scan.
.mono- Sets the figures in the monospace face, so the columns line up.
See Appearance › Tables for more information.
Add a callout
Set one block apart with color.
Let's close the page with somewhere to go for questions. This is where the two helpers from earlier come in, so first add their imports to the script block at the top of the file:
<script>
import { resolve } from '$app/paths';
import Link from '$lib/Link';
</script>Then, let's add the callout:
<section class="main">
<div class="action-area edge">
<h3 class="h6">
Questions about registration
</h3>
<p>
If you need an invitation letter for a visa, see
<Link href={resolve("/attend/travel#entry-requirements")}>
entry requirements
</Link>.
</p>
</div>
</section>.action-area- Fills the block in the site's action color.
.edge- Runs a rule down the block's left side.
.h6- Gives the heading the size of an
<h6>without changing what it is. Pick the heading level the document outline calls for, then size it with.h1through.h6if you need to.
See Appearance › Areas for more information.
Check your work
Before you open a pull request.
Resize the window with the page open. The margins appear at 48rem and 64rem, so anything you put in one deserves a look at all three widths.
Then, check that:
- The
<title>ends with— PGConf.dev 2027, and the meta description is a sentence of roughly 140 characters. - The page is listed in its section's
SectionNav.svelte. - Every section has an
idthat matches its heading, and the table of contents links to each one. - Internal links go through
resolve()with a full path. npm run lintandnpm run checkpass, andnpm run formathas been run.
The finished page is at /attend/registration.
Read its source beside any other page in site/(main)/; copying a
neighbour is how the site is maintained.