Overview#
Stay is a complete website for a small hotel, guest house or villa, built as a WordPress block
theme. The fictional hotel is Casa Marea, a 22-room house above the sea in Porto Lume, Liguria. The
theme has a home page with a booking bar, a rooms page with filters, a room page with a photo
lightbox, a live price and a two-month availability calendar, a dining page with menu tabs and a
table form, an experiences page with a filter, an offers page with copyable codes, a location and
contact page with an FAQ, a five-step booking flow and a 404 page. It is version 1.0.2 (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/stay.
Everything you see is made of core WordPress blocks arranged in block patterns, so you edit text,
links, buttons, lists and pictures 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 prices, availability and booking flow are worked out in the browser by
assets/js/main.js from the room data at its top.
Quick start#
Requirements: WordPress 7.1 or newer and PHP 7.4 or newer. No plugins are needed.
- Download
stay.zipfrom freeframe.dev/templates/stay (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 Stay.
- Go to Appearance > Stay setup and click Create the demo pages. This creates the Home, Rooms & suites, room, Dining, Experiences, Offers, Location & contact and Book your stay pages from the theme's page patterns, sets Home as the front page (Settings > Reading) and creates the two 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 hotel's name and the Tagline to a short description: the header and footer show the title, and the home page's browser title is "Title — Tagline".
- Set your rooms, rates and offer codes (see Rooms, prices and offers).
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 > Stay pages) made of section patterns. Every section is also offered on its own in the block inserter under Patterns > Stay.
| Page | Slug | Page pattern | Sections (pattern files in patterns/) |
|---|---|---|---|
| Home | / |
page-home.php |
home-hero (hero and booking bar), welcome, home-rooms, home-experiences, home-dining, reviews, getting-here, offers-cta |
| Rooms & suites | /rooms/ |
page-rooms.php |
rooms-header, rooms-list (filters and six room cards), in-every-room |
| Room | /room/ |
page-room.php |
room-breadcrumb, room-detail (gallery, live price, details, amenities, calendar), other-rooms, room-lightbox |
| Dining | /dining/ |
page-dining.php |
dining-hero, dining-places, menus (tabs), reserve-table (form) |
| Experiences | /experiences/ |
page-experiences.php |
experiences-header, experiences-list (filter and cards), events |
| Offers | /offers/ |
page-offers.php |
offers-header, offers-list (offers with codes and terms) |
| Location & contact | /contact/ |
page-contact.php |
contact-header, directions (map, getting here, contact details), contact-form, faq |
| Book your stay | /booking/ |
page-booking.php |
booking-header, booking-flow (the five steps, summary and confirmation) |
| 404 | any missing URL | templates/404.html |
404 |
There is one room page: it shows the room named in the URL (/room/?room=suite), as the HTML
version does. Posts use templates/single.html; the blog, archives and search results use
templates/index.html, templates/archive.html and templates/search.html.
Project structure#
freeframe-stay/
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 script (with their strings, page and image URLs),
editor styles, the `js` class script, theme colour, 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 > Stay setup: creates the demo pages and the two 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/main.js the site's JavaScript, with the room data at the top
assets/icons/ SVG icons for the Icon block
assets/img/ logo, favicon, social image, room, dining and coast illustrations, map
assets/fonts/ Bodoni Moda and Jost 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-stay.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 (linen) | the page background |
| Contrast (ink) | text, dark sections, the primary button |
| Shell, Sand, Line | panels, deeper panels and borders |
| Sea | the second dark colour (dining section, selected states, back-to-top button) |
| Mist | secondary text |
| Coral, Coral dark | the accent: eyebrows, the "Book" buttons, errors |
| Olive, Sun, Foam | olive accents, the warm yellow eyebrows on dark sections, light text on dark |
The theme's stylesheet reads every colour from the palette (bg-ink, text-coral... are the
presets), so changing Coral in Styles recolours every accent at once. Keep enough contrast between
Coral and Base for text (4.5:1).
Typography#
The fonts are Bodoni Moda (the display serif, font-display, with its optical sizes and italics)
and Jost (body text), 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 (font-sans) and --wp--preset--font-family--bodoni-moda
(font-display), 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 a picture 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 > Stay. Search for "Casa Marea", "Porto Lume", the phone number and the email address 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 booking bar | see Booking bar |
rooms-list |
the filter form | see Room filters |
room-detail |
the photo gallery, the price form, the amenities list, the availability calendar | the room page's widgets |
room-lightbox |
the photo lightbox | a dialog |
menus |
the menu tab buttons | see Dining menu tabs |
reserve-table, contact-form |
the forms and their thank-you messages | see Forms |
experiences-list |
the filter buttons | see Experiences filter |
offers-list |
the "Copy code" buttons | see Offer codes |
booking-flow |
the whole booking flow | see Booking flow |
footer |
the newsletter form, and the toast that shows short messages |
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" (an underscore stands for a space),
ffa-name--value becomes the attribute name="value" (ffa-role--tabpanel), and ff-hidden
hides a block until the script shows it. In the editor, hidden blocks are shown with a dashed
outline. The ones you will meet:
- Room cards (Article groups in
home-rooms,rooms-list,other-rooms):ffd-room-card,ffd-view--sea(sea,gardenorpool),ffd-bed--king(kingortwin) andffd-capacity--2(the number of guests); the filters read them. - Experience cards:
ffd-exp-cardandffd-type--Spa(Spa,Sea,LandorEvents). - The room page's texts with
ffd-room-field--name,--short,--description,--size,--bed,--capacity,--viewand--view-label(and the price, adata-room-field="rate"span inside its paragraph): the script fills them with the room named in the URL. - The menu panels on the Dining page: groups with the anchor
panel-breakfast(and so on) and the classesffa-role--tabpanel,ffa-aria-labelledby--tab-breakfastandff-hiddenfor the ones shown later. revealfades a block in as it scrolls into view;link-cardon a group makes the whole group clickable through its first link.
Images and artwork#
- Pictures (rooms, dining, the coast, the pool...) are SVG illustrations in
assets/img/, shown with Image blocks inside boxes that crop them (artboxes with an aspect ratio). Replace them with photos from the Media Library (landscape, at least 1600 × 1000 px); the crop comes from the box. - Room photos used by the script (the room page's gallery and lightbox, other rooms, the
booking flow) are the theme files
assets/img/room-<id>.svg,room-<id>-terrace.svgandroom-<id>-bath.svgfor each room id in the data. Replace those files with your own (same names), or change the paths inphotos(room page),roomCardHTML()andrenderRooms()inassets/js/main.jsto point at your media. - Logo: the header and footer show
assets/img/logo.svgin an Image block next to the Site Title block. Replace the image in Appearance > Editor > Patterns > Header (and Footer); the name comes from Settings > General > Site Title. - Map:
assets/img/map.svgon the home and contact pages; replace it with your own map picture, or an embed from your map provider. - Icons are Icon blocks: select one and pick another icon from the Stay collection (the
theme's icons) or WordPress's own. The arrow in buttons is drawn by CSS (
icon-after-arrow-right). - Social image:
assets/img/og-image.png(1200 × 630, sourceog-image.svg). WordPress doesn't output Open Graph tags by itself; see SEO and sharing. - Favicon:
assets/img/favicon.svgis used until you set a Site Icon in Settings > General.
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 header has two Navigation blocks with their own menus, created by the setup: "Main menu" (Rooms, Dining, Experiences, Offers, Location) on wide screens and "Mobile menu" (the same plus Home) in the full-height mobile menu. Edit them in Appearance > Editor > Navigation. WordPress marks the link to the current page. The "Book a stay" buttons and the phone number under the mobile menu are blocks in the header pattern.
The footer's link columns are List blocks: click a link to change it, press Enter to add one. The copyright year is added by PHP.
Rooms, prices and offers#
The prices, availability and the booking flow use DATA at the top of assets/js/main.js (part
"1. Data and helpers"), with the rules just below it:
| Data | Meaning |
|---|---|
rooms |
id (used in URLs and image names), name, view (sea, garden, pool), bed (king, twin), capacity, size (m²), rate (per night, in euro), short, description, highlights |
extras |
id, name, price, unit, text (the booking flow's step 3 lists them in its Custom HTML) |
promos |
offer codes: code, name, percent, minNights, advanceDays, rooms ([] for all), summary, terms |
amenities |
the list shown under every room's highlights |
| Rule | Default | Meaning |
|---|---|---|
WEEKEND_UPLIFT |
0.15 |
Friday and Saturday nights cost 15% more |
EXTRA_ADULT |
45 |
per night, for each adult beyond two |
EXTRA_CHILD |
25 |
per night, for each child once the room already has two guests |
VAT_RATE |
0.10 |
VAT included in all prices, shown as "Includes VAT" |
CITY_TAX |
3.5 |
per adult per night for the first 7 nights, paid at the hotel |
MAX_NIGHTS |
21 |
the longest stay bookable online |
The pages show the same rooms as blocks (the cards, the room page's text): keep them in step with
the data. When you change a room's name or description in DATA, add the new text to
freeframe_stay_script_strings() in inc/setup.php if you translate the site (the script
translates the visitor-facing text in DATA with it). Make these changes in a child theme so
updates don't overwrite them (see Child themes).
How a total is built: each night costs the room's rate (plus WEEKEND_UPLIFT on Fridays and
Saturdays, rounded to whole euros); extra guests are added per night; extras are added
(breakfast per guest per night, the others once per stay); a valid offer code takes its percentage
off the room total only; VAT and city tax are shown. To change the currency, edit money().
Features#
Booking bar#
The form on the home page (data-booking-bar) prefills itself from the saved draft (or two weeks
from today for three nights), keeps departure after arrival, checks the dates (not in the past,
departure after arrival, at most MAX_NIGHTS) and opens the booking page with the stay
(/booking/?checkin=…&checkout=…&adults=…&children=…). Errors appear under the fields and focus
moves to the first problem.
Room filters#
The Rooms page's filter form (data-room-filters) hides the cards that don't match their
ffd-view--, ffd-bed-- and ffd-capacity-- classes, updates the live count ("Showing 3 of 6
rooms", announced to screen readers) and shows the empty state when nothing matches. The filters
are written to the URL, so /rooms/?view=sea&guests=3 can be shared.
Room page and lightbox#
/room/?room=<id> shows one room: the page ships with the Sea View Room as a fallback and the
script fills every ffd-room-field--… block and the price, the amenities, the photos and the page title with
the requested room's data (an unknown id shows the first room). It lists three other rooms and
disables adult counts above the room's capacity. The gallery buttons open a <dialog> with the
bedroom, terrace and bathroom photos; arrow keys and the buttons move between them, Esc or a click
on the backdrop closes it.
The price form next to the gallery shows a live breakdown (nights with weekend nights, extra guests, total, VAT and city tax) and refuses a stay with more guests than the room sleeps or a booked night. "Book this room" opens the booking flow on the room step.
Availability calendar#
The room page shows two months of availability. Booked nights are struck through and can't be chosen as arrival; click an arrival date, then a departure date, and the price updates. A stay that runs into a booked night is refused with a message, announced to screen readers.
Availability comes from isBlocked(roomId, iso) in assets/js/main.js, which returns a
deterministic demo pattern. Replace it with your reservations system's data, for example the
booked nights of each room in a Set:
var BOOKED = new Set(); // fill from your API, e.g. ['sea:2026-11-02', 'sea:2026-11-03']
function isBlocked(roomId, iso) { return BOOKED.has(roomId + ':' + iso); }firstBlocked() uses isBlocked() for every night of a stay, so the room page and the booking
flow follow the same data.
Booking flow#
The Book your stay page is one form with five steps (a Custom HTML block in booking-flow):
- Dates and guests, checked like the booking bar. If the guests change and the chosen room no
longer sleeps them, the script (
fitRoomToGuests()inassets/js/main.js) switches to the first room that does (the same rule as for guests from the URL) and the summary says so ("The Sea View Room sleeps up to 2 guests, so we've switched you to the Marea Suite …"). If no room sleeps that many (5 or 6 guests), the summary shows no price, only "No room sleeps 6 guests" and a note to choose fewer guests or contact the hotel, and the room step lists every room as too small. - Room: rooms are listed with their price for the dates; rooms that are too small or booked on any night are disabled with the reason. The first free room is preselected.
- Extras: the checkboxes (their values are the
DATA.extrasids). - Guest details: name, email and phone, checked inline, plus an arrival time and requests.
- Review: a summary ("1 adult, 1 child"), the offer code field and the cancellation policy.
A live summary with the price breakdown sits beside the form, the step list marks the current
step (aria-current="step") and focus moves to each step's legend. Arriving from a room page (a
room in the URL) starts on step 2; ?promo=LONGSTAY fills the code, checked on the last step.
On the final step the page shows a confirmation with a demo reference and clears the draft.
Nothing is booked. The place to send the booking is marked in the submit handler
(// Demo only: send \state` and the guest details to your reservations system here.`); replace
it with a request to your booking engine, and check prices and availability again on your server.
Offer codes#
The "Copy code" buttons (data-copy-code="LONGSTAY") copy the code and show a short message (the
toast). The clipboard needs HTTPS; elsewhere the message shows the code. The offer cards are
blocks: keep them in step with DATA.promos. "Book with this offer" links to
/booking/?promo=LONGSTAY. The terms are Details blocks.
Dining menu tabs#
The menus are accessible tabs: arrow keys move between them, Home and End jump to the first and
last, and only the selected tab is in the tab order. To add a menu, add a tab button in the tab
list's Custom HTML (id="tab-wine", aria-controls="panel-wine") and a group with the anchor
panel-wine and the classes ffa-role--tabpanel ffa-aria-labelledby--tab-wine ffa-tabindex--0 ff-hidden.
Experiences filter#
The filter buttons (data-exp-filter) show the cards whose ffd-type--… class matches and update
the count. A matching hash opens a category directly: /experiences/#sea. "Ask the concierge"
links to the contact form with ?topic=experiences&about=boat#contact-form, which selects the
topic and starts the message (the about keys are in initContactPrefill()).
Header, mobile menu, scroll reveals and back to top#
- The header gains a border and shadow once the page scrolls.
- The menu button opens the full-height mobile menu, updates
aria-expandedand its label, and Esc closes it and returns focus to the button. - Blocks with the
revealclass fade in as they scroll into view; revealing one also reveals everything above it. With reduced motion, or without JavaScript, everything shows at once. - A "Back to top" button appears after about one screen of scrolling; it moves focus to the main
content, lifts itself above the toast and hides while it would cover the room page's sticky
price card. 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, ink), Accent (coral), Outline (WordPress's own, drawn as the template's outline button), Light (on dark), Outline (on dark) |
Forms#
| Form | Page | Checks |
|---|---|---|
| Booking bar | Home | dates |
| Price form | Room | dates, capacity, availability |
| Booking | Book your stay | each step |
Table request (data-form="table") |
Dining | required fields, email, date from today |
Contact (data-form="contact") |
Location & contact | name, email, message |
Newsletter (data-form="newsletter") |
footer of every page |
Every form[data-form] is handled by initForms() in assets/js/main.js: errors appear under
each field, focus moves to the first problem, and the success message is shown. The forms don't
send anything until you connect them.
Option 1: a form plugin#
Install a form plugin (WPForms, Gravity Forms, Fluent Forms, Contact Form 7, Jetpack Forms...),
build the form there, then replace the form's Custom HTML block (and its success message) with
the plugin's block. Give its fields the class field and its labels field-label to match the
theme. For table requests and bookings, a booking engine's widget does the same job.
Option 2: a form service#
Keep the theme's form and send it to a service such as Formspree: in
initForms() (in a child theme's copy of assets/js/main.js), replace the comment in the submit
handler with a request, and show the success message when it succeeds:
fetch('https://formspree.io/f/your-id', { method: 'POST', body: new FormData(form), headers: { Accept: 'application/json' } })
.then(function (res) {
if (!res.ok) throw new Error('Request failed');
form.reset();
if (success) { success.classList.remove('hidden'); success.focus(); }
});and remove the lines after it that show the success message straight away. Branch on
form.dataset.form (table, contact, newsletter) for different endpoints, and update the
success messages, which say the forms are demos.
Saved data (localStorage)#
| Key | Shape |
|---|---|
casa-marea:booking:v1 |
{ checkin, checkout, adults, children, room, extras, promo }: the booking in progress |
The booking bar, the room page and the booking flow share this draft. Every read is checked
(sanitizeDraft()): dates must be real dates, adults 1 to 4 and children 0 to 2, the room and
extras known ids, the code 3 to 20 letters or digits; anything else is dropped and the stored value
repaired. Values in the URL win over the draft. When the booking flow restores a room that can't
sleep the restored guests, guests from the URL keep their number and get the first room that sleeps
them; otherwise the guests are reduced to the room's capacity (adults first). The draft is cleared after a confirmed booking.
SEO and sharing#
Page titles come from WordPress: the page title, an em dash and the site name (for example
"Dining — Casa Marea"); the home page shows the site name and tagline (Settings > General); the
room page's title follows the room shown. 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. WordPress
already generates a sitemap at /wp-sitemap.xml. For a hotel, add your address and phone as
structured data (Hotel or LodgingBusiness) with the SEO plugin, and keep them identical on
your Google Business Profile.
Accessibility#
Built in: a skip link to the main content (added by WordPress, styled by the theme), landmarks
(header, navigation with labels, main, footer), one h1 per page, visible focus rings, the mobile
menu button with aria-expanded, accessible tabs, a dialog for the lightbox, a calendar of buttons
with full date labels, live counts and price breakdowns announced to screen readers, form errors
announced through aria-describedby and aria-invalid, alt text on pictures, 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 (<label for="...">).
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-stay, 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-stay. All text in patterns,
templates and PHP goes through WordPress's translation functions; the script's strings (room
names and descriptions, extras, offers, prices, calendar labels, messages) are translated in PHP
(freeframe_stay_script_strings() in inc/setup.php). Create a translation with Loco Translate,
or with WP-CLI and Poedit:
wp i18n make-pot wp-content/themes/freeframe-stay wp-content/themes/freeframe-stay/languages/freeframe-stay.potSave freeframe-stay-it_IT.mo (for example) in wp-content/languages/themes/ or in the theme's
languages/ folder. Page content created by Stay setup is copied into your pages in the site's
language at that moment; translate it in the editor like any other content. Dates and prices are
formatted by the script in British English (en-GB, dayFmt, monthFmt and money()); change
the locale there for another language. Filter values (data-filter, data-exp-filter, the
ffd-… classes) stay in English; translate only their labels.
Child themes#
Use a child theme for changes you want to keep when Stay is updated (your rooms and prices in
assets/js/main.js especially). Create wp-content/themes/stay-child/style.css:
/*
Theme Name: Stay Child
Template: freeframe-stay
*/Templates, parts, patterns and theme.json in the child theme override the parent's files of the
same name. To use your own copy of the script, copy assets/js/main.js into the child theme and,
in its functions.php, load it in place of the parent's (wp_deregister_script( 'freeframe-stay' )
and wp_enqueue_script() on wp_enqueue_scripts, priority 30), keeping the data the parent
prints before it (window.freeframeStayL10n, window.freeframeStayPages,
window.freeframeStayAssets). 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.
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 > Stay.
- A room card doesn't show up in a filter. Check its
ffd-view--,ffd-bed--andffd-capacity--classes: the values must match the filter's options. - The room page shows the wrong prices or photos. It uses
DATA.roomsinassets/js/main.js, not the blocks on the page; theroomin the URL must be a room id. - Links from the script go to missing pages. Room cards and the booking bar link to the pages
with the demo's slugs (
/room/,/booking/); if you renamed them, change the slugs back or editfreeframe_stay_page_urls()in a child theme. - Stay 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 > Stay pages.
- The booking flow or the forms don't send anything. They are demos; see Booking flow and Forms.
- A page shows the title twice or no title. Pages use a template without a title, because the page patterns start with their own heading. For a plain text page (privacy policy, terms), choose the Page with title template in the page's settings.
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.
- Data and ARIA attributes on content blocks are written as classes and restored by the script (see Content).
- The room cards' picture links keep the picture's alt text as their name (the HTML version adds
an
aria-labelsuch as "Sea View Room: view room").
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 SVG artwork. Bodoni Moda and Jost
are licensed under the SIL Open Font License 1.1 (assets/fonts/*/OFL.txt). Casa Marea, its staff
and its guests are fictional.