Freeframe

Documentation

Pocket

v1.0.1Updated Oct 10, 2026A bright launch site for a mobile app, with phone mockups and a scroll-driven feature tour.

Overview#

Pocket is a launch website for a mobile app, built as a WordPress block theme. The fictional app is Pocket, a calm budgeting app from Bristol. The theme has a home page with layered phone mockups, a sticky feature tour that swaps the phone's screen as you scroll, count-up figures and app store reviews; a features page; a screenshot carousel with a lightbox; pricing with a monthly/yearly switch, a comparison table and an FAQ; release notes with type and platform filters; a searchable help centre with a contact form; a press kit with copy buttons; a privacy policy with a table of contents that follows the scroll; and a 404 page. Light and dark themes are included. It is version 1.0.1 (see template.json and style.css), works with WordPress 7.1 and PHP 7.4 or newer, and is the WordPress version of the HTML template of the same name. See it live at freeframe.dev/templates/pocket.

Everything you see is made of core WordPress blocks arranged in block patterns, so you edit text, links, buttons, lists and images in the block editor without touching code. Colours, the fonts and font sizes are design tokens in theme.json: change them in Appearance > Editor > Styles and the whole site follows. The phone screens are pictures built in HTML and SVG (no image files), kept in Custom HTML blocks.

Quick start#

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

  1. Download pocket.zip from freeframe.dev/templates/pocket (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 Pocket.
  3. Go to Appearance > Pocket setup and click Create the demo pages. This creates the Home, Features, Screenshots, Pricing, What's new, Help centre, Press kit and Privacy policy pages from the theme's page patterns, sets Home as the front page (Settings > Reading) and creates the header menus. Pages that already exist with the same slug are left alone, so it is safe to click it again.
  4. In Settings > General, set the Site Title to your app's name and the Tagline to a short description: the header and footer show the title, and the home page's browser title is "Title — Tagline".
  5. Point the store buttons at your App Store and Google Play pages (see Images and artwork).

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

Page Slug Page pattern Sections (pattern files in patterns/)
Home / page-home.php home-hero (phones and store buttons), featured-in, feature-tour, stats (count-up), reviews, private-by-design, download (store buttons and QR code)
Features /features/ page-features.php features-header (section links), feature-list (six features with phones), little-things
Screenshots /screenshots/ page-screenshots.php screenshots-header, screenshot-carousel, screenshot-screens (the lightbox's screens), screenshot-lightbox, screenshots-cta
Pricing /pricing/ page-pricing.php pricing-header (monthly/yearly switch), pricing-plans, pricing-compare, pricing-faq
What's new /changelog/ page-changelog.php changelog-header (filters), releases (timeline)
Help centre /support/ page-support.php support-header (search), help-articles (topics, articles and the contact form)
Press kit /press/ page-press.php press-header, press-about (boilerplate, leadership, key facts), press-assets (logos and colours), press-toast
Privacy policy /privacy/ page-privacy.php privacy-header, privacy-policy (table of contents and policy)
404 any missing URL templates/404.html 404

WordPress 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 (terms, imprint), choose the Page with title template in the page's settings.

Project structure#

freeframe-pocket/
  style.css                 theme header (name, version, requirements, license); no styles
  theme.json                design tokens: palette, fonts, font sizes, layout widths, button
  functions.php             loads the files in inc/
  inc/setup.php             stylesheet and script (with its strings), editor styles, the no-flash
                            dark mode script, theme colours, 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 > Pocket setup: creates the demo pages and the menus
  templates/                page.html (pages), page-with-title.html, single.html, index.html,
                            archive.html, search.html, 404.html
  parts/                    header.html and footer.html (they show the header and footer patterns)
  patterns/                 every section and page (PHP files with block markup)
  assets/css/theme.css      the compiled stylesheet (do not edit: built from src/)
  assets/css/editor.css     the same for the block editor (generated by src/editor-css.mjs)
  assets/js/main.js         the site's JavaScript; assets/js/editor.js tweaks the editor
  assets/icons/             SVG icons for the Icon block
  assets/img/               logo mark, logos for the press kit, favicon, social image
  assets/fonts/             Plus Jakarta Sans and Sora and their licenses (OFL.txt)
  src/theme.css             the Tailwind CSS source of assets/css/theme.css
  src/icons.css             icons drawn before or after text with CSS (generated)
  src/editor-css.mjs        writes assets/css/editor.css after each build
  languages/                freeframe-pocket.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 Used for
Pine 50 … Pine 950 the brand green: primary buttons, links, eyebrows, icons, the stats and help centre bands
Sun 300 … Sun 500 the yellow accent: the download banner, ratings, primary buttons in dark mode
Coral 400, Coral 500 warm accent, form errors in dark mode
Cream 50 … Cream 200 light backgrounds and panels
Ink 800 … Ink 950 text, the footer, dark mode backgrounds
Base, Contrast the page background and text (cream and ink; ink and white in dark mode)

The theme's stylesheet reads every colour from the palette (bg-pine-600, text-sun-300... are the presets), so changing Pine 700 in Styles recolours every link and eyebrow at once. Keep text colours at a 4.5:1 contrast in both themes.

The phone screens use inline styles with hex colours so each screen is self-contained: search the Custom HTML blocks for #0f7457 (the brand green) and other hex values to recolour them. The logos in assets/img/ are SVG files you can edit in any vector or text editor.

Typography#

The fonts are Sora (headings, font-display, weights 600 to 800) and Plus Jakarta Sans (body text, weights 400 to 800), bundled in assets/fonts/ and declared in theme.json (settings.typography.fontFamilies), so no request goes to Google. To use other fonts, install them in Appearance > Editor > Styles > Typography > Fonts (the Font Library) and select them, or replace the fontFace entries in theme.json. The utility classes use the families --wp--preset--font-family--plus-jakarta-sans (font-sans) and --wp--preset--font-family--sora (font-display), so rename those slugs only together with src/theme.css.

Font sizes are the presets in theme.json (xs 0.75rem to 9xl 8rem, the Tailwind scale). The stylesheet uses them too: changing Large in Styles changes every text-lg in the design.

Dark mode#

The theme switch in the header toggles dark mode and remembers the choice in localStorage (pocket-theme: "dark" or "light"); otherwise the site follows the visitor's system setting. A small script printed first in <head> applies it before the page paints, so there is no flash. To always show the light theme, add add_filter( 'freeframe_pocket_dark_mode', '__return_false' ); to a child theme's functions.php and remove the switch from the header pattern.

Content#

Edit pages in Pages: click any text to change it, select a button to change its link, select an image and use Replace. Sections are groups; open List View (the stacked lines icon at the top left) to select a section, move it, duplicate it or delete it. To add a section from another page, use the block inserter (+) > Patterns > Pocket. Search for "Pocket", "Bristol" and "pocket.example" when you make it yours: they appear in the pages, the header and the footer.

Some content is in Custom HTML blocks, because blocks can't express it: edit those in the block's HTML view.

Pattern Custom HTML What it is
header the theme switch, the menu button buttons driven by assets/js/main.js
home-hero, feature-tour, feature-list, 404 the phone mockups see Images and artwork
download the QR code an SVG picture; replace it with an Image block of your own code
screenshot-carousel, screenshot-screens, screenshot-lightbox the carousel with its captions, the screens shown in the lightbox, the lightbox see Screenshot carousel and lightbox
pricing-header, pricing-compare the monthly/yearly switch and its status line; the comparison table see Pricing switch
changelog-header the type buttons, the platform select and the count see Release note filters
support-header, help-articles the search field and its count; the contact form and its thank-you panel see Help centre search and Forms
press-about, press-assets, press-toast the copy buttons, the colour swatches, the "Copied" message see Press kit copy buttons
footer the newsletter form and its thank-you message see Forms

Blocks can't store data- attributes, so the theme writes them as classes on blocks and the first lines of assets/js/main.js turn them back into attributes before anything else runs: ffd-name--value becomes data-name="value", ffa-name--value becomes the attribute name="value" (ffa-aria-labelledby--tour-title), and ff-hidden hides a block until the script shows it. They are on:

Page Block Classes
Home each tour step (an article group) ffd-tour-step--budgets (bills, goals, insights: the name of its screen in the phone)
What's new each release (an article group) ffd-release ffd-platform--both (both, ios or android)
What's new each change (a list item) ffd-change--new (new, improved or fixed)
Help centre each group of articles ffd-help-group
Help centre each article (an Accordion item) ffd-faq-item
Help centre the "No articles found" card ffd-help-empty hidden
Privacy policy each section ffd-toc-section (its anchor is the section's id)

The prices on the Pricing page and the figures on the home page keep their attributes in the paragraph's text: a price is <span data-price-monthly="4.99" data-price-annual="3.99">4.99</span> and a billing note is a paragraph with ffd-billing-note ffd-monthly-note--per_month,_billed_monthly classes (an underscore stands for a space); a figure is <span data-count="340">340</span> (with optional data-decimals and data-suffix). Edit them in the paragraph's HTML view (Edit as HTML). The table of contents links on the privacy page carry data-toc-link the same way.

reveal fades a block in as it scrolls into view (reveal-delay-1 to 3 stagger neighbours).

Images and artwork#

  • Phones: every phone is <div class="phone"><div class="phone-screen">…</div></div> in a Custom HTML block, exposed to screen readers as one picture (role="img" with an aria-label describing the screen) or hidden when it is decorative. Edit the markup inside .phone-screen, or replace it with a real screenshot (portrait, about 9:19.5, for example 1170×2532):

    html
    <div class="phone"><div class="phone-screen">
      <img src="https://your-site.com/wp-content/uploads/home-screen.png" alt="Pocket home screen showing a $1,240 safe-to-spend balance" class="w-full h-full object-cover">
    </div></div>
  • Store buttons: the "Download on iPhone / Get it for Android" buttons are Button blocks with the App store style; their icons are drawn by CSS (icon-before-app-store, icon-before-play-store). They are generic, not official badge artwork. Point their links at your App Store and Google Play pages, or replace them with the official badges (Image blocks) following Apple's and Google's guidelines.

  • Logo: the logo mark in the header and footer is assets/img/logo.svg next to the Site Title block (edit them in Appearance > Editor > Patterns > Header and Footer). The press kit offers pocket-logo.svg, pocket-logo-dark.svg and favicon.svg (the app icon) for download. The favicon is used until you set a Site Icon in Settings > General.

  • Icons are Icon blocks: pick another icon from the Pocket collection (the theme's icons) or WordPress's own.

  • Social image: assets/img/og-image.png (1200 × 630) comes from og-image.svg.

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: the desktop menu ("Main menu": Features, Screenshots, Pricing, What's new, Help) and the mobile menu ("Mobile menu": the same plus Press kit), both created by the setup. Edit them in Appearance > Editor > Navigation. WordPress marks the link to the current page. The "Get the app" buttons link to the home page's download section (/#download).

The footer's link columns are List blocks; the copyright year is added by PHP.

Features#

Feature tour#

On the home page, the phone in the tour (data-tour-phone, in a Custom HTML block) is sticky while four steps scroll past. The phone holds one screen per step (data-tour-screen="budgets"...); when a step (a group with ffd-tour-step--budgets) reaches the middle of the window, its screen is shown and the phone's label names it (from the step's heading). On small screens each step shows its own phone instead, and with reduced motion the screens don't animate. To add a step, duplicate one, change its class, and add a matching data-tour-screen inside the phone.

  • The track (data-carousel-track) is a horizontally scrolling list with scroll snapping. The previous and next buttons move one screen; with the track focused, the left and right arrow keys do the same. The status line says which screens are visible.
  • Each screen's button (data-lightbox-open="0"...) opens the lightbox (role="dialog", aria-modal="true"). Its contents come from the <template data-lightbox-item data-title="…" data-caption="…"> elements in the screenshot-screens block, one per screen, in the same order.
  • In the lightbox the arrow keys move between screens, Escape or a click on the backdrop closes it, Tab stays inside, and focus returns to the button that opened it.

To add a screenshot, add a slide to the track, a button with the next index and a matching <template>.

Pricing switch#

The two data-billing buttons (monthly, annual) use aria-pressed; choosing one swaps every price and billing note and updates a status message for screen readers (data-billing-status).

Accordions#

The pricing questions and the help articles are Accordion blocks: add, remove and reorder items in the editor. WordPress's accordion script opens and closes them (with aria-expanded); the theme styles them as in the HTML version.

Release note filters#

The type buttons (data-change-filter: all, new, improved, fixed) and the platform select (data-platform-filter) hide non-matching changes and empty releases and update the count (data-change-count). To add a release, duplicate one in List View, edit it and set its ffd-platform--… class and its changes' ffd-change--… classes (Block settings > Advanced > Additional CSS class(es)).

Typing in the search field (data-help-search) filters every article by its text, hides empty groups and shows "3 articles match …" or the "No articles found" card. The topic cards link to the groups' headings (their anchors). The contact card is sticky beside the articles; it carries data-back-to-top-hide (as the class ffd-back-to-top-hide), so the back-to-top button never covers it.

Press kit copy buttons#

Buttons with data-copy="text" copy that text; data-copy-target="boilerplate" copies the text of the block with that anchor (the "About Pocket" paragraph). The toast (data-toast, role="status") confirms the copy. Without the Clipboard API (for example on plain http://), a hidden textarea is used. Update a button's data-copy together with its visible text.

Privacy table of contents#

The links in the table of contents (data-toc-link) point at the sections' anchors; the link for the section in view gets aria-current="true". The policy text is a placeholder: have it reviewed for your product and region.

Header, mobile menu, scroll reveals, count-up and back to top#

  • The header gets its shadow once the page scrolls (is-scrolled).
  • The menu button opens #mobile-menu and updates aria-expanded and its label; Escape closes it and returns focus.
  • Blocks with the reveal class fade in once their top passes 92% of the window height (checked on scroll, so jumping to the end reveals everything above); figures with data-count count up when revealed. With reduced motion everything shows at once.
  • A "Back to top" button appears after about one screen of scrolling; it scrolls to the top (instantly with reduced motion), moves focus to the main content, lifts above the toast and hides while it would cover the help centre's contact card. To remove it, delete the "Back to top" part 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, green; yellow in dark mode), Secondary (white with a ring), Sunny (yellow), App store (dark store button with a small label line and a large name line)

WordPress's own Outline style is removed in the editor (Secondary replaces it). In an App store button, the first line is <small> and the second <strong> (edit them as HTML).

Forms#

Form Where Fields
Contact Help centre name, email, topic, phone (iPhone or Android), a message of at least 20 characters
Newsletter footer email

Every form[data-validate] is handled in assets/js/main.js: errors appear under each field (<p id="<field id>-error">) with aria-invalid, fields re-check as you type after the first submit, focus moves to the first problem, and on success the form is hidden and the element named in data-success is shown and focused. "Send another message" (data-form-reset="contact-form") clears the form and its errors and brings it back. Nothing is sent until you connect the forms.

Option 1: a form plugin#

Install a form plugin (WPForms, Gravity Forms, Fluent Forms, Contact Form 7, Jetpack Forms...), build the form there, then replace the form's Custom HTML block with the plugin's block. Give its fields the class input, its labels label and its button btn btn-primary to match the theme.

Option 2: a form service#

Keep the theme's form and send it to a service such as Formspree:

  1. Add the endpoint and field names to the form in its Custom HTML block: <form action="https://formspree.io/f/your-id" method="POST" … data-validate> and name="email"... on the fields.

  2. In a child theme's copy of assets/js/main.js, replace the body of onValid (it has a "Demo only" comment):

    js
    function onValid(form) {
      var success = document.getElementById(form.dataset.success);
      var button = form.querySelector('[type="submit"]');
      button.disabled = true;
      fetch(form.action, { method: 'POST', body: new FormData(form), headers: { Accept: 'application/json' } })
        .then(function (res) {
          if (!res.ok) throw new Error('Request failed');
          form.hidden = true;
          if (success) { success.hidden = false; success.focus(); }
        })
        .catch(function () {
          button.disabled = false;
          alert('Sorry, your message could not be sent. Please try again.');
        });
    }
  3. Update the thank-you messages, which say the forms are demos.

Saved data (localStorage)#

Pocket stores only the visitor's theme choice (pocket-theme). Any other value is ignored and the site follows the system setting.

SEO and sharing#

Page titles come from WordPress: the page title, an em dash and the site name (for example "Pricing — Pocket"); the home page shows the site name and tagline (Settings > General). The theme outputs theme-color for light and dark mode, but not 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. If you publish in the app stores, consider adding Apple's Smart App Banner (<meta name="apple-itunes-app" content="app-id=…">) with a small plugin or a child theme.

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, visible focus outlines and AA contrast in light and dark mode, the mobile menu button with aria-expanded, phone mockups exposed as single described pictures or hidden when decorative, accordions with aria-expanded, the lightbox (focus trap, Escape, focus return), aria-pressed toggles and live regions for filter counts, the carousel status, the billing status and the toast, form errors announced through aria-describedby and aria-invalid, and reduced-motion support for every animation. The demo passes axe (WCAG 2.1 A and AA) in both themes.

When you edit: keep one Heading 1 per page, give images alt text (or mark them decorative), give every new phone a meaningful aria-label, 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 or template, otherwise the class has no style. Classes you type in the editor's Additional CSS class(es) field are only styled if the stylesheet already contains them.

To deploy, zip the theme folder (named freeframe-pocket, 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-pocket. All text in patterns, templates and PHP goes through WordPress's translation functions; the script's strings (labels, counts, form errors, the carousel and lightbox status) are translated in PHP (freeframe_pocket_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-pocket wp-content/themes/freeframe-pocket/languages/freeframe-pocket.pot

Save freeframe-pocket-fr_FR.mo (for example) in wp-content/languages/themes/ or in the theme's languages/ folder. Page content created by Pocket setup is copied into your pages in the site's language at that moment; translate it in the editor like any other content. The filter values (data-change-filter, ffd-change--… classes) stay in English; translate only their labels. The billing notes' two wordings are in their classes (ffd-monthly-note--per_month,_billed_monthly): translate them there, writing spaces as underscores.

Child themes#

Use a child theme for changes you want to keep when Pocket is updated. Create wp-content/themes/pocket-child/style.css:

css
/*
Theme Name: Pocket Child
Template: freeframe-pocket
*/

Templates, parts, patterns and theme.json in the child theme override the parent's files of the same name. The parent's stylesheet only contains the classes the parent uses: if your child theme's patterns use new utility classes, compile a stylesheet for the child (copy package.json and src/theme.css, point the @source lines at the child's folders) and enqueue it. To change the script, copy assets/js/main.js into the child theme and load it in place of the parent's (wp_deregister_script( 'freeframe-pocket' ) and wp_enqueue_script() on wp_enqueue_scripts, priority 30), keeping the strings the parent prints before it (window.freeframePocketL10n).

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 > Pocket.
  • A new release or change doesn't respond to the filters. Check its ffd-release, ffd-platform--… and ffd-change--… classes.
  • A new help article isn't found by the search. It must be an item of an Accordion block inside a group with the class ffd-help-group, and carry the class ffd-faq-item.
  • The header menu shows other pages, or an old menu. Select each Navigation block and choose "Main menu" or "Mobile menu" (or yours) in the block settings.
  • Pocket 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 > Pocket pages.
  • The forms don't send anything. They are demo forms; see Forms.
  • The copy buttons show "Copy failed". Browsers only allow copying from secure pages (https:// or localhost) or after a click; check the site's address.

Differences from the HTML version#

The pages, copy, design and interactions are the same. What WordPress changes:

  • Straight apostrophes typed in text blocks are shown curly, as WordPress does everywhere (text in Custom HTML blocks and menu labels keeps them straight).
  • The FAQ and help articles are Accordion blocks: closed answers are hidden (the HTML version keeps them in the page at zero height, so they are part of its text) and open without the height animation; the plus sign is drawn by WordPress's accordion icon.
  • The hand-drawn underline under "on your side" is drawn by CSS instead of an inline SVG.
  • Data and ARIA attributes on content blocks are written as classes and restored by the script (see Content).

License#

MIT, which is compatible with the GPL that WordPress requires (see LICENSE.txt in the theme folder). Free for personal and commercial use, including the phone screens and SVG artwork. Plus Jakarta Sans and Sora are licensed under the SIL Open Font License 1.1 (assets/fonts/*/OFL.txt). Pocket, its company and everyone in this theme are fictional.

Search documentation

Search the guides and every template's documentation