This is the step-by-step playbook for building the theme.
Read start-stackable.md first if you do not yet know what Default, tokens, or phases are.
Work one phase at a time, in order.
When a phase is done, tick that phase in start-stackable.check.md.
Do not start the next phase until the current one is checked off.
- Confirm you are on the phase marked do this next (right now: Phase 2).
- Read that phase's "is for" sentence so you know the job.
- Do the numbered items in order. Each item has What (the outcome) and How (the files and edits).
- Use This phase is done when as your own smoke test in WordPress.
- Then run the matching list in the acceptance check.
Skip Phase 0. It is already finished.
Product rules (what you must not add): start-stackable.md.
Word meanings: CONTEXT.md.
Constants for every file you add:
- WordPress:
Requires at leastinstyle.css(currently 6.7) theme.jsonversion:3- Text domain:
start-stackable - PHP function prefix:
start_stackable_ - No
stackable/*blocks in this repo - PHP bootstrap filename:
functions.php(neverfunction.php)
| Phase | Status |
|---|---|
| 0 Bootstrap | Done. Skip it. |
| 1 Design system | Do this next. |
| 2-11 | Later. Do not start these yet. |
The file tree in the appendix is the destination. It is not what the repo looks like today.
You need a local WordPress where this folder is the active theme.
- Point
wp-content/themes/start-stackableat this repo (symlink or copy). - In wp-admin: Appearance → Themes → Activate Start Stackable.
- Open Appearance → Editor (Site Editor) to judge styles, headers, and templates.
- Open the front of the site (
/) to judge first activation.
What to run while you edit:
| You are editing | Command |
|---|---|
theme.json, templates/, parts/, patterns/, styles/ |
Nothing to compile. Refresh the Site Editor or front. |
src/css/ or src/js/ |
npm run start (watches and writes assets/build/) |
| Need an installable zip | npm run build |
If a theme.json change seems ignored, the site may have user Global Styles stored in the database.
Test on a fresh site, or in Site Editor → Styles reset to defaults.
Do not put colors, font sizes, or spacing into CSS.
Those belong in theme.json.
- Use core blocks only in templates, parts, and patterns (no
stackable/*). - Navigation blocks must not have a
"ref":attribute. Let WordPress create the menu on the site. - Every color, font-size, and spacing value in HTML must be a slug that exists in
theme.json.spacing|50and colorsecondaryare bugs unless those slugs exist (they do not). - Keep
settings.useRootPaddingAwareAlignmentsastrue. - Every user-visible string in a PHP pattern uses
esc_html__/esc_html_ewith text domainstart-stackable. - No remote file fetch, no demo importer, no plugin requirement.
Status: done. Do not redo this.
This phase already gave us a legal block theme: functions.php, compile tooling, empty folders for later work, and stub templates/parts.
Already true (do not recreate from scratch):
functions.phpexists and enqueuesassets/build/frontend.*when that file is present.- It adds body class
stk--is-stackable-theme. npm run startcompilessrc/→assets/build/.templates/andparts/files exist (they are still skeleton markup).patterns/,assets/fonts/,assets/images/, andstyles/typography/are empty on purpose.- Navigation has no hardcoded
ref.
This phase is done when: the theme activates with no PHP fatal, Site Editor opens, and npm run start emits assets/build/frontend.js and frontend.css.
That is already true.
Status: done.
What you are making: colors, type, spacing, shadows, radius, and default styles for headings/buttons/quotes so a boring core-block page already looks intentional. Default type is Plus Jakarta Sans for headings and the system UI stack for body. You are not redesigning the header, post cards, or patterns yet. Those come in later phases and will inherit this work.
Files you will edit:
theme.json(main work)assets/fonts/(add.woff2files)readme.txt(font license)
-
Bundle Plus Jakarta Sans for headings
What: Default headings use a bundled OFL webfont. Body copy uses the system UI stack (already registered as slug
sans-serif). That pairing is intentional: headings carry the product voice, body stays native and small in the zip.How:
- Download Plus Jakarta Sans (variable
.woff2if available, otherwise the weights you need). - Confirm the license file is OFL or SIL.
- Put the
.woff2file(s) inassets/fonts/. - Put the license text in
readme.txtunder Copyright (font name, license, URL). - Do not replace body with this face.
- Do not pick a different heading font unless the PRD changes.
Check:
assets/fonts/is no longer only.gitkeep. - Download Plus Jakarta Sans (variable
-
Register two families and assign heading vs body
What: WordPress loads Jakarta for headings (including site title and post title). Paragraphs, excerpts, nav, captions, and buttons keep the system stack.
How: keep the existing
sans-seriffamily insettings.typography.fontFamiliesfor body. Add a second family withfontFacepointing at the file you added. Keepstyles.typography.fontFamilyasvar:preset|font-family|sans-serif.Shape (path must match the file you added):
{ "name": "Plus Jakarta Sans", "slug": "plus-jakarta-sans", "fontFamily": "\"Plus Jakarta Sans\", sans-serif", "fontFace": [ { "fontFamily": "Plus Jakarta Sans", "fontWeight": "200 800", "fontStyle": "normal", "src": ["file:./assets/fonts/plus-jakarta-sans.woff2"] } ] }Then set
styles.elements.heading.typography.fontFamilytovar:preset|font-family|plus-jakarta-sans. Set the same family onstyles.blocks.core/site-titleandstyles.blocks.core/post-titleso those match headings.Check: Site Editor → a Heading uses Plus Jakarta Sans. A Paragraph still uses the system stack.
-
Keep palette slugs; you may refine hex values
What: Stackable already inherits these slug names. Renaming a slug breaks the contract.
How: in
settings.color.palette, do not rename or delete:primary,primary-light,primary-soft,primary-deep,base,base-accent,tint,contrast,contrast-accent,outline,outline-contrast. You may change the"color"hex so Default looks better. Keeplayout.contentSizeat645pxandlayout.wideSizeat1340px.Check: grep
theme.jsonfor those eleven slugs; they still exist. -
Make font sizes fluid
What: type should scale between a min (phone) and max (desktop), not a single rem.
How: each object in
settings.typography.fontSizesalready hasname,slug, andsize. Add afluidobject withminandmax. Keep the existing slugs (x-smallthroughxxx-large).Shape:
{ "name": "M", "slug": "medium", "size": "1.125rem", "fluid": { "min": "1rem", "max": "1.125rem" } }Repeat for every size.
settings.typography.fluidis alreadytrue; leave it.Check: resize the front end from ~375px to desktop; body and headings change size smoothly.
-
Make spacing fluid with
clamp()What: mobile must not look like a shrunk desktop (huge padding, huge gaps).
How: in
settings.spacing.spacingSizes, change each"size"from a single rem (for example"0.5rem") to aclamp(). Keep the existing slugs (smallthroughxxxx-large).Shape:
{ "name": "S", "slug": "small", "size": "clamp(0.5rem, 0.4rem + 0.3vw, 0.5rem)" }Tune the middle values so the scale still feels even. Do not invent new slugs such as
50or60.Check: a page with padding presets does not look oversized on a 375px-wide window.
-
Add shadow presets
What: the editor can pick Small / Medium / Large shadows instead of raw CSS.
How: add
settings.shadow.presets(create theshadowobject if missing).Shape:
{ "name": "Small", "slug": "small", "shadow": "0 1px 2px rgb(0 0 0 / 0.06)" }Add at least small, medium, and large. Use a darker shadow only if you also ship a dark variation later.
Check: Site Editor → a Group block → Border & Shadow shows your presets.
-
Add border radius sizes
What: buttons and cards use named radii, not a one-off
5pxin templates.How: add
settings.border.radiusSizes.Shape:
{ "name": "Small", "slug": "small", "size": "4px" }Add a range from small through large, plus a full/pill size if you want round buttons. Square buttons (
0) are allowed only if that is a deliberate Default look. Otherwise pointstyles.blocks.core/buttonborder radius at a preset, not"0"unless you mean it.Check: a Button in the editor offers your radius presets.
-
Style elements (headings, links, buttons, captions)
What: default HTML elements look designed without the user picking anything.
How: extend
styles.elementsintheme.json. Headings already have font sizes. HeadingfontFamilyshould already be Plus Jakarta Sans from step 2. AddfontWeightandlineHeightonh1-h6. Addbutton(fill) and make sure outline buttons stay readable (the outline variation already lives understyles.blocks.core/button.variations.outline). Stylelink(and hover if you set it) andcaption.Check: a page with Heading, Paragraph, Button (fill + outline), and a caption looks consistent in the editor with no extra CSS file.
-
Style core blocks you will actually use
What: navigation, quotes, search, pagination, and post title match Default, not leftover core greys.
How: add entries under
styles.blocksfor at least:core/button(already started),core/navigation,core/site-title,core/quote,core/pullquote,core/separator,core/search,core/query-pagination,core/post-title,core/post-terms,core/post-comments-form(and comment blocks if you touch them). Use palette slugs (var:preset|color|primary, and the other contract slugs), not raw hex.Check: insert each of those blocks on a test page; none look like unstyled core.
-
Confirm custom templates and parts are registered
What: Site Editor lists Blank, Full Width, and all header/footer parts.
How:
customTemplatesandtemplatePartsare already intheme.json. Do not duplicate them. Only edit titles if a name is unclear.Check: in Site Editor, you can assign Blank Canvas and Full Width to a page, and swap header/footer parts.
-
Optional extras
What: a short gradient or duotone set that uses palette colors.
How: only if it still references existing palette slugs. If you replace core defaults, turn
defaultPalette/defaultGradients/defaultDuotoneoff so the UI is not a second vocabulary.Skip this if time is tight. Items 1-10 are the phase.
- Site Editor → Styles looks like a finished design system (type, colors, shadows, radius).
- A Heading uses Plus Jakarta Sans; a Paragraph uses the system stack.
- A page of core Paragraph + Heading + Button looks intentional.
- You did not need a new CSS file to achieve that.
readme.txtnames Plus Jakarta Sans and its license.
Then work the Phase 1 list in start-stackable.check.md.
Status: do this next.
What you are making: skins the user can switch in Site Editor → Styles. Not a second palette with different slug names. Same slugs, different values.
Files you will edit:
styles/colors/*.json(already eight hue files)styles/typography/*.json(empty except.gitkeep)- optionally one combined file under
styles/for a named look
-
Audit existing color files
What: each color skin is accessible and uses the same eleven slugs as
theme.json.How: open every file in
styles/colors/. Keep the slug list identical to Default. Adjust hex socontrasttext onbase(and buttons onprimary) is readable. Do not addsecondaryor other extra slugs.Check: switch Purple, Teal, etc. in Styles; header, footer, and buttons change; no leftover blue from Default.
-
Add at least two typography variations
What: the user can pick more than one type treatment (for example Default vs Display).
How: add JSON files under
styles/typography/(for exampledisplay.json). Each file needs"version": 3, a"title", andsettings.typographyand/orstyles.typographythat changefontFamiliesor heading scale. Still use the Phase 1 pairing as Default (Jakarta headings, system body). A variation may change heading scale or weight. If a variation introduces another webfont, bundle and license it the same way.Check: switching a typography variation changes headings on the front.
-
Add at least one dark variation
What: a dark skin that still uses slugs
base,contrast,primary, and the rest.How: either a new file in
styles/colors/(for exampledark.json) or a combined file instyles/. Invertbase/contrast(dark background, light text). Recheck button and link contrast.Check: activating the dark style makes the whole site dark, including header and footer, with no hardcoded white/black left in templates.
-
Optional named looks
What: a combined "Agency" style that sets color + type together.
How: only if it still overrides the same slugs. Skip if the color + typography files already cover it.
Switching each variation in Site Editor updates header, footer, buttons, and blog cards. Templates contain no leftover hardcoded hex that ignores the variation.
Then work the Phase 2 list in the acceptance check.
Status: later (after Phase 2).
What you are making: header and footer that look designed on a blank install, and that are safe (no broken menu IDs).
The intended structure is: markup lives in a pattern file, and parts/*.html only includes that pattern.
Files you will add or edit:
patterns/header.php,header-transparent.php,header-minimal.php,footer.php,footer-landing.phpparts/*.html(replace current markup with a pattern include)theme.jsontemplateParts(already registered; do not duplicate)
See Appendix B for the slug list.
-
Write the header pattern first
What: a solid bar with site title or logo, and Navigation, optional search or button.
How: add
patterns/header.php. WordPress reads the file header automatically.<?php /** * Title: Header * Slug: start-stackable/header * Categories: header * Block Types: core/template-part/header */ ?>
Below the header, paste block markup (you can copy from
parts/header.htmlas a starting point, then design it). Every user-visible string:esc_html_e( 'Search', 'start-stackable' );or wrap inesc_html__(). Navigation:<!-- wp:navigation {"overlayMenu":"mobile"} /-->with no"ref".Check: Appearance → Editor → Patterns shows Header.
-
Point
parts/header.htmlat that patternWhat: the template part is one line, so header markup lives in one place.
How: replace the contents of
parts/header.htmlwith:<!-- wp:pattern {"slug":"start-stackable/header"} /-->Check: the front still shows a header. Editing the pattern edits every template that uses the header part.
-
Repeat for transparent, minimal, both footers
What: five designed parts, five patterns.
How: same as steps 1-2 for:
start-stackable/header-transparent(same structure, no opaque background)start-stackable/header-minimal(compact)start-stackable/footer(columns: identity, socials, two navs, copyright)start-stackable/footer-landing(compact)
Social links:
url":"#"only. Footer copyright: site title or "© {year} {site name}", not "Start Stackable Theme". Optional: a core Buttons CTA in the default header.Check: you can swap header/footer parts in the Site Editor. Front shows logo/title + nav + footer with no "Attempt recovery" warnings.
Activate on a blank site, assign a menu in the Site Editor, and the front shows a designed header and footer. No missing-block warnings. No author brand in the copyright.
Then work the Phase 3 list in the acceptance check.
Status: later (after Phase 3).
What you are making: first activation "wow" with only Hello World (or a couple of posts).
index.html is the latest-posts front.
home.html is the same grid when the user later sets a static front page.
Files you will add or edit:
patterns/post-card.php,post-meta.php,comments.phppatterns/template-index-grid.php,template-archive.php,template-search.php,template-404.php,template-single.phptemplates/index.html,home.html,archive.html,search.html,404.html,single.html
Template patterns use Inserter: false so users do not insert a whole page layout by accident.
See Appendix B.
-
Build a post card pattern
What: one card used in every grid (home, archive, search results).
How:
patterns/post-card.php, inserter true, category posts. Include featured image with a fixed aspect ratio, title as a link, excerpt, category, date. Usetheme.jsonspacing and radius presets, notborder-radius:5pxand notspacing|50.Check: inserting the pattern in a post looks like a real card, not loose core blocks.
-
Build the index/home template pattern
What: header + grid of cards + pagination + footer.
How:
patterns/template-index-grid.phpwithTemplate Types: index, homeandInserter: false. Query Loopinherit: true. Insidepost-template, include the post-card pattern (or the same blocks). Add query pagination.Then make
templates/index.htmlandtemplates/home.htmleach:<!-- wp:pattern {"slug":"start-stackable/template-index-grid"} /-->Check:
/on a latest-posts site shows header, grid, footer. Nofront-page.htmlfile exists. -
Build archive, search, and 404 the same way
What: those views feel like the same product.
How:
- Archive: header + archive title + same grid + footer.
- Search: header + search title/field + results (cards or a designed list) + empty state + footer.
- 404: header + designed "not found" + search + footer.
Replace
templates/archive.html,search.html,404.htmlwith pattern includes.
Check: a category URL, a search, and a bogus URL all look designed. Empty search still looks designed.
-
Build the single post
What: a crafted article, not leftover core.
How:
template-singlewith category terms, H1 title,post-metapattern (author · date · categories), featured image, content,commentspattern, previous/next or a related Query Loop. Pointtemplates/single.htmlat that pattern. Fix any leftoverspacing|50/secondaryslugs while you are here.Check: a post with a featured image shows title, image, meta, comments form.
A site with two posts and featured images shows a designed grid on /, a crafted single, working search and 404.
No overlapping header.
Cards, type, and spacing match Phase 1.
Then work the Phase 4 list in the acceptance check.
Status: later (after Phase 4).
What you are making: three ways a page can sit in the shell. Ordinary pages keep a title. Site Kit marketing pages need no theme title and no extra padding that blocks a full-bleed first section. Blank is content only (no header/footer).
Files: patterns/template-page.php, template-full-width.php, template-blank.php (or edit the HTML templates directly if you have not extracted patterns yet), plus templates/page.html, full-width.html, blank.html.
theme.json already registers full-width and blank under customTemplates.
-
Ordinary page canvas
What: title is allowed, content is constrained.
How:
templates/page.html(ortemplate-pagepattern include): header, Post Title, optional featured image, Post Content, footer. Use constrained layout so text does not span the whole viewport.Check: a normal page shows an H1 that comes from Post Title.
-
Full-width canvas (kit default)
What: header + footer, no Post Title, no extra
mainpadding.How: today's
templates/full-width.htmlwrapspost-contentin a padded Group. Remove that padding (and any margin that keepsalignfulloff the viewport edges). Do not output a Post Title block.post-contentshould bealignfull.Check: the same page assigned to Full Width has no theme H1. A full-width Group/Cover as the first block reaches the left and right edges.
-
Blank canvas
What: landing / coming soon: content only.
How:
templates/blank.htmlshould be Post Content only. Nowp:template-partfor header or footer.Check: view source or the front DOM: no site header or footer.
page shows a title.
full-width does not, and alignfull works.
blank has no header/footer in the DOM.
Then work the Phase 5 list in the acceptance check.
Status: later (after Phase 5).
What you are making: theme-owned header flags (behaviors), with Stackable off.
theme.json cannot do this, so it lives in src/.
functions.php already enqueues the compiled bundle.
Do not add a second enqueue.
Files: src/css/header-flags.css, src/js/header-flags.js (stubs already exist).
Run npm run start while you edit them.
Contract class names (honor these on body or the header wrapper):
stk-shell-header-stickystk-shell-header-transparent
-
Keep measuring
--stk-header-heightWhat: heroes can pad themselves below the bar.
How:
src/js/header-flags.jsalready sets--stk-header-heighton:rootfrom.wp-site-blocks > header. Keep that. If the bar shrinks after scroll, also set a scrolled variant (document it if kits will use it).Check: in DevTools,
--stk-header-heightis a non-zeropxvalue. -
Sticky
What: the header stays at the top while the page scrolls.
How: in
header-flags.css, when.stk-shell-header-stickyis present, position the header so it sticks. Do not require a Stackable container.Check: add the class in the editor or as a body class; scroll; the bar stays.
-
Transparent + scroll-to-solid
What: overlay on a full-bleed first section; after scroll, opaque background and readable text.
How:
.stk-shell-header-transparentremoves the opaque bar at the top of the page. On scroll, JS adds a scrolled class; CSS gives a solid background and contrast-safe text. Works on thefull-widthcanvas with a tallalignfullfirst Group (no Stackable block required).Check: nav sits over the hero; after scroll the bar is opaque and still readable.
-
Dual logo (if two logos exist)
What: optional logo swap when the bar becomes solid.
How: two Site Logo blocks (or an image pair). Toggle visibility with the scrolled class. Skip the extra markup if you only have one logo; still support the class for kits.
-
Mobile menu above the hero
What: the overlay Navigation must not sit under the first section.
How: raise
z-indexof the nav overlay above the full-bleed first section.Check: on a phone width, open the menu over a hero; it is clickable and visible.
A full-width page with a tall alignfull first Group shows nav over the hero.
After scroll the bar is opaque.
--stk-header-height is non-zero px.
Mobile menu works over the hero.
All with Stackable off.
Then work the Phase 6 list in the acceptance check.
Status: later (after Phase 6).
What you are making: users can reconstruct Default from the inserter, and optionally start one marketing page without the plugin. This is not a Design Library. Do not add standalone hero, pricing, testimonial, team, FAQ, or logo-grid patterns.
Files: finish any missing patterns from Appendix B; add patterns/page-home.php.
-
Finish atoms
What: post-card, post-meta, comments, header/footer patterns all exist and are inserter-visible where the inventory says so.
How: add whatever Phase 3-4 did not already add. Template patterns stay
Inserter: false.Check: inserter shows header, footer, post-card, comments.
-
Add exactly one Homepage starter
What: a full homepage built from core Group, Cover, Columns, Heading, Buttons, Query.
How:
patterns/page-home.phpwithSlug: start-stackable/page-homeandBlock Types: core/post-contentso it appears when creating a new page. Core blocks only. Use Default tokens.Check: New Page offers the starter. Inserted on a Full Width page it looks coherent with Default. Inserter does not list a hero/pricing/testimonial set.
-
Pattern categories
What: patterns are findable.
How: use core categories (
header,footer,posts) first. Register extra categories infunctions.phponly if those are not enough.
Inserter shows the shell patterns plus page-home, and does not show a section catalog.
Then work the Phase 7 list in the acceptance check.
Status: later (after Phase 7).
What you are making: if Woo is installed, shop/product/cart/checkout/account are not unstyled core. If Woo is not installed, the theme still activates.
Files: templates/archive-product.html, single-product.html, page-cart.html, page-checkout.html, plus account / order confirmation if the current Woo block-theme handbook lists them.
Optional hidden Woo patterns.
-
Add the HTML templates
What: Woo views use Woo/core blocks and this theme's tokens.
How: follow the current Woo block theme handbook for the file names. Reuse spacing, radius, and type from
theme.json. Do not require Woo in PHP. Unused templates are harmless until Woo is active.Check: with Woo active, shop and product are designed. With Woo inactive, the theme still activates.
-
Mobile cart and checkout
What: readable on a phone.
How: check the templates at 375px width. Fix spacing with presets, not a one-off CSS file unless
theme.jsoncannot express it. -
Tags
What: do not advertise e-commerce until these files exist.
How: add the
e-commercetag tostyle.cssonly after this phase ships.
Woo on → shop/product look like Default. Woo off → theme still activates. Theme Check does not fail on missing Woo.
Then work the Phase 8 list in the acceptance check.
Status: later (after Phase 8).
What you are making: recommend Stackable without auto-installing it, and optionally share breakpoints with the plugin.
Identity (supports, enqueue, body class) is already in functions.php.
Do not add a kit catalog, importer, or remote pattern fetch.
File: functions.php only.
-
Leave existing setup alone unless it is wrong
What: supports, enqueue, body class already exist.
How:
start_stackable_setup,start_stackable_enqueue_assets, andstart_stackable_body_classare done. Do not add a second enqueue path for header flags. -
Dismissible "install Stackable" admin notice
What: if the plugin is inactive, show a notice. User installs from WordPress.org if they want it. Gone when the plugin is active.
How: in
functions.php, onadmin_notices, if Stackable is not active, print a dismissible notice. Recommend slugstackable-ultimate-gutenberg-blocks(WordPress.org only). Use core plugin-install capability or TGMPA; install only on user action. Persist dismiss (user meta or similar) so it does not return every page load after dismiss. If the plugin is active, output nothing.Check: plugin off → notice shows and can be dismissed. Plugin on → no notice.
-
Breakpoint handshake
What: when the plugin is present, it can use this theme's tablet/mobile widths.
How: wrap in
function_exists/ plugin-active check, thenadd_filter( 'stackable_responsive_breakpoints', ... )with this theme's widths. Do nothing if the plugin is absent.Check: with plugin off, no PHP notices. With plugin on, the filter runs.
-
Never import on activate
What: switching to this theme does not create kit pages or menus (beyond what core Navigation does).
How: do not hook
after_switch_theme/after_setup_themeto create content.
Plugin off → notice dismissible, site works. Plugin on → body class present, notice gone. Activate never creates kit pages.
Then work the Phase 9 list in the acceptance check.
Status: later (after Phase 9).
What you are making: a zip a Theme Directory reviewer can walk without Stackable.
Files: screenshot.png, readme.txt, style.css.
-
Screenshot of Default
What: 1200×900 image of
/with sample posts, not a Site Kit.How: capture the designed blog after Phase 4. Save as
screenshot.pngin the theme root.Check: the file exists and matches what a fresh latest-posts site shows.
-
readme.txt for the listing
What: sells the designed blog + FSE, not the architecture.
How: Description, FAQ (plugin is optional; no bundled starter sites), licenses (GPL, font, images), changelog via the existing process. Do not hand-edit changelog files that the repo marks as generated.
-
Honest
style.csstagsWhat: only tags that are true.
How: include
full-site-editing,block-patterns,style-variations,blog. Adde-commerceonly if Phase 8 shipped. -
Theme Check
What: fix real Theme Review issues.
How: run Theme Check (or the project's equivalent) against the theme folder or the
npm run buildzip. Fix failures rather than silencing them without cause.
A reviewer with Stackable deleted can walk Default, and the listing matches the zip.
Then work the Phase 10 list in the acceptance check.
Status: later (after Phase 10).
What you are making: a kit import can assign canvases and header flags without editing theme PHP and without this theme importing anything.
Files: header-flag CSS/JS (already from Phase 6), maybe a thin functions.php reader for post meta if classes are not enough.
Contract: sibling repo ../Stackable/docs/prd/site-kits.CONTRACT.md.
-
Honor the classes/meta the plugin writes
What: import sets
full-width+ sticky + transparent and the home hero sits under the header.How: implement whatever the contract names (
stk-shell-header-sticky,stk-shell-header-transparent, and any page meta). Do not invent a second set of class names. If the contract and this theme disagree, update both in the same change.Check: with Stackable mounted, a fixture import (plugin e2e or a manual assignment) produces the overlay hero.
-
Receive a style overlay
What: the plugin may select a shipped variation or write user Global Styles using the same token slugs.
How: do not have the theme edit its own JSON from the plugin. Just keep slugs stable so an overlay works.
-
Do not import
What: this repo still contains zero kit HTML.
How: no pages, menus, or kit JSON in this theme.
Plugin can assign full-width + transparent + sticky without a theme PHP change for each kit.
This repo still has zero kit HTML.
Then work the Phase 11 list in the acceptance check.
- Run the full
start-stackable.check.md. - Add e2e specs listed in
start-stackable.agents.mdfor surfaces that now exist. Do not add failing specs for unfinished work. npm run buildand install that zip on a clean WordPress.
The theme is done when the full acceptance check passes and Theme Review constraints in .cursor/rules/wordpress-theme-review.mdc still hold.
When every phase is done, npm run build copies a tree like this (docs, e2e, .cursor, src/, webpack.config.js, and node_modules stay out):
start-stackable/
style.css
theme.json
functions.php
screenshot.png
readme.txt
assets/fonts/ # Plus Jakarta Sans (headings) + license cited in readme
assets/images/ # CC0 images used by patterns
assets/build/ # compiled frontend.css / .js / .asset.php
templates/ # index, home, page, full-width, blank, single,
# archive, search, 404, plus Woo files if Phase 8 shipped
parts/ # header, header-transparent, header-minimal,
# footer, footer-landing
patterns/ # see Appendix B
styles/colors/*.json
styles/typography/*.json
Do not ship front-page.html.
Do not ship function.php.
Developer-only (not in the zip): src/ (edit here), webpack.config.js.
Use this when you reach Phases 3, 4, and 7.
Slugs are start-stackable/<name>.
Templates and parts should become one-line pattern includes so strings stay translatable and layout lives in one file.
| Slug | Template Types | Contents |
|---|---|---|
template-index-grid |
index, home |
header part + query grid of post cards + pagination + footer |
template-archive |
archive |
header + archive title + same grid + footer |
template-search |
search |
header + search title/field + results + footer |
template-404 |
404 |
header + designed not-found + search + footer |
template-single |
single |
header + terms, H1, meta, featured image, content, comments, post nav + footer |
template-page |
page |
header + title + featured image + content + footer |
template-full-width |
(custom template) | header + post-content alignfull, no Post Title, no extra main padding + footer |
template-blank |
(custom) | post-content only |
| Slug | Part |
|---|---|
header |
Solid bar: Site Title or Logo, Navigation without ref, optional search or button |
header-transparent |
Same structure, no opaque background |
header-minimal |
Compact |
footer |
Columns: site identity, socials, two nav columns, copyright = site name not "Start Stackable Theme" |
footer-landing |
Compact |
| Slug | Role |
|---|---|
post-card |
Featured image (fixed aspect), title link, excerpt, category, date |
post-meta |
Author · date · categories |
comments |
Heading, comment list, form; designed, not leftover core |
| Slug | Role |
|---|---|
page-home |
Block Types: core/post-content. One full homepage from core blocks. Not a section catalog. |
Do not add standalone hero, pricing, testimonial, team, FAQ, or logo-grid patterns.
Shop archive, single product, cart, checkout, account: core/Woo blocks, same tokens.