Freeframe

Documentation

Studio

v1.0.1Updated Oct 17, 2026A minimal, Swiss-style website for a small design studio, with a filterable work index, case studies and a brief estimator.

Overview#

Studio is a website for a small design studio, built as a WordPress block theme. The fictional brand is Raster, a five-person studio in Zürich working across identity, digital product, editorial and wayfinding. The theme sits on a strict 12-column grid with one typeface family, a monochrome palette and a single red accent: a home page with a rising headline, selected work, the four disciplines, a client wordmark grid, count-up numbers and journal teasers; a work index with discipline filters, a year sort and grid/list views with a hover preview; case studies drawn from the project data (/project/?id=fondo) with generated artwork in each project's palette; a services page with deliverables and prices, three ways of working, a brief estimator and an FAQ; a studio page with a floor plan, values, a process timeline, the team and open roles; a journal with topic filters and expandable notes; a five-step project enquiry; 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/studio.

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 work index toolbar, the estimator, the enquiry, the journal's filter bar and note buttons, the newsletter form) 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 studio.zip from freeframe.dev/templates/studio (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 Studio.
  3. Go to Appearance > Studio setup and click Create the demo pages. This creates the Home, Work, Case study, Services, Studio, Journal 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 > Studio pages) made of section patterns. Every section is also offered on its own in the block inserter under Patterns > Studio.

Page Slug Page pattern Sections (pattern files in patterns/)
Home / page-home.php home-hero (rising headline, availability), selected-work, what-we-do, clients, numbers (count-up), journal-teaser, cta
Work /work/ page-work.php work-hero, work-index (filters, sort, grid/list views, nine projects), not-shown
Case study /project/ page-project.php case-study (Seeline; other projects from ?id=), case-not-found
Services /services/ page-services.php services-hero, disciplines, engagement, estimator, faq
Studio /studio/ page-studio.php studio-hero, space (floor plan), values, process (timeline bars), team, jobs (open roles), visit
Journal /journal/ page-journal.php journal-hero, notes (topic filter and eight notes), newsletter
Contact /contact/ page-contact.php enquiry (five-step enquiry, contact details and studio hours)
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-studio/
  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 (with its strings, page links and cover URLs),
                            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 > Studio 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         case study data and artwork, work index, estimator, enquiry, journal...
  assets/icons/             SVG icons for the Icon block
  assets/img/               project covers (work/), logo, favicon, social image
  assets/fonts/             Host Grotesk and Fragment Mono, 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-studio.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 #f6f6f3 the page background
Sheet #ffffff raised surfaces: the enquiry panel, the estimate, choice tiles
Tint #ecece7 quiet fills, image backgrounds, the estimator band
Rule #d6d6cf hairlines
Faint #8b8b85 decorative lines and field underlines (not for text)
Mute #5b5b56 secondary text and labels (AA on Paper and Sheet)
Ink #121211 text, buttons, dark sections
Ink 2 #2a2a28 the ink button's hover
Signal #e8381e the one accent, for small fills: dots, squares, bars
Signal ink #c02a12 red text on Paper (enough contrast)
White, Black for your own blocks

The theme's stylesheet reads these colours from the palette (bg-ink, text-mute, bg-signal...), so changing Signal in Styles recolours every dot, square and bar at once. Keep Signal ink dark enough for text on Paper (4.5:1). The SVG artwork (the project covers, the floor plan, the 404 grid and the case study gallery) carries its own colours in its markup.

Typography#

Two fonts are bundled in assets/fonts/ and declared in theme.json (settings.typography.fontFamilies), so no request goes to Google: Host Grotesk (variable, 300 to 700, and italic 400; preset sans) for everything, and Fragment Mono (400; preset mono) for the small labels, indexes and counts. 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--sans, --mono), so keep those slugs. The generated case study artwork and the floor plan name the fonts directly (SANS and MONO in main.js, font-family="Fragment Mono, …" in the plan).

Font sizes are the presets in theme.json (xs 0.75rem to 9xl 8rem, the Tailwind scale). The classes display (large headline), h-section (section heading), lead, label (small mono caps), idx (mono index numbers) and ff-dot (a red square before the text) 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 > Studio.

What the script drives is in Custom HTML blocks: edit those in the block's HTML view, and keep their ids and data- attributes.

Pattern Custom HTML What it is
work-index the filter, sort and view toolbar, the count, the preview, the empty state see Work index
case-study the breadcrumb, the three gallery drawings see Case studies
estimator the form and the estimate see Brief estimator
notes the topic chips and count, each "Read note" button see Journal filters and notes
newsletter the form and its thank-you message see Forms
enquiry the steps, the form and its success message see Project enquiry
space, 404 the floor plan, the grid drawing SVG drawings (labels in the theme's fonts)
header, footer the menu button, the copy and grid buttons, 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 aria-hidden, so the script turns them back when the page loads: a class starting with ffd- becomes a data attribute (ffd-project ffd-year--2025 ffd-disciplines--identity_wayfinding on a work item becomes data-project data-year="2025" data-disciplines="identity wayfinding"; _ stands for a space), ff-hidden becomes hidden and ff-aria-hidden becomes aria-hidden="true".

  • reveal (with reveal-d1 to reveal-d3 for a delay) fades a block in as it scrolls into view; rise on a heading lifts each line (<span><span>…</span></span> in the heading's HTML view).
  • link-card on a group makes the whole group clickable: the first link inside it stretches over the group (project cards, discipline rows, journal teasers, previous/next projects).

Projects

A project appears in up to four places:

  1. PROJECTS in assets/js/main.js: the case study itself (see Case studies). The array order is the order of the previous/next links.
  2. assets/img/work/<id>.svg: its cover (800 × 600). inc/setup.php passes every SVG in that folder to the script.
  3. The work-index pattern (Work page): one list item.
  4. Optionally the selected-work pattern (Home): one of the four cards.

To add one to the work index, select a project item in List View (a Group with the classes work-item ffd-project ffd-order--9 ffd-year--2021 ffd-disciplines--identity) and duplicate it. Change the cover, the name and its link (/project/?id=<id>), the title, the disciplines and the year, and set the classes: ffd-year--<year> (the sort), ffd-order--<n> (the order within a year) and ffd-disciplines--<keys> (keys of DISCIPLINES, joined with _). Then update the counts in the chips (the chip-count numbers in the toolbar's Custom HTML), the (9) after the Work heading and the "Showing all 9 projects" text.

Journal notes

Each note in the notes pattern is a Group with the anchor note-… and the classes ffd-note ffd-tags--type_process (tags are keys of the topic chips, joined with _). Its hidden text is the Group with the anchor note-…-body and the class ff-hidden; the "Read note" Custom HTML button's aria-controls must point at that anchor. Update the chip counts when you add a note.

Count-up numbers

The home page numbers are <span data-count="140">140</span> in the paragraph's HTML view. Keep the final value as the text: that is what visitors see without JavaScript or with reduced motion.

Rolling dates. The availability dates in the patterns are <span data-months-ahead="n">, which assets/js/main.js fills with the month n months from now and its year: "From Feb 2027" (home hero) and "Booking projects from February 2027" (call to action) at 4, the job start dates "From January 2027" (3) and "From March 2027" (5), and the internship deadline "15 December 2026" (2, with data-day="15"). The dates in the patterns are shown without JavaScript. For fixed dates, replace the spans with plain text.

Images and artwork#

There are no photos; all artwork is SVG.

Artwork Size / ratio Notes
assets/img/work/*.svg 800 × 600 (4:3) Project covers in Image blocks (home, work index, case study) and in the list preview. Replace with 4:3 images
Case study gallery 1600 × 900 and 800 × 1000 Seeline's three drawings are Custom HTML in case-study; the others are generated by ART in main.js from each project's palette and mark
Floor plan (space), 404 grid (404) inline SVG Custom HTML; replace with an Image block if you prefer
assets/img/logo.svg 18 × 18 the logo in the header (an Image block)
assets/img/og-image.png 1200 × 630 social image (source og-image.svg)
assets/img/favicon.svg 32 × 32 used until you set a Site Icon in Settings > General

Images in cover frames use object-cover, so other ratios still fit. Covers in cards have an empty alt text because the link names the project. The arrow on links and buttons is drawn with CSS (icon-after-arrow-right); other icons are Icon blocks (pick another from the Studio 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 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": Work, Services, Studio, Journal, Contact). Each link's label ends with its number, <span class="idx">01</span> (edit the label's HTML), shown in the mobile menu only. 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.

The footer's Index and Elsewhere menus are List blocks: click a link to change it, press Enter to add one. The email address and phone number appear in the header's mobile menu, the footer, the contact page and the copy button's data-copy; search for them when you change them.

Features#

Work index#

The Work page's work-index section is the ffd-work group. Each project is a list item with ffd-project, ffd-order--n, ffd-year--yyyy and ffd-disciplines--… in the list with work-list ffd-view--grid ffd-work-list.

  • Filter. Chips with data-filter="all|identity|digital|editorial|wayfinding" set aria-pressed. The choice is kept in the URL as ?discipline=identity, so filtered views can be shared; unknown values fall back to all.
  • Sort. The <select data-sort> sorts by year, newest (default) or oldest, kept as ?sort=oldest. Items of the same year keep their order.
  • Count. [data-work-count] (aria-live="polite") reads "Showing all 9 projects" or "Showing 3 of 9 projects". An empty state with a Show all projects button appears if a discipline has nothing.
  • View. The Grid and List buttons switch data-view on the list; the choice is saved on the device (studio-work-view). Both layouts are CSS on the same blocks (.work-list[data-view=…] in src/theme.css).
  • Preview. In list view at 768 px and wider, the project's cover follows the pointer; when a row gets keyboard focus it sits beside the row. It hides on blur, on scroll and with Escape, and is aria-hidden. On phones the list shows a small thumbnail instead.

Case studies#

The Case study page reads ?id=. Without it, and for ?id=seeline, it shows the page's own blocks (the case-study pattern: Seeline, editable like any page). For any other id, the script draws the matching PROJECTS entry with renderProject() in its place and updates the tab title (the project and the site title). An unknown id removes the article and shows the case-not-found section, its heading promoted to the page's h1. Previous and next links wrap around PROJECTS.

A project in PROJECTS looks like this (shortened):

js
{
  id: 'seeline',                       // /project/?id=seeline and assets/img/work/seeline.svg
  client: 'Seeline',
  title: 'A calmer identity for the lake ferries',
  disciplines: ['identity', 'wayfinding'], // keys of DISCIPLINES
  year: 2025, sector: 'Public transport', location: 'Zürich', duration: '14 months',
  services: ['Identity system', 'Wayfinding', 'Timetable design', 'Livery'],
  summary: '…', challenge: '…',
  approach: [{ title: 'Audit every touchpoint', text: '…' }, …],   // three steps
  results: [{ value: '−38%', label: '…' }, …],                     // three results
  quote: { text: '…', name: 'Andrea Lutz', role: 'Head of customer experience, Seeline' },
  credits: ['Noemi Imhof', 'Selin Karaca', 'Céline Odermatt'],
  palette: { bg: '#1d3b57', ink: '#f1efe7', accent: '#ff7a3d', soft: '#8fb3c9' },
  mark: '<circle cx="50" cy="50" r="38" …/>',  // SVG in a 100×100 box, filled with currentColor
  poster: { lines: ['Summer', 'timetable'], sub: '1 May – 30 Sept 2025' },
  sign: ['3', 'Departures', 'Rapperswil · Thalwil · Küsnacht'],
  gallery: [{ kind: 'board', caption: '…' }, { kind: 'signage', caption: '…' }, { kind: 'poster', caption: '…' }]
}

Gallery kind values: board, screens and spread are wide (the first image); poster, signage and mark are tall. screens reads ui: { headline, figure }, spread reads spread: { title, sub }, poster reads poster and signage reads sign.

The projects' text stays in English in main.js (the labels around it, such as "Overview" and "Next project", are translated). If you change the first project in PROJECTS, change the case-study pattern's blocks too (they are what the page shows for it), or remove the class ffd-rendered--seeline from its article group so the script always draws it from data.

Brief estimator#

The estimator on the Services page (/services/#estimator) is the estimator pattern's Custom HTML. Its price model is at the top of the second part of main.js:

js
var SCOPE = {
  strategy: { label: __('Brand strategy'), cost: [12000, 18000], weeks: [3, 4] },
  identity: { label: __('Identity system'), cost: [28000, 45000], weeks: [6, 9] },
  …
};
var SCALE = { focused: { label: __('Focused'), mult: 0.75, pace: 0.8 }, standard: …, extensive: … };
var URGENCY = { relaxed: { label: __('Relaxed'), mult: 0.95, pace: 1.2 }, standard: …, priority: … };
  • Budget = the sum of the selected services' cost × SCALE.mult × URGENCY.mult, low rounded down and high rounded up to CHF 1’000, shown with a Swiss thousands separator (CHF 63’000–105’000).
  • Duration = the longest selected service plus half of the others (phases overlap), × SCALE.pace × URGENCY.pace, rounded to whole weeks.
  • Send this brief links to the Contact page with ?services=identity,website&budget=60-120&timeline=standard; the budget band comes from the midpoint of the range and BUDGETS.

The checkboxes and radios in the Custom HTML must use the same keys as SCOPE, SCALE and URGENCY, and the price hints in their labels are text, so update them when you change the numbers. The default selection is DEFAULT_BRIEF. Choices are saved on the device (see Saved data); Reset restores the defaults. Results are announced through a visually hidden aria-live region.

Project enquiry#

The form on the Contact page ([data-enquiry]) has five steps: Project, Budget and timeline, Details, About you and Review. "Next" validates the current step before moving on; the step tabs let visitors jump back to any step they have reached. Moving between steps focuses the new step's heading and announces "Step 2 of 5: Budget and timeline". The Review step lists every answer with an "Edit" button. The details field shows a live character count. The form is prefilled from the estimator's link: ?services= (comma-separated SCOPE keys), ?budget= (under-30, 30-60, 60-120, 120-plus) and ?timeline= (relaxed, standard, priority); when anything is prefilled, a note says so.

Journal filters and notes#

Topic chips (data-tag) filter the notes, kept in the URL as ?tag=process; the count announces the result. Each note's "Read note" button toggles aria-expanded and the hidden text. Opening /journal/#note-figures expands that note (clearing a filter that would hide it); the home page's teasers link there.

FAQ and open roles#

The FAQ on the Services page and the open roles on the Studio page are Details blocks with the class acc (a native <details>: keyboard and screen reader support built in). Add a question by duplicating one in List View. The plus/minus is drawn by CSS (.acc > summary::after in src/theme.css).

Clock, studio status and small extras#

  • Zürich clock. Every <time data-clock> (in the header and the mobile menu) shows the current time in Zürich, updated every 15 seconds.
  • Studio status. <span data-studio-status> on the Contact page says whether the studio is open now, using STUDIO_HOURS in main.js (days with 0 = Sunday, open and close in 24-hour decimals, tz). Keep it in step with the hours written on the page.
  • Copy email. The footer's <button data-copy="hello@raster.example"> copies the address and briefly shows "Copied" (in an aria-live label). Browsers only allow copying on https:// or localhost; elsewhere the label asks the visitor to copy it by hand.
  • Grid overlay. The footer's "Show grid" button (aria-pressed) overlays the 12-column grid in translucent red. Delete its Custom HTML block to drop it.

Reveals and motion#

Blocks with reveal fade up as they scroll into view; rise headings lift each line into place; the fill-bar bars of the process timeline grow from the left; numbers with data-count count up. Everything above a revealed block is revealed too, so fast jumps never leave hidden content. With reduced motion all of this is off, and without JavaScript nothing is hidden.

Header, mobile menu and back to top#

The header is sticky. Below 1024 px the Menu button (#menu-button, aria-expanded) opens a full-height menu; Escape or a link closes it and focus returns to the button. A back-to-top button appears after about one screen of scrolling and steps aside while the footer's "Back to top" link is visible. 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 ink button), Line, Line (on dark), Paper (on dark)

The class is-small on a Button block makes it the header's smaller size, and icon-after-arrow-right adds the arrow.

Forms#

There are two forms. Both validate in the browser and neither sends anything until you connect it; their success messages say so.

Form Where Validates
Project enquiry ([data-enquiry]) Contact at least one service; organisation; a budget and a timeline; details of 40+ characters; an optional link starting with http:// or https://; name; a valid email; the consent box
Newsletter ([data-newsletter]) Journal a valid email

Validation is in checkField(), checkGroup() and validate() in assets/js/main.js. Errors appear under each field (.field-error), linked with aria-describedby, and the first invalid field gets focus.

To collect enquiries, use a form plugin (replace the Custom HTML with the plugin's block; give its fields the classes field-label, input and its button btn btn-ink), or post the form in the script (in a child theme). In the enquiry's submit handler, replace the lines after "Demo only: nothing is sent" with a request, for example to Formspree:

js
fetch('https://formspree.io/f/your-form-id', {
  method: 'POST',
  body: new FormData(enquiry),
  headers: { Accept: 'application/json' }
}).then(function (res) {
  if (!res.ok) throw new Error('Request failed');
  enquiry.parentElement.hidden = true;
  success.querySelector('[data-success-name]').textContent = enquiry.elements.name.value.trim().split(/\s+/)[0];
  success.hidden = false;
  success.focus();
}).catch(function () {
  alert('Sorry, something went wrong. Please email hello@raster.example.');
});

Then change the success text in the enquiry pattern and remove the "This is a demo" note. The newsletter works the same way: replace the lines after "Demo only: connect your newsletter provider here" with a request to your provider, and update the thank-you text in the newsletter pattern.

Saved data (localStorage)#

Key Shape Validation on read Reset
studio-work-view "grid" or "list" anything other than "list" means grid switch the view, or remove the key
studio-brief-v1 {"scope":["identity","website"],"scale":"standard","urgency":"standard"} unknown or duplicate scope keys are dropped; an unknown scale or urgency falls back to the default; invalid JSON means defaults the estimator's Reset button removes it

These are the HTML version's keys. Storage errors (private browsing) are ignored. To reset everything, remove both keys in your browser's developer tools (Application > Local Storage).

SEO and sharing#

Page titles come from WordPress: the page title, an em dash and the site name (for example "Services — Raster"); the home page shows the site name and tagline (Settings > General). A case study's tab title is set by the script ("Fondo: Saving, made legible — Raster"). 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. Search engines index Seeline's case study from the page's own blocks; the other case studies are drawn by the script, so if they matter for search, give each its own page (duplicate the Case study page and edit its blocks) and link to 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 in signal red, the menu button with aria-expanded, filter chips and the view switch with aria-pressed and live result counts, a hover preview that also follows keyboard focus and is hidden from screen readers, native <details> for the FAQ and roles, form steps that move focus to their heading and announce progress, form errors announced through aria-describedby and aria-invalid, labelled drawings and reduced-motion support for every animation. Red text on Paper uses Signal ink for 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 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/:

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-studio, 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-studio. All text in patterns, templates and PHP goes through WordPress's translation functions; the script's strings (counts, estimator labels, step names, case study labels, form errors and messages) are translated in PHP (freeframe_studio_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-studio wp-content/themes/freeframe-studio/languages/freeframe-studio.pot

Save freeframe-studio-de_DE.mo (for example) in wp-content/languages/themes/ or in the theme's languages/ folder. Page content created by Studio setup is copied into your pages in the site's language at that moment; translate it in the editor like any other content. The case studies in PROJECTS and prices in Swiss francs stay as written in main.js.

Child themes#

Use a child theme for changes you want to keep when Studio is updated (your projects, prices and hours in particular). Create wp-content/themes/studio-child/style.css:

css
/*
Theme Name: Studio Child
Template: freeframe-studio
*/

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-studio) and enqueue yours in the child's functions.php, keeping the inline window.freeframeStudio data (or copy it). The covers list (images) comes from the parent's assets/img/work/; add your own entries if your covers live in the child theme. The parent's stylesheet only contains the classes the parent uses: for 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 > Studio.
  • A new project doesn't show under a filter or sorts in the wrong place. Its item needs ffd-project, ffd-year--<year>, ffd-order--<n> and ffd-disciplines--<keys>. See Projects.
  • A project link shows "We couldn't find that project". The ?id= doesn't match an id in PROJECTS. Add the project there (see Case studies).
  • Editing the Case study page changes nothing for ?id=fondo. Only Seeline is the page's own blocks; the others come from PROJECTS in main.js.
  • The estimate doesn't change after editing prices. The tiles show text from the pattern, but the numbers come from SCOPE, SCALE and URGENCY in assets/js/main.js. Change both.
  • Studio 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 > Studio pages.
  • The forms don't send anything. They are demo forms; see 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 project covers, case study artwork, floor plan and logo are included under the same license. Host Grotesk and Fragment Mono are licensed under the SIL Open Font License 1.1 (OFL.txt in each folder of assets/fonts/).

Search documentation

Search the guides and every template's documentation