Overview#
Clinic is a calm, accessible website for a dental or medical practice, built as a WordPress block
theme. The demo practice is Brightside Dental, a fictional family dental clinic. The theme has a
home page with a live open-now badge and the next free appointment, a services page with typical
costs, a detailed treatment guide (dental implants), a team page with a specialty filter, a five-step
online booking wizard, a patient information page with a searchable insurance list, a contact page
and a 404 page, all with larger text and high contrast for easy reading. 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/clinic.
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.
All treatment descriptions, prices and aftercare notes are placeholder copy, not medical advice. Replace them with your practice's own information.
Quick start#
Requirements: WordPress 7.1 or newer and PHP 7.4 or newer. No plugins are needed.
- Download
clinic.zipfrom freeframe.dev/templates/clinic (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 Clinic.
- Go to Appearance > Clinic setup and click Create the demo pages. This creates the Home, Services & costs, Treatment guide, Our team, Book appointment, Patient info and Contact pages from the theme's page patterns, sets Home as the front page (Settings > Reading) and creates the header menu. Pages that already exist with the same slug are left alone, so it is safe to click it again.
- Set the site title to your practice's name in Settings > General (the demo uses "Brightside Dental"), then edit any page in Pages, and the header, footer and templates in Appearance > Editor.
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 > Clinic pages) made of section patterns. Every section is also offered on its own in the block inserter under Patterns > Clinic.
| Page | Slug | Page pattern | Sections (pattern files in patterns/) |
|---|---|---|---|
| Home | / |
page-home.php |
home-hero (hero, illustration, next free slot card), home-services, why-us, team-preview, reviews (rating breakdown and quotes), insurers, visit-us (open-now badge, opening hours, map), cta |
| Services & costs | /services/ |
page-services.php |
services-hero (with links to each category), services-list (four categories of expandable treatments with typical costs, payment plans prompt) |
| Treatment guide | /service/ |
page-service.php |
treatment-hero, treatment-guide (step-by-step timeline, aftercare, FAQ, sticky cost breakdown, clinician card), related-treatments |
| Our team | /team/ |
page-team.php |
team-hero, team-grid (specialty filter and eight team members), training |
| Book appointment | /appointments/ |
page-appointments.php |
booking-hero, booking-wizard (wizard and live summary) |
| Patient info | /patients/ |
page-patients.php |
patients-hero, patients-info (first visit, insurance search, forms, payment options, FAQ, privacy) |
| Contact | /contact/ |
page-contact.php |
contact-hero, contact-details (emergency notice, form, opening hours, contact cards, map, directions, accessibility) |
| 404 | any missing URL | templates/404.html |
404 |
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-clinic/
style.css theme header (name, version, requirements, license); no styles
theme.json design tokens: palette, fonts, font sizes, spacing, layout widths, button
functions.php loads the files in inc/
inc/setup.php stylesheet and script, editor styles, the "js" class script, script strings,
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/navigation.php current-page markers (aria-current) in the header
inc/site-setup.php Appearance > Clinic setup: creates the demo pages and the menu
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: settings (hours, clinicians, treatments), hours,
forms, team filter, insurance search, booking wizard, back to top
assets/js/editor.js block editor tweaks
assets/icons/ SVG icons for the Icon block
assets/img/ portraits (<clinician-id>.svg), hero.svg, map.svg, favicon, social image
assets/fonts/ the Figtree and Fraunces font files 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-clinic.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:
| Colours | Used for |
|---|---|
| Brand 50 to Brand 900 | the teal of buttons, links, eyebrows, icons, badges, focus rings and the dark footer |
| Ink | headings and strong text |
| Contrast | body text (also the page's text colour) |
| Muted | secondary text |
| Line | borders and dividers |
| Sky Soft, Cream | soft section backgrounds |
| Sun, Coral | warm accents (the emergency bar icon, the hand-drawn underline, the newsletter button) |
| Base, White, Black | the page background, and plain colours for your own blocks |
The theme's stylesheet reads these colours from the palette, so changing Brand 700 in Styles
recolours every primary button, link and accent at once. Keep a full ramp from light (50) to dark
(900), and keep Brand 600 and darker dark enough for white text (WCAG AA). In theme.json:
{ "slug": "brand-700", "name": "Brand 700", "color": "#1b625b" }The browser bar colour (<meta name="theme-color">) is set in freeframe_clinic_head_meta() in
inc/setup.php. A few decorative details have their colour written into src/theme.css (the yellow
underline under "at ease", the select arrow) and the illustrations in assets/img/ have their own
colours.
Typography#
Text uses Figtree (variable, weights 400 to 800) and headings use Fraunces (variable, weights
500 to 700), bundled in assets/fonts/ and declared in theme.json
(settings.typography.fontFamilies), so no request goes to Google. The classes h-display,
section-title and font-display use Fraunces. 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 stylesheet reads --wp--preset--font-family--figtree and
--wp--preset--font-family--fraunces, so rename those slugs only together with src/theme.css.
The base text size is slightly larger than usual (17 px: html { font-size: 106.25% } in
src/theme.css) for readability; keep it that way. Font sizes are the presets in theme.json (xs
0.75rem to 9xl 8rem, the Tailwind scale), relative to that base. 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 > Clinic.
The practice name, phone number, email and address appear in the header, footer and several sections: search for them in each page and in the header and footer patterns.
Treatments (services page) and questions (treatment guide, patient info) are Details blocks: the summary line holds the treatment name and its price range, the inside holds the description. To add one, duplicate a Details block in List View.
Some content is in Custom HTML blocks, because blocks can't express it or a script drives it: edit those in the block's HTML view.
| Pattern | Custom HTML | What it is |
|---|---|---|
visit-us, contact-details, footer |
opening hours | the printed hours, with today highlighted (see Opening hours) |
visit-us, contact-details |
open-now badge | filled in by assets/js/main.js |
team-grid |
the filter buttons and the "Showing …" line | see Team filter |
booking-wizard |
the wizard and the summary card | see Booking wizard |
patients-info |
the insurance search and its list | see Insurance search |
contact-details |
the contact form | see Forms |
footer |
the newsletter form, and the toast used for short messages | see Forms |
header |
the menu button | a button driven by assets/js/main.js |
A few blocks carry a class (Block settings > Advanced > Additional CSS class(es)) that the script or stylesheet reads:
revealfades a block in as it scrolls into view; a class such as[--d:80ms]delays it.link-cardon a group makes the whole group clickable: the first link inside it stretches over the group.ff-specialty-<key>on a team member andff-filter-gridon their container: the team filter (see below).ff-next-sloton the home page paragraph that shows the next free appointment.ff-demo-downloadon the downloadable forms (patient info): the link shows a message instead of downloading. Upload your PDF to the Media Library, then remove the class and link to the PDF to make it a real download.ff-underline(the hand-drawn line under "at ease"),ff-hero-blobsandff-cta-blobs(the soft shapes behind the page headers and the call to action) are drawn by the stylesheet.
Images and artwork#
- Portraits are SVG illustrations in
assets/img/(dr-okoye.svg,jordan-ellis.svg…), shown with Image blocks. Replace them with photos from the Media Library, in the same portrait proportions (6:7, cropped from the top). The booking wizard showsassets/img/<clinician-id>.svg(see Booking wizard): replace those files, keeping their names, to change the pictures there too. - Hero illustration (
hero.svg) and map (map.svg) are Image blocks: replace them with a photo of your practice and a map image or screenshot (embed an interactive map with the Embed block or a Custom HTML block if you prefer). - Logo: the header and footer show
assets/img/favicon.svgin an Image block next to the practice name (a paragraph with a link: "Brightside" and "Dental" in two styles). Edit both in Appearance > Editor > Patterns > Header (and Footer). - Icons: icons are Icon blocks. Select one and pick another icon from the Clinic collection
(the theme's icons) or WordPress's own. Icons inside a button (the calendar and phone) or after a
link (the arrows) are drawn by CSS: the class
icon-before-calendaroricon-after-arrow-rightadds one (icon-size-20makes it 1.25rem); the available names are insrc/icons.css. - 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.
Above the header is the emergency bar with the phone number.
The header has two Navigation blocks showing the same menu: the desktop one and the one in the
mobile menu. Edit the menu once in Appearance > Editor > Navigation (the setup creates "Main
menu" with Home, Services, Our team, Patients and Contact). The Home link has the class
ff-mobile-only (link settings > Advanced), so it only shows in the mobile menu, as in the HTML
version. A Navigation block that doesn't point at a menu shows the most recently created one; to pin
a menu, select the Navigation block and choose it in the block settings, for both blocks.
WordPress marks the menu link of the page being viewed (aria-current="page"). The theme also marks
the header's buttons that link to the current page (the "Book appointment" buttons on the booking
page) and keeps Services current on the treatment guide (freeframe_clinic_menu_parents() in
inc/navigation.php, filterable with freeframe_clinic_menu_parents).
The footer's Explore column is a List block: click a link to change it, press Enter to add one.
Features#
Opening hours and the open-now badge#
The opening hours used by the scripts are at the top of assets/js/main.js (section 1, Settings):
// Opening hours per weekday (0 = Sunday). Times are 24h decimals: 8.5 = 8:30 am.
var HOURS = {
0: null,
1: [8, 19], 2: [8, 19], 3: [8, 19], 4: [8, 19],
5: [8, 16],
6: [9, 14]
};
var TIMEZONE = 'America/Los_Angeles'; // the practice's time zone (IANA name)The open-now badge (data-open-status) shows "Open now · closes at 7:00 pm" or "Closed now · opens
tomorrow at 8:00 am" and refreshes every minute. Hours lists (data-hours) highlight today's row
with data-today="true" and aria-current="date"; data-day on each row uses the same numbers
as HOURS (0 = Sunday … 6 = Saturday). Both use the time in TIMEZONE, so every visitor sees
the practice's day and time, wherever their device is. Set TIMEZONE to your practice's zone.
The printed hours are Custom HTML blocks (in the visit-us and contact-details patterns, so
in the Home and Contact pages, and in the footer pattern): keep them in step with HOURS.
Next free slot#
On the home page, the paragraph with the class ff-next-slot shows the earliest free 60-minute
slot in the next two weeks across all clinicians, for example "Tomorrow, 9:30 am".
Booking wizard#
The Book appointment page contains the wizard (form[data-booking], a Custom HTML block) with five
steps: Treatment, Clinician (the people who offer it, plus "First available"), Date and
time (a week view, up to WEEKS_AHEAD weeks, and time slots), Your details and Review
(every choice with a "Change" link back to its step). Link to /appointments/?service=implant to
preselect a treatment. A live summary card follows the choices; each step is validated before moving
on, and focus moves to the step's heading. The confirmation says the practice would confirm by
email; nothing is booked.
The settings are in section 1 of assets/js/main.js:
var LUNCH = [12.5, 13.5]; // no appointments during lunch (weekdays)
var SLOT_STEP = 30; // minutes between appointment start times
var MIN_NOTICE_MINUTES = 90; // earliest bookable time from now
var WEEKS_AHEAD = 4; // how far ahead people can book
var CLINICIANS = {
'dr-okoye': { name: __('Dr. Amara Okoye'), role: __('General & family dentist'), days: [1, 2, 3, 4, 5] },
…
};
var SERVICES = [
{ id: 'checkup', name: __('Check-up & cleaning'), minutes: 60, note: __('Exam, clean and polish'), who: ['dr-okoye', 'jordan-ellis', 'sofia-marchetti'] },
…
];days lists the weekdays a clinician works (0 = Sunday); minutes sets a treatment's length and
who lists the clinician ids who offer it. Slots start every SLOT_STEP minutes from opening as
long as the appointment ends by closing time, skip LUNCH on weekdays and anything sooner than
MIN_NOTICE_MINUTES, only count clinicians who work that day, and some are marked as taken by
isTaken(), a fixed pattern so the demo looks realistic. The wizard shows each clinician's portrait
from assets/img/<id>.svg (the theme passes the URLs of its images to the script), so add a portrait
with the same file name when you add someone. __() translates the names (see
Translations); add new strings to freeframe_clinic_script_strings() in
inc/setup.php.
The team page and treatment pages are blocks, so update them separately when you change
CLINICIANS or SERVICES, and copy your changes to assets/js/main.js in a child theme (or they
are overwritten when you update the theme).
To take real bookings, replace isTaken() with availability from your scheduling system and send
the booking in the submit handler (the comment "To take real bookings"). Many practices embed their
practice-management or booking provider's widget instead: on the Book appointment page, delete the
wizard's Custom HTML block and the summary card and add the provider's embed code in a Custom HTML
block.
Team filter#
On the team page, the buttons with data-filter (all, general, ortho, kids, surgery,
cosmetic, hygiene, care, in a Custom HTML block) show the team members whose group has the
class ff-specialty-<key>, inside the group with the class ff-filter-grid, and update the
"Showing …" line. To add a specialty, add a button with a new data-filter value and give the team
members the class ff-specialty-<value>.
Insurance search#
On the patient info page, the search field (data-plan-search) filters every data-plan item by its
text, updates the count (data-plan-count) and shows the "couldn't find that plan" message
(data-plan-empty) when nothing matches. Add an insurer by adding another <li … data-plan> in the
Custom HTML block.
Treatments and questions#
The Details blocks open and close without JavaScript and are found by the browser's find-in-page.
The plus sign that turns into a cross is drawn by the stylesheet (.faq summary::before and
::after in src/theme.css); the class faq gives a Details block that look.
Sticky header, mobile menu and scroll reveals#
The header sticks to the top and gains a shadow once the page scrolls (is-scrolled). Below 1024 px
the menu button (data-menu-toggle) opens #mobile-menu, swaps its icon, updates aria-expanded
and its label, and closes with Escape (returning focus to the button) or when the window grows past
1024 px. The mobile menu Group starts with the class hidden; the script turns it into the hidden
attribute it toggles. Blocks with reveal fade in when scrolled into view (everything above is
revealed too). They are only hidden once JavaScript runs (html.js), so the content is there without
it, and they appear immediately with reduced motion.
Back to top#
A button appears after scrolling about one screen down; it scrolls to the top (instantly with
reduced motion), moves keyboard focus to the main content and lifts above the toast. The sticky cost
card on the treatment guide has the class ff-back-to-top-hide, so the button never covers it. It is
created by assets/js/main.js; to remove it, delete the "Back to top" part of that file and the
.back-to-top styles in src/theme.css, then run pnpm build.
Block styles#
In the block settings' Styles panel:
| Block | Styles |
|---|---|
| Button | Fill (default, the primary teal), Outline, Ghost, White (on teal), Outline (on teal), Chip (WordPress's own Outline style is replaced by the theme's) |
Add the class is-small to a Button block for the small size used in the header.
Forms#
| Form | Where | Notes |
|---|---|---|
| Contact | Contact page (contact-details pattern) |
its own thank-you text in data-success-text |
| Newsletter | Footer | email only |
Booking wizard (data-booking) |
Book appointment page | see Booking wizard |
Forms marked data-validate are checked by validateField() in assets/js/main.js: required fields,
email format, phone numbers on fields with data-phone (at least 7 digits), minlength, and dates
of birth in the future. Errors appear under each field, are linked with aria-describedby, and the
first invalid field is focused. On success the [data-success] element shows the form's
data-success-text. Nothing is sent anywhere: they are demo forms until you connect them.
Option 1: a form plugin#
The usual way on WordPress. Install a form plugin (WPForms, Gravity Forms, Fluent Forms, Contact
Form 7, Jetpack Forms...), build the form there, then on the Contact page delete the form's Custom
HTML block and insert the plugin's block or shortcode in its place. The plugin sends the messages
and stores them. To make its fields look like the theme's, add the theme's classes where the plugin
allows custom classes: field on fields, label on labels, btn btn-primary on the submit button.
Option 2: a form service#
Keep the theme's form and send it to a service such as Formspree:
-
Create a form on the service and copy its endpoint URL.
-
Edit the Contact page, open the form's Custom HTML block and add the endpoint to the form tag:
html<form class="card grid gap-5 p-6 sm:p-8" novalidate data-validate data-success-text="Thank you!" action="https://formspree.io/f/your-id" method="post"> -
In section 5 of
assets/js/main.js, replace the success code in the submit handler (after the comment "To send for real") so it posts the form and only shows the thank-you text when the service accepts it:jsfetch(form.action, { method: 'POST', body: new FormData(form), headers: { Accept: 'application/json' } }) .then(function (res) { if (!res.ok) throw new Error('Request failed'); var success = $('[data-success]', form); success.textContent = form.dataset.successText || __('Thank you!'); success.classList.remove('hidden'); form.reset(); }) .catch(function () { toast('Sorry, that didn’t send. Please call us instead.'); });Make this change in a child theme (copy
assets/js/main.jsthere and load it instead), or it will be overwritten when you update the theme.
Health information is sensitive: send forms only over HTTPS to services you trust, ask for the minimum needed, and check your local privacy rules (for example HIPAA in the US or GDPR in the EU) before collecting medical details online.
Saved data (localStorage)#
Clinic stores nothing in the visitor's browser.
SEO and sharing#
Page titles come from WordPress: the page title, an em dash and the site name (for example
"Services & costs — Brightside Dental"); 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. Local practices benefit from
Dentist or MedicalClinic structured data (address, phone, opening
hours), which most SEO plugins can add. WordPress already generates a sitemap at /wp-sitemap.xml.
Accessibility#
Clinic is built for patients of all ages and abilities: larger base text, high contrast, thick
focus rings, a skip link to the main content (added by WordPress, styled by the theme), labelled
landmarks (header, navigation, main, footer), one h1 per page, the mobile menu button with
aria-expanded, filter buttons with aria-pressed, a booking wizard with fieldsets and legends,
focus moved to each step's heading, the current step marked with aria-current="step" and each day
button labelled with its date and availability, form errors announced and linked to their fields,
aria-current on the current page's links and today's opening hours, 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, keep text contrast at WCAG AA, give images 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/ (including the classes the booking wizard writes):
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-clinic, 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-clinic. All text in patterns,
templates and PHP goes through WordPress's translation functions; the script's strings (status
texts, day and month names, treatments, clinicians, form errors, wizard texts) are translated in PHP
(freeframe_clinic_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-clinic wp-content/themes/freeframe-clinic/languages/freeframe-clinic.potSave freeframe-clinic-de_DE.mo (for example) in wp-content/languages/themes/ or in the theme's
languages/ folder. Page content created by Clinic 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 Clinic is updated. Create
wp-content/themes/clinic-child/style.css:
/*
Theme Name: Clinic Child
Template: freeframe-clinic
*/Templates, parts, patterns and theme.json in the child theme override the parent's files of the
same name. To change the booking settings, copy assets/js/main.js to the child theme and load it
instead of the parent's:
add_action( 'wp_enqueue_scripts', function () {
wp_deregister_script( 'freeframe-clinic' );
wp_register_script( 'freeframe-clinic', get_stylesheet_directory_uri() . '/assets/js/main.js', array(), '1.0.0', array( 'in_footer' => true, 'strategy' => 'defer' ) );
wp_enqueue_script( 'freeframe-clinic' );
}, 15 );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.
Troubleshooting#
- The open-now badge or the booking times are an hour or more out. Set
TIMEZONEat the top ofassets/js/main.jsto your practice's IANA zone name (for example'America/Chicago'). - Today's row in the hours list is off by one day.
data-daymust use the same numbers asHOURS(0= Sunday …6= Saturday). - A treatment has no free days. Check that its
whoclinicians work on open days (daysinCLINICIANS) and that the appointment length fits within the hours outsideLUNCH. - A clinician's portrait is missing in the wizard. Add
assets/img/<clinician-id>.svgto the theme (or child theme folder you load the script from). ?service=doesn't preselect a treatment. The value must match anidinSERVICES.- The team filter shows nobody. Each team member's group needs the class
ff-specialty-<key>with a key used by a filter button, inside the group withff-filter-grid. - 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 > Clinic.
- The header menu shows other pages, or an old menu. A Navigation block without a chosen menu shows the most recent one. Select it and choose "Main menu" (or yours) in the block settings, in both the desktop and the mobile Navigation block.
- Clinic 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 > Clinic pages.
- The forms don't send anything. They are demo forms; see Forms.
- The theme can't be installed on an older WordPress. Clinic uses the Icon block's icon collections from WordPress 7.1; update WordPress first.
- 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.
License#
MIT, which is compatible with the GPL that WordPress requires (see LICENSE.txt in the theme
folder). Free for personal and commercial use. The illustrations, portraits and map are included
under the same license. Figtree and Fraunces are licensed under the SIL Open Font License 1.1
(assets/fonts/figtree/OFL.txt, assets/fonts/fraunces/OFL.txt).