Freeframe

Documentation

Atlas

v1.0.0Updated Oct 15, 2026A bold, magazine-style personal brand site for a real estate agent, with listings, a home value estimate and calculators.

Overview#

Atlas is a personal-brand website for an independent real estate agent, built as a WordPress block theme. The fictional brand is Delia Quintero, a Nashville agent who trained as an architect and works in six neighborhoods. The theme has a home page with her portrait, "I'm buying" and "I'm selling" paths, an instant home value estimate, the homes she is listing now, her record in four count-up numbers, a review carousel, a neighborhood teaser and a booking call to action; a listings page with For sale, Under contract and Sold tabs, filters, sorting and shareable links; a listing page driven by ?id= with an illustrated room gallery, open houses you can add to a calendar and a tour request; a selling page with a three-step home value estimate and a seller net sheet; a buying page with a six-step guide, an affordability calculator and an FAQ; an interactive neighborhood map with six guides; an about page with a sales chart and a timeline; a consultation booking 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/atlas.

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 parts drawn from data (the listing cards, the listing page, the house and room illustrations) and the interactive parts (the filters, the gallery, the calculators, the map highlight, the booking flow and the forms) work as in the HTML version: they are empty blocks or small Custom HTML blocks filled and driven by assets/js/main.js. Colors 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 atlas.zip from freeframe.dev/templates/atlas (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 Atlas.
  3. Go to Appearance > Atlas setup and click Create the demo pages. This creates the Home, Listings, Listing, Sell, Buy, Neighborhoods, About and Book a consult 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 > Atlas pages) made of section patterns. Every section is also offered on its own in the block inserter under Patterns > Atlas.

Page Slug Page pattern Sections (pattern files in patterns/)
Home / page-home.php home-hero (portrait, buyer and seller paths, rating), estimate-teaser (instant estimate), featured-listings, record (count-up numbers), reviews (carousel), neighborhoods-teaser (links and map), home-cta
Listings /listings/ page-listings.php listings-hero, listings-results (status tabs, filters, sort, results, empty state), off-market
Listing /listing/?id=… page-listing.php listing-detail (gallery, facts, description, highlights, open houses, tour request, similar homes), listing-not-found
Sell /sell/ page-sell.php sell-hero (numbers and what every listing includes), valuation (home value estimate), net-sheet, sell-process
Buy /buy/ page-buy.php buy-hero (tour sketch), buyer-steps, affordability (calculator), buyer-faq
Neighborhoods /neighborhoods/ page-neighborhoods.php neighborhoods-hero, neighborhood-map (map and info panel), neighborhood-guides (six guides and a call to action)
About /about/ page-about.php about-hero (portrait and story), sold-history (numbers and chart), timeline, promises, credentials
Book a consult /contact/ page-contact.php contact-hero, booking (consult types, day strip, times, details, summary, confirmation)
404 any missing URL templates/404.html 404

The Listing page shows one home from the data in assets/js/main.js, picked by ?id= (/listing/?id=utah-4512); without an id it shows the first listing. It has no menu link: the listing cards link to it.

Posts use templates/single.html; the blog, archives and search results use templates/index.html, templates/archive.html and templates/search.html. For a plain text page (a privacy policy, for example) choose the Page with title template.

Project structure#

freeframe-atlas/
  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 the site title, its strings and the pages'
                            URLs), editor styles, the `js` class script, theme color 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 > Atlas 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         listings, neighborhoods, illustrations, filters, calculators, booking...
  assets/icons/             SVG icons for the Icon block
  assets/img/               portrait, logo, favicon, social image
  assets/fonts/             Albert Sans and Gloock, 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-atlas.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 colors#

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

Color Default Used for
Paper, Sand 50 to 300 #f6f2ea, #f1ece2 to #c9bba2 the page background, soft panels and borders
Ink 400 to 950 #8a8d9a to #0e1430 text (800), headings (950), the top bar, dark sections and the footer
Cobalt 50 to 900 #eef1fd to #18245f the brand blue: buttons and links (600, 700), the estimate and calculator boxes (800)
Persimmon 50 to 700 #fff1eb to #a13517 the warm accent: highlighted words, section numbers, the selling path
Butter 100 to 400 #fbf1cf to #e8b93b highlights on cobalt and ink, the under-contract pill
Moss 100 to 700 #e3ead9 to #435a39 parks in the art and "sold" results
White #ffffff cards and your own blocks

The theme's stylesheet reads these colors from the palette (bg-cobalt-600, text-persimmon-700...), so changing Cobalt 600 in Styles recolors every primary button at once. To move to another brand color, change the whole scale together and keep the 600 shade dark enough for white button text (4.5:1); use Persimmon 700 or darker for small persimmon text on paper, and Ink 600 or darker for small grey text. The illustrations carry their own hex colors: each listing's art colors and the GLASS, LAWN, TREE and TREE_DARK constants in assets/js/main.js, the maps, sketch and chart in Custom HTML blocks, and the SVG files in assets/img/. Search for the old values (for example #2742cc and #e4552b) to change them too. The browser UI color (theme-color) is set in freeframe_atlas_head_meta() in inc/setup.php.

Component classes from the HTML template work on blocks too: add them in Block settings > Advanced > Additional CSS class(es). card gives a group the white card look, panel the sand panel, cobalt-block and ink-block the dark gradient boxes (add on-dark so text and focus outlines suit them), kicker is the small numbered label above a heading (its number is a kicker-num span), display the headline style, lede the intro paragraph, hl (on a span in a heading) the persimmon highlighted words and swash their hand-drawn underline, tag a small sand pill and link-arrow the underlined link with an arrow.

Typography#

Two fonts are bundled in assets/fonts/ and declared in theme.json (settings.typography.fontFamilies), so no request goes to Google: Gloock (one weight; preset display) for headlines, prices and big numbers, and Albert Sans (variable, 400 to 800, with italics; preset sans) for text. Gloock has no italic, so emphasis in headlines uses the hl color and the swash underline. 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. SVGs with text (the maps, the sketch, the chart and the footer wordmark) name the fonts in font-family attributes.

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 > Atlas.

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

Pattern Custom HTML What it is
home-hero the house in the "Just sold" card an SVG picture
estimate-teaser the neighborhood and size form see Home value estimate
featured-listings, listings-results, listing-detail the "turn on JavaScript" notes; in the listings: the status tabs, sort menu, filters, result count and the empty state's picture and buttons; on the listing page: the breadcrumb, the status line, the gallery, the facts and the tour form see Listings and filters and Listing page
reviews the Previous and Next buttons and the "1 / 6" status see Carousel, numbers and motion
neighborhoods-teaser, 404 the maps (and the 404's bobbing pin) SVG pictures
valuation, net-sheet, affordability the step indicator and the forms with their results see Features
buy-hero, sold-history the tour sketch and the sales chart SVG pictures (labelled for screen readers)
neighborhood-map the map and the info panel see Neighborhood map
booking the form, the confirmation and the summary see Consultation booking
header, footer the menu button; the newsletter form and the wordmark driven by the script; decorative

Some blocks carry classes (Block settings > Advanced > Additional CSS class(es)) that the script reads. Blocks can't store data- attributes, hidden or tabindex, so the script turns classes starting with ffd- into attributes when the page loads, ff-hidden into hidden and ff-tabindex-0 into tabindex="0": ffd-featured-listings on the home page's empty list becomes data-featured-listings, and ffd-area-card--germantown on a guide becomes data-area-card="germantown".

  • reveal fades a block in as it scrolls into view; reveal-delay-1 to reveal-delay-3 stagger it.
  • link-card on a group makes the whole group clickable: the first link inside it stretches over the group (the buying and selling paths on the home page, the logo).

The data that drives the interactive parts lives in constants at the top of the second part of assets/js/main.js, as in the HTML version:

Constant What it holds
AGENT name, phone, email, office address and time zone (America/Chicago), used in calendar files
AREAS the six neighborhoods: id, name, ppsf (price per square foot), median, dom (average days on market) and yoy (one-year price change)
LISTINGS every home, for sale, under contract or sold
VALUATION factors and rounding for the home value estimate
CONSULT_TYPES bookable consultations: label, length in minutes and where they happen
AVAILABILITY weekly hours, slot spacing, notice period, days shown, closed dates and the demo "booked" share
PRICE_STEPS, SORTS, AFFORD the listings' price filter steps and sort options; the affordability rules

Adding a listing

Copy an object in LISTINGS (in a child theme, see Child themes) and change it:

js
{ id: 'utah-4512', status: 'active', featured: true, address: '4512 Utah Ave', area: 'sylvan-park', zip: '37209',
  price: 925000, beds: 3, baths: 2, sqft: 1780, lot: '0.21 acres', year: 1928, type: 'Single-family', style: 'Craftsman bungalow', dom: 3, listed: '2026-10-12',
  headline: 'A Sylvan Park bungalow with the original built-ins',
  description: ['First paragraph.', 'Second paragraph.'],
  features: ['Original built-ins and fireplace', 'Deep back deck'],
  openHouses: [['sat', '13:00', '15:00']],
  art: { style: 'bungalow', sky: '#e3ead9', body: '#8fa97a', roof: '#435a39', trim: '#f6f2ea', door: '#a13517' } }
  • id must be unique; it is the ?id= of the listing page (/listing/?id=utah-4512).
  • status is active, pending or sold. Sold homes also need sold: { price, date } (date as YYYY-MM-DD).
  • area must be an id from AREAS.
  • featured: true puts a home for sale on the home page (the first three are shown).
  • listed (and sold.date) drive the "Newest" sort.
  • openHouses is a list of [weekday, start, end] in Nashville time, with weekdays sun to sat and 24-hour times. Each is shown on its next date, so they never go stale. Only homes for sale show open houses.
  • art.style picks the house drawing (see Images and artwork).

The cards, the listing page, the "recent sales" in the estimate, the map counts and the guide buttons all read this array. The listings data, the neighborhood names and the sentences built from them stay in English in the script; the labels around them are translatable.

The text that is page content is not in the data: the "Open this weekend" bar in the header, the "Just sold" card on the home page, the record numbers (238 homes, 8 days, 101.6%, $171M, also on the selling and about pages), the reviews, the guides' text and the chart. Keep them in step when you change the data.

Images and artwork#

  • Houses and rooms. houseSVG() in assets/js/main.js draws a 400 × 300 (4:3) house for each listing from its art object: victorian, farmhouse, bungalow, townhome, modern, ranch, foursquare, tudor or condo, colored by sky, body, roof, trim and door. The gallery on the listing page adds five rooms (ROOMS: living, kitchen, bedroom, bath, yard), listed with their captions in GALLERY. To use photos, add a photo URL to each listing and return an <img> instead of houseSVG(l, ...) in listingCard() and roomSVG(); keep a 4:3 crop.
  • Portrait. assets/img/delia-portrait.svg (480 × 600, 4:5) is an Image block on the home, about and listing pages: select it and Replace it with a 4:5 photo. The [&_img]:arch class rounds the top into an arch.
  • Maps, sketch and chart are inline SVG in Custom HTML blocks (neighborhoods-teaser, neighborhood-map, 404, buy-hero, sold-history); replace any picture with an Image block if you prefer.
  • The logo is assets/img/logo.svg in an Image block next to the name in the header and footer patterns. The social image is assets/img/og-image.png (1200 × 630, source og-image.svg); the favicon assets/img/favicon.svg is used until you set a Site Icon in Settings > General.

Icons are Icon blocks: select one and pick another from the Atlas collection or WordPress's own.

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 top bar with this weekend's open house is the first group of the header: change its link (/listing/?id=fatherland-1418) and text, or delete the group.

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": Home, Listings, Buy, Sell, Neighborhoods, About, Contact). Its first link (Home) and last link (Contact) are hidden in the desktop header, which has the logo and its own "Book a consult" button; keep them first and last, or remove the .site-nav … :first-child and :last-child rules in src/theme.css. 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; on the listing page the script marks Listings.

The footer's Explore and Neighborhoods menus are List blocks: click a link to change it, press Enter to add one. The phone number, email and address also appear in the header, on the listing and booking pages and in AGENT; search for them when you change them. The license line is placeholder text (license no. 000000, Larkspur & Main Realty): replace it with your real details and check your state's advertising and fair-housing rules.

Features#

Listings and filters#

initListings() fills the listings page ([data-listings]) with cards in [data-results].

  • Status tabs are buttons with data-status (active, pending, sold, all) and aria-pressed.
  • Filters: neighborhood (area), minimum bedrooms (beds, 1 to 4), minimum and maximum price (min, max, from PRICE_STEPS) and sort (newest, price-asc, price-desc, size). Sold homes are filtered and sorted by their sold price.
  • Live counts: each tab and each neighborhood option shows how many homes you would see if you picked it.
  • Shareable links: the state is written to the address bar, for example /listings/?status=sold&area=east-nashville&beds=3; defaults are left out and unknown values are ignored. Link to a filtered list from any button (the guides link to /listings/?area=…).
  • Empty state: shown when nothing matches, with a clear-filters button.

The result count is an aria-live region, so screen readers hear "3 homes for sale" after each change.

Listing page#

initDetail() reads ?id=, fills every [data-l="…"] block (address, area, price, headline, description, facts, highlights...) and sets the tab title to "address, neighborhood — site title" (the site title from Settings > General). An unknown id shows the listing-not-found section, whose heading becomes the page's only h1. The gallery has previous and next buttons, thumbnails with aria-current, and answers the left and right arrow keys when focused. Sold homes show the sold price and the percentage of asking, and a "Want one like this?" panel instead of the tour form; homes under contract say so in the open-house box. "You might also like" shows homes from the same neighborhood first.

Open houses and calendar files#

nextOpenHouse() turns each [weekday, start, end] into its next date in AGENT.timeZone, skipping one that has already ended today. Times are shown in Central Time, with the visitor's own time added when they are elsewhere. "Add to calendar" downloads an iCalendar (.ics) file built by buildIcs() (times in UTC, so every calendar app places it correctly).

Home value estimate#

The teaser on the home page ([data-estimate-teaser]) and the three-step estimate on the selling page ([data-valuation]) share estimate():

value = sqft x area ppsf x type factor x condition factor x (1 + bed and bath adjustment) x (1 + extras)
range = value plus or minus the condition's spread, rounded to $5,000

The factors are in VALUATION. The teaser links to /sell/?area=…&sqft=…#valuation, which prefills the estimate. Each step validates before moving on and moves focus to the next step's heading. The result shows where the estimate sits against the neighborhood median and lists the agent's sales there; "Use in the net sheet" copies the midpoint into the net sheet. The page says clearly that it is illustrative, not an appraisal.

Seller net sheet#

[data-net-sheet] recalculates on every input: sale price minus commission and closing costs (in percent), mortgage payoff and repairs or credits. The bar's [data-bar] segments are sized as a share of the price, and a warning appears when costs exceed the price. Fields with data-money get thousands separators when you leave them.

Affordability calculator#

[data-afford] on the buying page applies the 28/36 rule in AFFORD: housing costs up to 28% of gross monthly income, and housing plus debts up to 36%. It finds the highest price whose monthly cost (principal and interest, property tax, insurance, mortgage insurance below 20% down, HOA dues) fits both, draws a donut of the payment and links to /listings/?max= with the highest price step at or below the result. Change FRONT_END, BACK_END, PMI_RATE or PMI_BELOW for other assumptions.

Neighborhood map#

Hovering or focusing an area on the map ([data-area], a link to its guide) highlights its guide card ([data-area-card], the ffd-area-card--<id> class on the guide's group) and fills the info panel; hovering or focusing a card highlights its area. Selecting an area, or arriving with a hash such as #east-nashville, keeps it highlighted. Blocks with ffd-area-stat--<id>:median (also ppsf, dom, yoy) show statistics from AREAS, and spans with data-area-count="<id>" in the guide buttons show the number of homes for sale. To add a neighborhood, add it to AREAS, a polygon to the maps and a guide (duplicate one in List View, then change its HTML anchor and classes).

Consultation booking#

initBooking() runs the booking page:

  1. Consult type: radio cards matching CONSULT_TYPES (buyer, seller, value, relocation). /contact/?type=seller preselects one and ?listing=<id> prefills the message.
  2. Day: the next AVAILABILITY.daysShown days with a free time, skipping closed dates and days without hours.
  3. Time: slots every slotStep minutes inside the day's hours, long enough for the consult and at least minNoticeHours from now, in Nashville time.
  4. Details: name, email, optional phone, timeline and message.

The summary updates live; on submit, missing choices are reported inline and focus moves to the first problem. The confirmation offers an .ics download. demoBusyShare marks a fixed share of times as booked so the demo looks realistic; set it to 0 once connected.

  • Reviews (ffd-carousel) are a scroll-snap track with Previous and Next buttons and a "1 / 6" status; swipe, trackpad and keyboard scrolling work, and it never moves on its own.
  • Count-up numbers animate spans with data-count (and data-decimals) in the record section.
  • Scroll reveals fade in reveal blocks; revealing one reveals everything above it.
  • FAQ: the buyer questions are an Accordion block (WordPress's own script).
  • Back to top is created by the last block in assets/js/main.js.

Animations, smooth scrolling and transitions are off for visitors who prefer reduced motion.

Block styles#

Select a Button block and choose a style in the block settings: the default (cobalt), Persimmon, Ink, Butter, Outline and Outline (on dark). Add is-large or is-small in Additional CSS class(es) for the large and small sizes.

Forms#

Every form is a demo: it validates in the browser and shows a success state, but nothing is sent or booked until you connect it. The confirmations say so.

Form Where Validates
Newsletter ([data-newsletter-form]) footer email
Tour request ([data-tour-form]) listing page date (today or later), name, email, optional phone
Home value estimate selling page address, neighborhood, square feet (400 to 9,000)
Booking booking page consult type, day, time, name, email, optional phone

Fields with data-validate (required, email, phone-optional, address, sqft, future-date, choice) are checked on submit and again as the visitor corrects them; data-message overrides the empty-field message. Errors appear under the field, are linked with aria-describedby and focus moves to the first problem.

Connecting a form#

Use a form plugin (replace the form's Custom HTML block with the plugin's block; add the classes label, input and btn btn-primary to its labels, fields and button), or send the form from the script (in a child theme). In the newsletter handler in assets/js/main.js (search for Connect a real endpoint here), send it before showing the success panel, for example with Formspree:

js
form.addEventListener('submit', function (e) {
  e.preventDefault();
  if (!validateForm(form)) return;
  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('Send failed');
      form.hidden = true;
      success.classList.remove('hidden');
      success.focus();
    })
    .catch(function () { alert('Sorry, that did not send. Please email hello@deliaquintero.example.'); });
});

Do the same in the tour form handler in initDetail(); it also sends the listing address in a hidden listing field.

Connecting a booking tool#

Either replace the booking section with your Cal.com or Calendly embed (a Custom HTML block with its embed code), or keep this flow and post the request to your form service in the submit handler in initBooking() (the block marked "Connect a real booking tool"), sending state.type, state.slot.start.toISOString() and the details. Set AVAILABILITY.demoBusyShare to 0 either way.

SEO and sharing#

Page titles come from WordPress: the page title, an em dash and the site name (for example "Listings — Delia Quintero Real Estate"); the home page shows the site name and tagline (**Settings

General**), and the listing page sets its own title from the listing. 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. Because listing pages are built from ?id=, search engines see one Listing page; if listings matter for search, give each home a page of its own. RealEstateAgent structured data (schema.org) with your real details can be added by most SEO plugins; the theme doesn't include it because it would describe a fictional agent.

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 (the not-found state takes it over), labelled sections, visible focus outlines (cobalt on light, butter on dark), the menu button with aria-expanded and a label that changes with its state (Escape closes the menu), status tabs and day buttons with aria-pressed, radios for the other choices, aria-live result counts, estimate, calculator results and booking summary, a keyboard-operable gallery and map, booked times announced as booked, form errors linked with aria-describedby and aria-invalid, alt text, role="img" labels or aria-hidden on every picture, and reduced-motion support. The demo passes axe (WCAG 2.1 A and AA).

When you edit: keep one Heading 1 per page and don't skip heading levels, give images alt text (or mark them decorative), keep link text meaningful, check contrast when you change colors, label any field you add to a form and add data-validate when it needs checking.

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-atlas, 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-atlas. All text in patterns, templates and PHP goes through WordPress's translation functions; the script's interface strings (labels, validation messages, open-house and booking lines) are translated in PHP (freeframe_atlas_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-atlas wp-content/themes/freeframe-atlas/languages/freeframe-atlas.pot

Save freeframe-atlas-es_ES.mo (for example) in wp-content/languages/themes/ or in the theme's languages/ folder. Page content created by Atlas 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 in US English by the script (toLocaleDateString and toLocaleString with en-US).

Child themes#

Use a child theme for changes you want to keep when Atlas is updated (your listings in particular). Create wp-content/themes/atlas-child/style.css:

css
/*
Theme Name: Atlas Child
Template: freeframe-atlas
*/

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-atlas) and enqueue yours in the child's functions.php, keeping the inline window.freeframeAtlas data (or copy it). 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 > Atlas.
  • A listing page says "That listing isn't on my desk". The ?id= doesn't match an id in LISTINGS. Check the spelling in the link (the header's open-house link included).
  • Listing links go to the wrong page. The script finds the Listing, Listings and Sell pages by their slugs (listing, listings, sell; see freeframe_atlas_page_urls() in inc/setup.php). Keep those slugs, or change the list there.
  • Open houses show the wrong day or time. Weekdays are sun to sat, times are 24-hour Nashville time, and the zone is AGENT.timeZone.
  • No booking days appear. Check AVAILABILITY.hours (day 0 is Sunday) and closed, and that minNoticeHours isn't larger than your hours.
  • Home or Contact is missing from the desktop menu. The desktop header hides the menu's first and last links. See Navigation, header and footer.
  • Atlas 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 > Atlas pages.

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 portrait, house and room illustrations, maps, chart and logo are included under the same license. Albert Sans and Gloock are licensed under the SIL Open Font License 1.1 (OFL.txt in each folder of assets/fonts/). The agent, brokerage, homes, prices and clients are fictional.

Search documentation

Search the guides and every template's documentation