Freeframe

Documentation

Fade

v1.0.1Updated Oct 16, 2026A bold, vintage-modern barbershop website with live wait times, online booking, a product shop and gift cards.

Overview#

Fade is a website for a barbershop, built as a WordPress block theme. The demo brand is Fade Bros. Barber Co., a fictional independent barbershop with two shops in Chicago: Logan Square (2741 N Milwaukee Ave) and Pilsen (1532 W 18th St). The theme has a home page with an illustrative "current wait" board for each shop, a services preview, the barbers, the house rules, reviews, both shops with live open-now badges and a gift card call to action; a services page with a price list, a category filter and an FAQ; a barbers page with shop and speciality filters and each barber's next free time; a five-step booking flow that works out time slots from each barber's schedule and the length of the chosen services; a shop with a saved bag and a gift card builder; locations with a map, hours, transit, parking and the walk-in wait; an about page with the story, a timeline, the standards and careers; a contact page; and a 404 page. It is version 1.0.0 (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/fade.

Headings, copy, cards, lists, buttons and images are core WordPress blocks arranged in block patterns, so you edit them in the block editor without touching code. The interactive parts (the wait board, the filter chips, the booking flow, the bag, the gift card builder and the forms) work as in the HTML version: they are small Custom HTML blocks driven by assets/js/main.js. Colours and fonts are design tokens in theme.json: change them in Appearance > Editor > Styles and the whole site follows.

Quick start#

Requirements: WordPress 7.1 or newer and PHP 7.4 or newer. No plugins are needed.

  1. Download fade.zip from freeframe.dev/templates/fade (the WordPress tab).
  2. 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 Fade.
  3. Go to Appearance > Fade setup and click Create the demo pages. This creates the Home, Services & prices, Barbers, Book a chair, Shop & gift cards, Locations & hours, About & careers 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.
  4. Visit your site. 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:

sh
pnpm install
pnpm dev     # builds the CSS, then starts WordPress with the theme at http://127.0.0.1:9400

pnpm 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 > Fade pages) made of section patterns. Every section is also offered on its own in the block inserter under Patterns > Fade.

Page Slug Page pattern Sections (pattern files in patterns/)
Home / page-home.php home-hero (wait board and stamp), marquee, menu-teaser (four service tickets), team-teaser (four barbers), house-rules, reviews, shops (both shops with open-now status), gift-teaser
Services & prices /services/ page-services.php services-hero, service-menu (category filter and the price list), services-faq, services-cta
Barbers /barbers/ page-barbers.php barbers-hero, barber-list (filters and six profiles), barbers-cta
Book a chair /book/ page-book.php book-hero, booking (the five steps and the summary)
Shop & gift cards /shop/ page-shop.php shop-hero, products (filter, eight products and the bag), gift-cards (builder and live preview)
Locations & hours /locations/ page-locations.php locations-hero, map, location-logan, location-pilsen (address, transit, parking, hours, walk-in wait), wait-model
About & careers /about/ page-about.php about-hero, story (storefront illustration), timeline, standards, careers
Contact /contact/ page-contact.php contact-hero, contact-form (form, both shops, group bookings)
404 any missing URL templates/404.html 404

Every page shares the header (with the bag count) and the footer (open-now badges for both shops and the newsletter form). 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 page of text, choose the Page with title template.

Project structure#

freeframe-fade/
  style.css                 theme header (name, version, requirements, license); no styles
  theme.json                design tokens: palette, fonts, spacing, layout widths, button
  functions.php             loads the files in inc/
  inc/setup.php             stylesheet and script (with its strings), editor styles, the `js`
                            class script, theme colour and favicon, 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 > Fade 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 FADE data, open-now, wait estimate, filters, booking, bag, gift cards, forms
  assets/icons/             SVG icons for the Icon block
  assets/img/barbers/       barber portraits, one SVG per barber id (400 x 480)
  assets/img/               logo, favicon, social image
  assets/fonts/             Anton and Archivo, with their licenses
  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-fade.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.txt

Customising#

Brand and colours#

The colours are in theme.json (settings.color.palette) and in Appearance > Editor > Styles > Colors > Palette:

Colour Default Used for
Paper #f2ead9 the page background
Card #fbf7ef cards and inputs
Sand #e8dcc4 soft panels and alternate sections
Line #cdbd9f hairlines on paper
Ink #151515 text, borders, hard shadows, dark sections
Coal #24211e raised surfaces on dark (the wait board)
Mute #5a5249 secondary text
Barber red, Barber red dark #b5281c, #8f1c12 buttons, eyebrows, links, the button hover
Barber blue #1d3557 the blue in the barber-pole stripes
Brass #e0a83a accents on dark backgrounds only
Go green #2c6a37 "Open" dots and positive states
Error #b3261e form errors
White, Black for your own blocks

The theme's stylesheet reads these colours from the palette (bg-red, text-mute, border-brass...), so changing Barber red in Styles recolours every red button, eyebrow and link at once. Keep text colours at 4.5:1 contrast or more against Paper, Card and Sand; Brass is only for text on Ink. The barber-pole stripes are the pole-stripe utility in src/theme.css (a repeating-linear-gradient of red, paper and navy); the header and footer use it through the pole-band group, and the home hero and the 404 through ff-pole. The SVG artwork (portraits, products, the map, the storefront, the stamp, the logo and the social image) has its colours written in: search for the old values (for example #b5281c) to change them too.

Typography#

Two fonts are bundled in assets/fonts/ and declared in theme.json (settings.typography.fontFamilies), so no request goes to Google: Anton (preset display) for headings, set in uppercase, and Archivo (preset sans), a variable font with weights 400 to 900 and a width axis from 62% to 125%. The condensed class and the button, tag and label styles use font-stretch for the narrow, sign-painter look. To use other fonts, install them in Styles > Typography > Fonts (the Font Library) and select them, or replace the fontFace entries in theme.json. The utility classes read the presets (--wp--preset--font-family--display and --sans), so keep those slugs. The SVGs with text name the fonts directly (font-family="Anton, Impact, sans-serif").

The classes font-display (Anton, uppercase), condensed (narrow Archivo), eyebrow (the small red label with a rule), section-title (the big section heading) and lede (the larger intro paragraph) give text the template's styles.

Content#

Edit pages in Pages: click any text to change it, select a button to change its link. 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 > Fade.

What the script drives, and pictures drawn in SVG with text, are Custom HTML blocks: edit those in the block's HTML view, and keep their ids and data- attributes.

Pattern Custom HTML What it is
home-hero the stamp, the wait board see Current wait estimate
marquee, gift-teaser, 404 the scrolling services, the gift card stack, the razor decorative pictures and text
service-menu the category chips and the count see Services filter
barber-list the shop and speciality chips, the count, the empty message see Barber filters
booking the five steps and the summary see Booking flow
products the product chips, the count, the product pictures, the "Add to bag" buttons, the bag see Bag and checkout
gift-cards the builder form, the live preview and its announcement see Gift card builder
map, story the map of both shops, the storefront SVG pictures
location-logan, location-pilsen the hours table, the walk-in wait card see Open now and hours
contact-form the form and its success message see Forms
header, footer the bag link, the menu button, the newsletter form driven by the script

Some blocks carry classes (Block settings > Advanced > Additional CSS class(es)) that the script reads. Blocks can't store data- attributes or hidden, so the script turns classes starting with ffd- into attributes when the page loads, and ff-hidden into hidden: ffd-open-now--logan on a badge becomes data-open-now="logan", and ffd-locs--logan_pilsen on a barber card becomes data-locs="logan pilsen".

  • reveal fades a block in as it scrolls into view.
  • card, card-shadow, ticket, tag, tag-solid, status, link-arrow and text-link are the template's card, ticket, tag, badge and link looks.

The data the scripts need lives in one object, FADE, at the top of assets/js/main.js:

Key What it holds
shop Name, email, timeZone (America/Chicago), currency and locale
locations The two shops: id, address, phone, number of chairs, hours per weekday (0 = Sunday, null = closed), transit and parking
categories Service categories (cuts, beard, shaves, kids, packages)
services Each service: id, cat, name, mins, price, optional needs and popular
specialties The speciality filter on the barbers page
barbers Each barber: id, short name, role, skills, specialties and schedule per shop per weekday
traffic The demo walk-in traffic curve used by the wait estimate
products, productCategories Shop products and their filter categories
giftDesigns, gift, shipping Gift card designs and limits, flat shipping and the free-shipping threshold

The visible lists (the price list, the barber cards, the product cards, the hours tables) are page content, so they are indexed by search engines and you edit them in the editor. When you change a service, barber or product, change it in both the page and FADE (in a child theme, see Child themes). Visible names in FADE are wrapped in __() so they can be translated.

Adding a service

Add an entry to FADE.services:

js
{ "id": "towel-lineup", "cat": "cuts", "name": __("Hot towel & line-up"), "mins": 20, "price": 24, "desc": "A line-up finished with two hot towels." }

Then, on the Services & prices page, duplicate a row of the Cuts group (a list item group with the class svc-row) in List View and change its name, description, time, price and the button link (/book/?service=towel-lineup). In the booking pattern's Custom HTML, copy a <li> of the Cuts list in step 2 and set value="towel-lineup". needs lists the skills a barber must have; it defaults to the service's own category. Packages use it, for example needs: ['cuts', 'shaves'] for The Full Service, so only barbers with both skills are offered.

Adding a barber

Add an entry to FADE.barbers, a portrait at assets/img/barbers/<id>.svg (the booking flow loads it by id), and a card on the Barbers page: duplicate a card (a list item group with the classes reveal ffd-barber--marcus ffd-locs--logan ffd-specs--fades_beards) and change its classes, picture, text, "Book with" link (/book/?barber=<id>) and the empty group with ffd-next-free--<id>, which the script fills with the next free time.

js
{
  "id": "nia", "short": __("Nia"), "name": __("Nia Brooks"), "role": __("Barber"), "years": 4,
  "skills": ["cuts", "beard", "kids"], "specialties": ["fades", "kids"],
  "bio": "…", "signature": "Burst fade",
  "schedule": { "logan": { "4": ["12:00", "20:00"], "5": ["12:00", "20:00"] } }
}

Keep barber schedules inside the shop's opening hours.

Images and artwork#

Everything is SVG, so there are no photos to license:

  • Barber portraits: assets/img/barbers/<barber id>.svg, 400 x 480 (5:6), in Image blocks on the home and barbers pages: select one and Replace it with a photo of the same proportions. The booking flow shows every SVG in that folder by barber id (freeframe_fade_barber_images() in inc/setup.php passes them to the script as window.freeframeFade.barbers): keep the file names, or change the *.svg pattern there if you use JPGs.
  • Products: drawn inline in each product card (.product-art, square) in Custom HTML blocks; replace the <svg> with an <img> of a square product shot.
  • Storefront (About) and map (Locations) are inline SVGs in Custom HTML blocks so they use the theme's fonts. Replace either with an Image block if you prefer a picture.
  • Icons are Icon blocks: select one and pick another from the Fade collection or WordPress's own.
  • Logo: assets/img/logo.svg (header) and assets/img/logo-light.svg (footer), in Image blocks next to the Fade Bros. name.
  • Social image: assets/img/og-image.png (1200 x 630, source og-image.svg). Favicon: assets/img/favicon.svg, used until you set a Site Icon in Settings > General.

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 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": Services, Barbers, Shop, Locations, About, Contact). 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 current page in the menu. The bag link (with its count) and the menu button are Custom HTML blocks; the "Book a chair" buttons are Button blocks.

The footer's links are List blocks: click a link to change it, press Enter to add one. The shops' addresses, phone numbers and open-now badges also appear on the home, locations and contact pages; search for them when you change them.

Features#

Booking flow#

The Book a chair page, section 9 of assets/js/main.js. Five steps inside one <form id="booking-form"> in the booking pattern's Custom HTML:

  1. Shop: radio cards with open-now status and who is in today.
  2. Services: checkboxes; the duration and total add up in the summary and the running total.
  3. Barber: "First available" plus every barber who works at the chosen shop and has the skills for every chosen service, with their next free time. Barbers left out are listed with the reason (for example "Rosa (no straight-razor shaves)").
  4. Time: a month calendar and the open times for the chosen day, grouped into morning, afternoon and evening.
  5. Details: name, mobile, email, notes, reminder and cancellation policy, all validated.

If nobody at the chosen shop can do every chosen service in one visit (a kids cut and a hot towel shave in Pilsen), step 2 explains it instead of continuing. The confirmation shows a summary, a reference and a button that downloads an .ics file. It clearly says nothing was booked.

URL parameters preselect choices: /book/?location=pilsen, ?service=fade, ?barber=rosa (a barber also sets their shop). Only ids that exist in FADE are accepted. The service tickets, price list, barber cards and shop cards link here with these parameters.

Accessibility: each step's heading receives focus when the step changes, and the step list marks the current step with aria-current="step". The calendar uses a roving tabindex: the arrow keys move by day and week, Home and End go to the start and end of the week, and Page Up and Page Down move four weeks. Each day's label says how many times are open; errors use role="alert".

Time slots#

Slots come from slotsFor(locId, key, ids, barberId, now):

  • The shop's hours that day, the barber's schedule at that shop, and the total minutes of the chosen services.
  • Start times every SLOT_STEP (15) minutes; today's earliest is LEAD_TIME (30) minutes from now.
  • Visitors can book BOOK_AHEAD (42) days ahead.
  • isBooked() marks about a third of each barber's half-hours as taken, using a stable hash so the diary looks real and doesn't change on reload. Replace it with your booking system's availability.
js
var SLOT_STEP = 15;      // minutes between start times
var LEAD_TIME = 30;      // earliest booking is this many minutes from now
var BOOK_AHEAD = 42;     // days visitors can book ahead (6 weeks)

All times are the shop's local time (FADE.shop.timeZone), whatever the visitor's own time zone is.

Connecting real bookings#

The flow is a front end only. To take real bookings, either replace the booking section's Custom HTML with your provider's embed (Square Appointments, Fresha, Booksy and Cal.com all offer one), or keep this flow and send the request where confirm() says so:

js
// Demo only: nothing is sent. Post { location, services, barber, date, time, details } to
// your booking provider here (Square Appointments, Fresha, Booksy or Cal.com APIs/embeds).

Booking APIs need a secret key, so call them from the server (a small plugin with a REST route, or a serverless function), never directly from the browser.

Current wait estimate#

The home page board ([data-wait-board] in home-hero) and the walk-in wait cards on the Locations page, section 6 of assets/js/main.js. Any element with data-wait="<shop id>" is filled in: [data-wait-num], [data-wait-detail], [data-wait-unit], [data-wait-chart] and [data-wait-chart-label].

estimateWait() multiplies the traffic level for this hour (FADE.traffic, 0 to 1, separate curves for weekdays, Saturday and Sunday) by 70 minutes, divides by the number of barbers working, adds a small stable variation and rounds to 5 minutes. It is illustrative: the page says so next to every number and the wait-model section of the Locations page explains it. To show real waits, replace estimateWait() with a call to your queue system, and remove the "Illustrative estimate" labels.

Add ?at=2026-10-17T10:30 to any page URL to preview the site at a shop-local time (useful for testing busy and closed states).

Open now and hours#

Section 5 of assets/js/main.js. Open-now badges (data-open-now="logan"; in blocks the class ffd-open-now--logan on a paragraph with the class status) show "Open now · until 8 pm", "Closing soon · 8 pm" (last 45 minutes) or "Closed · opens tomorrow 9 am". [data-today-hours] shows today's hours, and the current weekday's row in every [data-hours] table (the hours tables in the location patterns' Custom HTML) is highlighted and marked aria-current="date". Everything refreshes every minute. The hours tables are text: when you change FADE.locations[].hours, change them too.

Services filter#

The Services & prices page, section 7. Chips with data-filter (Custom HTML) show one category; the groups carry ffd-service-group--cuts and so on, and the count is announced with aria-live="polite". /services/?category=beard preselects a category.

Barber filters and next free time#

The Barbers page, section 8. Two chip groups filter by shop (data-barber-loc) and speciality (data-barber-spec); the cards carry their shops and specialities as classes (ffd-locs--, ffd-specs--). If no barber matches, an empty state offers to reset. ?location=pilsen and ?speciality=curly preselect filters. Each card shows the barber's next free time for a Classic cut.

Bag and checkout#

The Shop page, section 10. "Add to bag" buttons (data-add="<product id>") add products; the bag lists products with quantity steppers (1 to 10) and gift cards, offers pickup at either shop or shipping (FADE.shipping: $6, free over $50 in products) with a progress meter, and totals everything. "Checkout" shows a demo summary. The header bag icon shows the item count on every page and links to /shop/#bag.

To sell for real, send the bag lines from readBag() to Shopify (Buy Button or Storefront API), Square Online or Stripe Checkout where the [data-checkout] click handler is, or use a shop plugin such as WooCommerce instead.

Gift card builder#

The gift-cards section of the Shop page (/shop/#gift-cards), section 11. Preset amounts or a custom whole amount between FADE.gift.min and FADE.gift.max ($20 to $300), three designs (gift-pole, gift-stamp, gift-blackout in src/theme.css), recipient, sender, message (140 characters, with a counter) and "right away" or a date within a year. The preview updates as you type and a screen-reader summary is announced politely. A valid card is added to the bag as a gift line.

FAQ#

The FAQ on the Services & prices page is made of Details blocks (WordPress's own, native <details> and <summary>) with the class faq; the first one is open when the page loads (Open by default in its settings). Add a question by duplicating one in List View. The round plus/minus icon is drawn by CSS (.wp-block-details.faq > summary::after in src/theme.css).

Header, menu, reveals and back to top#

The header is sticky and gains a shadow after scrolling. Below 1024 px the menu button opens a full-screen menu (aria-expanded, Esc closes it and returns focus). Blocks with reveal fade up once as they scroll into view; nothing is hidden with reduced motion or without JavaScript. The marquee and the stamp stop moving with reduced motion. A back-to-top button appears after about one screen of scrolling; to remove it, delete the "Back to top" part at the end of assets/js/main.js 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 barber-red button with a hard shadow), Ink, Outline, Paper, Outline (on dark)

The class is-small on a Button block makes it the compact size. Icons on buttons are classes too (icon-after-arrow-right); see src/icons.css for the available icons.

Forms#

Form Where Validates
Booking details Book a chair, step 5 Name, 10-digit US mobile, email, policy checkbox
Gift card Shop Custom amount range, recipient name and email, sender, delivery date
Contact Contact Name, email, optional phone (10 digits if given), message of 20+ characters
Newsletter footer of every page Email

Errors appear inline next to each field, fields get aria-invalid, and focus moves to the first problem. On success each form shows a message that says it's a demo. No form sends anything until you connect it. /contact/?topic=careers (or group, chair-rental, feedback, press) preselects the topic; the careers cards on the About page link there.

To collect messages, use a form plugin (replace the form's Custom HTML block with the plugin's block; add the classes field-label, field and btn-red to its labels, fields and button), or send the form in assets/js/main.js (in a child theme). For Formspree, replace the comment in the contact form handler:

js
// Send new FormData(contact) to your endpoint here
fetch('https://formspree.io/f/your-id', {
  method: 'POST',
  body: new FormData(contact),
  headers: { Accept: 'application/json' }
}).then(function (res) {
  if (!res.ok) throw new Error('Send failed');
  contact.reset();
  ok.hidden = false;
  ok.focus();
});

Move the contact.reset() and success lines into the then so the message only shows after a real send, and update the success text in the contact-form pattern. The newsletter handler has the same kind of comment; point it at Mailchimp, Buttondown or your email tool.

Saved data (localStorage)#

Key Shape Validation on read
fade-bag { "items": [...], "delivery": "logan" | "pilsen" | "ship" } See below

This is the HTML version's key. Each item is either { type: "product", id, qty } or { type: "gift", key, amount, design, to, toEmail, from, message, date }. readBag() drops unknown product ids, duplicate lines, quantities that aren't whole numbers (and caps them at 10), gift cards with an amount outside the limits, an unknown design or an invalid email, and trims text to its maximum length. A cleaned version is written back. Bags open in two tabs stay in sync through the storage event.

To reset, empty the bag on the Shop page, or run localStorage.removeItem('fade-bag') in the browser console. Nothing else is stored.

SEO and sharing#

Page titles come from WordPress: the page title, an em dash and the site name (for example "Barbers — Fade Bros. Barber Co."); 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 x 630 image) as the default social image. WordPress already generates a sitemap at /wp-sitemap.xml. For local search, add BarberShop structured data with each shop's address, phone and opening hours; most SEO plugins can add it.

Accessibility#

Built in: a skip link to the main content (added by WordPress, styled by the theme), landmarks (header, labelled navigation, main, footer), one h1 per page, labelled sections, visible focus rings (red, brass on dark sections), the menu button with aria-expanded, filter chips with aria-pressed and live counts, a booking flow that moves focus to each step and a keyboard-operable calendar, native <details> for the FAQ, form errors linked with aria-describedby and aria-invalid, alt text or aria-hidden on every picture, and reduced-motion support for the reveals, the marquee and the stamp. Text meets WCAG AA contrast; the demo passes axe (WCAG 2.1 A and AA).

When you edit: keep one Heading 1 per page, give images alt text (or mark them decorative), keep Brass text on dark backgrounds only, 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/:

sh
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, template or the script's markup, 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-fade, 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-fade. All text in patterns, templates and PHP goes through WordPress's translation functions; the script's strings (shop, service, barber and product names, open-now and wait texts, the booking lines, the bag, form errors and messages, day and month names) are translated in PHP (freeframe_fade_script_strings() in inc/setup.php). Create a translation with Loco Translate, or with WP-CLI and Poedit:

sh
wp i18n make-pot wp-content/themes/freeframe-fade wp-content/themes/freeframe-fade/languages/freeframe-fade.pot

Save freeframe-fade-es_ES.mo (for example) in wp-content/languages/themes/ or in the theme's languages/ folder. Page content created by Fade setup is copied into your pages in the site's language at that moment; translate it in the editor like any other content. Prices are formatted with FADE.shop.locale and currency.

Child themes#

Use a child theme for changes you want to keep when Fade is updated (your shops, hours, services, barbers and products in particular). Create wp-content/themes/fade-child/style.css:

css
/*
Theme Name: Fade Child
Template: freeframe-fade
*/

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 assets/js/main.js, dequeue the parent's script (freeframe-fade) and enqueue yours in the child's functions.php, keeping the inline window.freeframeFade strings (or copy them). The parent's stylesheet only contains the classes the parent uses: if your child theme uses 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 pages look unstyled or new classes do nothing. The stylesheet only contains classes it found when it was built. Run pnpm build after 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 > Fade.
  • The wait board says "Closed" during your opening hours. Times are worked out in FADE.shop.timeZone. Set it to your shop's IANA zone (for example America/New_York) and check hours in FADE.locations (0 is Sunday).
  • A barber never shows up in booking. They must have the shop in schedule and every skill in the chosen services' needs (or category). Check skills spelling against the category ids.
  • No times on any day. The chosen services may be longer than the barber's shift, or the shift falls outside the shop's hours. Shorten the services or widen the schedule.
  • A new barber card doesn't show up under a filter. Its group needs the classes ffd-barber--<id>, ffd-locs--<shop ids> and ffd-specs--<speciality ids> (several joined with an underscore). See Adding a barber.
  • Fade 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 > Fade pages.
  • The booking flow, the bag or the forms don't send anything. They are demos; see Connecting real bookings and Forms.

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 barber portraits, product art, the storefront, the map, the stamp and the logo are original SVG artwork included under the same license. Anton and Archivo are licensed under the SIL Open Font License 1.1 (OFL.txt in each folder of assets/fonts/). All people, shops and reviews named in the demo are fictional.

Search documentation

Search the guides and every template's documentation