Overview#
Vows is a warm, botanical wedding website built as a WordPress block theme, for a fictional couple,
Amara & Theo, marrying at Hollowmere House on Saturday 19 June 2027. The theme has a home page with
a live countdown to the ceremony, the couple's story as a timeline, a weekend schedule with
add-to-calendar files, a travel page with an illustrated map, shuttle times and room blocks, an
RSVP where guests find their invitation by code or name and reply for everyone on it, a registry
with a honeymoon fund, a gallery with a lightbox, a searchable FAQ and a 404 page. It is version
1.0.1 (see template.json and style.css), works with WordPress 7.1 and PHP 7.4 or newer, and is
the WordPress version of the HTML template of the same name. See it live at
freeframe.dev/templates/vows.
Everything you see is made of core WordPress blocks arranged in block patterns, so you edit text,
links, buttons, lists and images in the block editor without touching code. Colours, the fonts and
font sizes are design tokens in theme.json: change them in Appearance > Editor > Styles and the
whole site follows. The parts that guests use (RSVP, registry, calendar files) read the wedding's
data from assets/js/data.js, as in the HTML version.
Quick start#
Requirements: WordPress 7.1 or newer and PHP 7.4 or newer. No plugins are needed.
- Download
vows.zipfrom freeframe.dev/templates/vows (the WordPress tab). - In your WordPress admin go to Appearance > Themes > Add New Theme > Upload Theme, choose the zip and click Install Now, then Activate. The theme is listed as Freeframe Vows.
- Go to Appearance > Vows setup and click Create the demo pages. This creates the Home, Our story, Schedule, Travel & stay, RSVP, Registry, Gallery and FAQ pages from the theme's page patterns, sets Home as the front page (Settings > Reading) and creates the header menus. Pages that already exist with the same slug are left alone, so it is safe to click it again.
- In Settings > General, set the Site Title to your names and the Tagline to the date: the home page's browser title is "Title · Tagline", the other pages' "Page · Title".
- Put in your own names, date, events, guest list and registry (see Content).
You can also try the theme without installing anything: the Try it in WordPress button on freeframe.dev opens it in WordPress Playground (WordPress running in your browser) with the demo pages already set up.
To work on the theme's files locally you need Node.js 24 and pnpm; WordPress itself runs in WordPress Playground, so there is no PHP, web server or database to install:
pnpm install
pnpm dev # builds the CSS, then starts WordPress with the theme at http://127.0.0.1:9400pnpm dev signs you in as admin (password password) and creates the demo pages. The site is
rebuilt from scratch every time you start it; your edits to theme files are live, edits made in the
WordPress admin are not kept.
Pages#
Each page is a page pattern (in Patterns > Vows pages) made of section patterns. Every section is also offered on its own in the block inserter under Patterns > Vows.
| Page | Slug | Page pattern | Sections (pattern files in patterns/) |
|---|---|---|---|
| Home | / |
page-home.php |
home-hero (names, date, countdown), save-the-date, plan-your-weekend, story-teaser, weekend-glance, rsvp-cta |
| Our story | /story/ |
page-story.php |
story-header, story-timeline, story-quote |
| Schedule | /schedule/ |
page-schedule.php |
schedule-header (days, whole-weekend calendar file), schedule-days |
| Travel & stay | /travel/ |
page-travel.php |
travel-header, venue-map, getting-there (shuttle times), where-to-stay (booking codes), local-tips |
| RSVP | /rsvp/ |
page-rsvp.php |
rsvp-header, rsvp-form |
| Registry | /registry/ |
page-registry.php |
registry-header, honeymoon-fund, gifts |
| Gallery | /gallery/ |
page-gallery.php |
gallery-header, gallery-photos, lightbox |
| FAQ | /faq/ |
page-faq.php |
faq-header (search), faq-questions |
| 404 | any missing URL | templates/404.html |
404 |
WordPress posts use templates/single.html; the blog, archives and search results use
templates/index.html, templates/archive.html and templates/search.html. For a plain text page
(a privacy note, accommodation details), choose the Page with title template in the page's
settings.
Project structure#
freeframe-vows/
style.css theme header (name, version, requirements, license); no styles
theme.json design tokens: palette, fonts, font sizes, layout widths, button
functions.php loads the files in inc/
inc/setup.php stylesheet and scripts (with their strings, page and image URLs),
editor styles, the `js` class script, theme colour, favicon, title
separator, pattern categories
inc/icons.php registers assets/icons/*.svg for the Icon block
inc/icon-labels.php the icons' names shown in the editor
inc/block-styles.php button block styles
inc/site-setup.php Appearance > Vows setup: creates the demo pages and the menus
templates/ page.html (pages), page-with-title.html, single.html, index.html,
archive.html, search.html, 404.html
parts/ header.html and footer.html (they show the header and footer patterns)
patterns/ every section and page (PHP files with block markup)
assets/css/theme.css the compiled stylesheet (do not edit: built from src/)
assets/css/editor.css the same for the block editor (generated by src/editor-css.mjs)
assets/js/data.js the wedding's data: events, meals, guest list, registry, fund, gallery
assets/js/main.js the site's JavaScript
assets/icons/ SVG icons for the Icon block
assets/img/ botanical ornaments, gallery illustrations, favicon, social image
assets/fonts/ Cormorant Garamond, Jost and Pinyon Script and their licenses (OFL.txt)
src/theme.css the Tailwind CSS source of assets/css/theme.css
src/icons.css icons drawn before or after text with CSS (generated)
src/editor-css.mjs writes assets/css/editor.css after each build
languages/ freeframe-vows.pot, the template for translations
blueprint.json WordPress Playground setup used by `pnpm dev`
screenshot.png the picture shown in Appearance > Themes
package.json the CSS build and the local WordPress (Node.js tooling)
readme.txt, README.md, DOCS.md, CHANGELOG.md, LICENSE.txtCustomising#
Brand and colours#
The colours are in theme.json (settings.color.palette) and in Appearance > Editor > Styles >
Colors > Palette:
| Colour | Used for |
|---|---|
| Base (paper), Linen, Blush, Card | the page background, soft panels, icon circles and gift art, cards and fields |
| Contrast (ink), Muted, Line | text, secondary text, hairlines |
| Sage, Sage deep, Sage light, Forest | the botanical greens: eyebrows, the Sage button, the timeline, the footer |
| Terra, Terra light, Gold | the terracotta accent (the RSVP button, script names, the current page), and the gold of the frames |
The theme's stylesheet reads every colour from the palette (bg-forest, text-terra... are the
presets), so changing Terra in Styles recolours every accent at once. The ornaments
(assets/img/corner.svg, wreath.svg, sprig.svg) and the gallery illustrations carry their own
colours: edit the SVG files to recolour them. Keep text colours at a 4.5:1 contrast.
Typography#
The fonts are Cormorant Garamond (headings, font-serif, roman 400 to 700 and italic 400 to 600),
Jost (text, font-sans, 400 to 600) and Pinyon Script (the names, script), bundled in
assets/fonts/ and declared in theme.json (settings.typography.fontFamilies), so no request
goes to Google. To use other fonts, install them in Appearance > Editor > Styles > Typography >
Fonts (the Font Library) and select them, or replace the fontFace entries in theme.json. The
utility classes use the families --wp--preset--font-family--jost,
--wp--preset--font-family--cormorant-garamond and --wp--preset--font-family--pinyon-script, so
rename those slugs only together with src/theme.css.
Font sizes are the presets in theme.json (xs 0.75rem to 9xl 8rem, the Tailwind scale). The
stylesheet uses them too: changing Large in Styles changes every text-lg in the design.
Content#
Edit pages in Pages: click any text to change it, select a button to change its link, select an image and use Replace. Sections are groups; open List View (the stacked lines icon at the top left) to select a section, move it, duplicate it or delete it. To add a section from another page, use the block inserter (+) > Patterns > Vows. Search for "Amara", "Theo", "Hollowmere" and "19 June" when you make it yours: they appear in the pages, the header and the footer.
Some content is in Custom HTML blocks, because blocks can't express it: edit those in the block's HTML view.
| Pattern | Custom HTML | What it is |
|---|---|---|
header |
the menu button | a button driven by assets/js/main.js |
home-hero |
the countdown and its "Today's the day" message | see Countdown |
save-the-date, schedule-header, schedule-days |
the "Add to calendar" buttons | see Add to calendar |
venue-map, getting-there, where-to-stay |
the illustrated map and its legend; the shuttle timetable; the copy buttons of the booking codes | see Travel map and booking codes |
rsvp-form |
the whole RSVP (the script builds the later steps) | see RSVP |
honeymoon-fund, gifts |
the progress bar and the contribution form; the category buttons, "Hide reserved gifts", the count and the gift list | see Registry and honeymoon fund |
gallery-photos, lightbox |
the photographs (buttons that open the viewer) and the viewer | see Gallery |
faq-header, faq-questions |
the search field; its result line | see FAQ search |
footer |
the small confirmation message ("toast") | messages from the script |
Blocks can't store data- attributes, so the theme writes them as classes on blocks and the first
lines of assets/js/main.js turn them back into attributes before anything else runs:
ffd-name--value becomes data-name="value", ffa-name--value becomes the attribute
name="value" (ffa-aria-labelledby--fund-title, ffa-aria-hidden--true), and ff-hidden hides
a block until the script shows it. On the FAQ page each question is a Details block with
ffd-faq and each group of questions a section with ffd-faq-group; the search reads them.
The figure "£1,840 raised" on the registry is a <strong data-fund-raised> in the paragraph's text
(edit it with Edit as HTML). Cards that link to a page ("Plan your weekend") are groups with
the class link-card: the link is on the card's heading and covers the whole card. reveal fades a
block in as it scrolls into view (classes like [--d:80ms] stagger neighbours).
The wedding's data (assets/js/data.js)
The parts guests use read window.VOWS in assets/js/data.js, the same file as in the HTML
version:
| Key | Shape | Used by |
|---|---|---|
keepDemoCurrent |
true |
demo only: moves every date on a year once the weekend is over (see Demo dates below) |
couple |
{ a, b, names, date_long, date_short, venue, place, ceremony_iso, rsvp_by, hashtag, email } |
calendar file titles (names) |
events |
{ id, day, title, start, end, place, address, dress, body } |
.ics files |
days |
{ id, label, date, title } |
reference only (the schedule page is blocks) |
meals, kidsMeal |
{ id, label, note } |
RSVP dinner choices (kidsMeal is offered first to guests with child: true) |
invites |
{ code, label, plusOne, guests: { id, name, child? }[] } |
RSVP lookup |
registry |
{ id, name, category, price, needed, reserved, icon, note } |
gift list (icon is a key of giftIcons) |
categories |
category names | reference only (the filter buttons are in the gifts pattern) |
fund |
{ title, destination, goal, raised, amounts: { value, label }[] } |
honeymoon fund |
gallery |
{ slug, title, caption, shape } (shape is "tall" or "wide") |
lightbox |
giftIcons |
{ <name>: "<svg paths>" } |
gift card icons |
Some values also appear in the pages, so change both together:
- Ceremony date and time:
couple.ceremony_iso, thedata-countdownattribute in the countdown's Custom HTML block (home-hero), and the dates written on the pages, the header's mobile menu and the footer. - Schedule: each event's blocks on the Schedule page and its entry in
events(used for the calendar files). Each "Add to calendar" button hasdata-ics="<event id>". - Fund:
fund.goalandfund.raised, the "£1,840 raised of £3,000" text, the progress bar'saria-valuemax,aria-valuenowand width, and the amount buttons (data-amount). - Gallery: the photographs' Custom HTML block (each button's
data-photois its index ingallery, and its picture isassets/img/gallery-<slug>.svg) and thegallerylist.
Add an invitation to invites. code is matched ignoring case, spaces and punctuation; id
must be unique within the invitation:
{
"code": "BIRCH-05",
"label": "The Novak family",
"plusOne": false,
"guests": [
{ "id": "anna", "name": "Anna Novak" },
{ "id": "eli", "name": "Eli Novak", "child": true }
]
}Add a gift to registry (needed is how many you'd like, reserved how many are already
taken):
{ "id": "teapot", "name": "Stoneware teapot", "category": "Kitchen", "price": 48, "needed": 1, "reserved": 0, "icon": "pot", "note": "For long Sunday breakfasts." }Times include their UTC offset (+01:00 is British Summer Time), so calendar files show the right
local time for guests anywhere. Use your venue's offset on the wedding date.
data.js is public: anyone can read the guest list in the browser. For a real wedding keep the
list short (codes and names only), or move the lookup to a server (see RSVP).
Demo dates. The demo is written for the weekend of 18–20 June 2027. While keepDemoCurrent
in assets/js/data.js is true and that weekend is over (after the last event in events ends),
the script moves every date to the same weekend of the next year, by whole weeks so weekdays never
change (Saturday 19 June 2027 becomes Saturday 17 June 2028): the countdown, the events and calendar
files, every <time datetime>, the text of data-date="2027-04-01" spans in the patterns ("1 April
2027"; with data-date-format="dm" "18 June"), data-edition="year" and data-edition="short"
("19.06.27"), and the date in the page title. Shifted dates are written in English. Before the
weekend is over nothing changes. Set keepDemoCurrent to false once you enter your real dates.
Images and artwork#
All artwork is SVG in assets/img/: the botanical ornaments (corner.svg, wreath.svg,
sprig.svg) and nine gallery illustrations (gallery-<slug>.svg, 1200 × 1500 for tall, 1200 × 900
for wide). In the pages they are Image blocks: select one and use Replace to pick a photo
from the Media Library. The ornaments are decorative (alt=""). The gallery's photographs and the
viewer read assets/img/gallery-<slug>.svg by the slugs in data.js; to use your photos there,
replace the files (keeping the names) or change the paths in the gallery-photos block and in
show() in assets/js/main.js. The favicon (assets/img/favicon.svg) is used until you set a Site
Icon in Settings > General; assets/img/og-image.png (1200 × 630) is the social image to give
your SEO plugin. Icons are Icon blocks: pick another icon from the Vows collection (the theme's
icons) or WordPress's own.
Navigation, header and footer#
The header and footer are template parts (parts/header.html, parts/footer.html) showing the
header and footer patterns. Edit them in Appearance > Editor > Patterns > Header / Footer.
The initials ("A & T") are a paragraph with a link: edit them there.
The header has two Navigation blocks: the desktop menu ("Main menu": Our story, Schedule, Travel, Registry, Gallery, FAQ, with the RSVP button beside it) and the full-screen mobile menu ("Mobile menu": Home, the same pages and RSVP, each with an arrow drawn by CSS), both created by the setup. Edit them in Appearance > Editor > Navigation. WordPress marks the link to the current page. The header becomes solid once the page scrolls or the mobile menu opens.
The footer's menu is a List block in a navigation group; the confirmation message ("toast") the script shows is the small Custom HTML block after the footer.
Features#
Countdown#
The countdown (data-countdown="2027-06-19T15:00:00+01:00", role="timer") shows days, hours,
minutes and seconds to the ceremony and updates every second. On the day it stops at zero and shows
"Today's the day" (data-countdown-done). Change the date in the block's HTML and in
couple.ceremony_iso.
Add to calendar#
Buttons with data-ics="<event id>" download an .ics file for that event, data-ics="all" one
for the whole weekend. The files are built in the browser from events (title with the couple's
names, place, description and dress code, times in UTC), escaped and folded as RFC 5545 requires,
and work with Apple Calendar, Google Calendar and Outlook. A message confirms the download.
RSVP#
- Find your invitation: the guest types the code on their card (
ROSE-24) or their full name as it is written there; case, spaces, punctuation and accents don't matter. - Your reply: for each guest on the invitation, attending or not, a dinner choice (children get the little ones' plate first) and dietary needs; a plus-one with their name and meal when the invitation allows one; a song request, a note and an email for the confirmation. Missing answers are listed at the top and marked on each field, and focus moves to the first one.
- Review, then Send reply.
An unsent reply is saved on the device (localStorage, key vows:rsvp-draft:v1) as the guest
types, and restored when they find the same invitation again. Saved replies are validated when
read: unknown invitations, guests, answers and meals are dropped, text is cut to its maximum length
and the stored value is repaired. Nothing is sent: see Connecting the forms.
Registry and honeymoon fund#
The gift list is built from registry: the category buttons filter it, "Hide reserved gifts"
hides the gifts that are fully reserved, and "Reserve" marks a gift as reserved by this visitor
(remembered in the browser, key vows:reserved:v1, validated when read: unknown, duplicate or
fully reserved gifts are dropped). The fund form takes a suggested amount or one of £5 to £5,000
and a name, then updates the total and the progress bar. Both are demos: reservations aren't shared
between guests and no payment is taken; link the fund to your payment page or gift list provider.
Travel map and booking codes#
The legend buttons beside the map (data-pin-target) highlight a place on the map (data-pin);
selecting it again clears it, and the pins can be selected too. The booking codes' copy buttons
(data-copy) copy the code and confirm with a message.
Gallery#
Each photograph opens the viewer (a native <dialog>): the arrow keys and the Previous/Next
buttons move between photos, Escape, the close button or a click outside close it, and focus returns
to the photograph you opened.
FAQ search#
Typing in the search field shows only the questions that contain the words, opens them, hides empty groups and announces "2 questions match …"; clearing it restores the questions you had open.
Header, mobile menu, scroll reveals and back to top#
- The header becomes solid once the page scrolls (
is-scrolled). - The menu button (
data-menu-toggle) opens the full-screen#mobile-menu, locks the page's scrolling and swaps its icon; Escape closes it and returns focus to the button. - Blocks with the
revealclass fade in as they scroll into view; everything above the bottom of the screen is revealed too. With reduced motion, or without JavaScript, everything shows at once. - A "Back to top" button appears after about one screen of scrolling and steps aside while the
mobile menu is open; it scrolls to the top (instantly with reduced motion) and moves focus to the
main content. To remove it, delete the "Back to top" part of
assets/js/main.jsand the.back-to-topstyles insrc/theme.css, then runpnpm build.
Block styles#
In the block settings' Styles panel:
| Block | Styles |
|---|---|
| Button | Fill (default, terracotta), Sage, Outline, Outline (on dark) |
WordPress's own Outline style is removed in the editor. The FAQ's questions are Details blocks;
the round plus that turns into a cross is drawn by CSS (ff-plus).
Connecting the forms#
| Form | Page | Checks |
|---|---|---|
| RSVP | RSVP | an invitation code or name; attendance and a dinner for every guest coming; a plus-one's name and dinner; an email |
| Honeymoon fund | Registry | a suggested amount or £5 to £5,000; a name |
Both are demos: nothing leaves the browser. To receive RSVPs, send the reply to a form service
(Formspree, Basin...) or a Google Sheet (with Apps Script) where the script says "Demo only", in a
child theme's copy of assets/js/main.js:
fetch('https://formspree.io/f/your-id', {
method: 'POST',
headers: { 'Content-Type': 'application/json', Accept: 'application/json' },
body: JSON.stringify(state)
});state holds the invitation code, every guest's answers, the plus-one, the song, the note and the
email. Then update the thank-you text, which says the reply was not sent. For the fund, point the
form at your payment or gift list page. A form plugin (WPForms, Gravity Forms, Fluent Forms...)
can replace either form's Custom HTML block if you prefer a simpler reply form.
Saved data (localStorage)#
| Key | What | Validation |
|---|---|---|
vows:rsvp-draft:v1 |
the unsent RSVP reply | dropped if not an object or for an unknown invitation; unknown guests, answers and meals removed; text cut to length; repaired in place |
vows:reserved:v1 |
gifts this visitor reserved | non-lists, unknown, duplicate and fully reserved ids removed; repaired in place |
Unreadable values are removed. Private browsing or full storage simply keeps the data in memory.
SEO and sharing#
Page titles come from WordPress: the page title, a middle dot and the site name (for example
"Schedule · Amara & Theo"); the home page shows the site name and tagline (Settings > General).
The theme doesn't output meta descriptions or Open Graph tags; use an SEO plugin such as Yoast SEO,
Rank Math or The SEO Framework, and give it assets/img/og-image.png (or your own 1200 × 630
image) as the default social image. A wedding site is often better kept out of search engines:
tick Settings > Reading > Discourage search engines from indexing this site, and consider a
password on the RSVP page (Page settings > Visibility).
Accessibility#
Built in: a skip link to the main content (added by WordPress, styled by the theme), landmarks
(header, labelled navigations, main, footer), one h1 per page, visible focus outlines, the mobile
menu with aria-expanded, a countdown with role="timer" and a label, RSVP steps marked
aria-current="step" with headings that take focus, an error summary and per-field errors
(aria-invalid, aria-describedby), radio groups with labels, filter buttons with aria-pressed,
live regions for counts and messages, a progress bar with values, a native modal lightbox with
focus return, decorative ornaments hidden from screen readers, and reduced-motion support for every
animation. The demo passes axe (WCAG 2.1 A and AA).
When you edit: keep one Heading 1 per page, give pictures alt text (or mark them decorative), keep link text meaningful, and label any field you add to a form.
Building and deploying#
You only need this if you change the theme's files. The stylesheet assets/css/theme.css is
compiled with Tailwind CSS from src/theme.css and the classes used in patterns/, parts/,
templates/, inc/ and assets/js/:
pnpm install
pnpm build # compile assets/css/theme.css and assets/css/editor.css
pnpm watch # recompile assets/css/theme.css on every change (run pnpm build before you commit)Run pnpm build after adding a utility class to a pattern or template, otherwise the class has no
style. Classes you type in the editor's Additional CSS class(es) field are only styled if the
stylesheet already contains them.
To deploy, zip the theme folder (named freeframe-vows, without node_modules/) and upload it in
Appearance > Themes, or copy the folder to wp-content/themes/ on your server.
Translations#
The theme is translation-ready with the text domain freeframe-vows. All text in patterns,
templates and PHP goes through WordPress's translation functions; the script's strings and the
visible text of data.js (events, meals, gifts, the fund, photos) are translated in PHP
(freeframe_vows_script_strings() in inc/setup.php), while ids, invitation codes, names and gift
categories (the filters' values) stay as they are. Create a translation with Loco Translate, or with
WP-CLI and Poedit:
wp i18n make-pot wp-content/themes/freeframe-vows wp-content/themes/freeframe-vows/languages/freeframe-vows.potSave freeframe-vows-fr_FR.mo (for example) in wp-content/languages/themes/ or in the theme's
languages/ folder. Page content created by Vows setup is copied into your pages in the site's
language at that moment; translate it in the editor like any other content.
Child themes#
Use a child theme for changes you want to keep when Vows is updated. Create
wp-content/themes/vows-child/style.css:
/*
Theme Name: Vows Child
Template: freeframe-vows
*/Templates, parts, patterns and theme.json in the child theme override the parent's files of the
same name. The parent's stylesheet only contains the classes the parent uses: if your child theme's
patterns use new utility classes, compile a stylesheet for the child (copy package.json and
src/theme.css, point the @source lines at the child's folders) and enqueue it. Your wedding's
data.js belongs in the child theme too: load it in place of the parent's
(wp_deregister_script( 'freeframe-vows-data' ) and wp_register_script( 'freeframe-vows-data', … )
on wp_enqueue_scripts, priority 30), so updates never overwrite your guest list. To change the
script, do the same with freeframe-vows and assets/js/main.js, keeping the data the parent
prints before it (window.freeframeVowsL10n, window.freeframeVowsPages,
window.freeframeVowsAssets).
Troubleshooting#
- The pages look unstyled or new classes do nothing. The stylesheet only contains classes it
found when it was built. Run
pnpm buildafter editing patterns, then reload. - "This block contains unexpected or invalid content". A pattern or page was edited by hand with markup that doesn't match the block. Click Attempt recovery, or re-insert the section from Patterns > Vows.
- A guest can't find their invitation. The code or the full name must match an entry in
invitesindata.js(accents, case and punctuation are ignored). - A calendar file has the wrong time. Check the UTC offset of the times in
events. - The countdown shows zeros. The date in
data-countdownhas passed; on the day it shows "Today's the day". - The header menu shows other pages, or an old menu. Select each Navigation block and choose "Main menu" or "Mobile menu" (or yours) in the block settings.
- Vows setup says the pages exist, but they are empty. It never overwrites existing pages. Delete (and empty the trash of) the pages with those slugs, or open each page and insert its page pattern from Patterns > Vows pages.
- RSVPs don't arrive. The form is a demo; see Connecting the forms.
Differences from the HTML version#
The pages, copy, design and interactions are the same. What WordPress changes:
- Straight apostrophes typed in text blocks are shown curly, as WordPress does everywhere (text in Custom HTML blocks, the script's messages and menu labels keeps them straight).
- The "Plan your weekend" cards have the link on their heading, stretched over the card (the HTML version wraps the whole card in a link), so screen readers announce the heading as the link's name.
- Scroll reveals hide content only once JavaScript runs (
jsclass), where the HTML version swaps ano-jsclass on<html>: the same result. - Data and ARIA attributes on content blocks are written as classes and restored by the script (see Content).
License#
MIT, which is compatible with the GPL that WordPress requires (see LICENSE.txt in the theme
folder). Free for personal and commercial use, including the illustrations. Cormorant Garamond,
Jost and Pinyon Script are licensed under the SIL Open Font License 1.1 (assets/fonts/*/OFL.txt).
Amara, Theo and everyone in this theme are fictional.