Skip to content

workingmodel/wm-wordpress-base-theme

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

32 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

WM WordPress Base Theme

A production-ready WordPress base theme built for modern development workflows. Ships with a Vite build pipeline, TypeScript, Tailwind CSS, GSAP, React islands (Shadcn UI), full Block Editor support, ACF page builder, WooCommerce templates, Schema.org structured data, and a one-command setup script.

Developed by Working Model Inc


Requirements

  • WordPress 6.0+
  • PHP 8.0+
  • Node.js 18+

Quick Start

npm run setup

This single command will:

  • Prompt for a project name and rename the theme slug + text domain throughout
  • Copy .env.example.env
  • Install all Node dependencies
  • Run the first production build

Then copy the theme folder to wp-content/themes/ and activate it in WordPress.


Scripts

Command Description
npm run setup First-time project setup (rename, install, build)
npm run dev Start Vite dev server with HMR
npm run build Production build to dist/
npm run test Run Vitest unit tests
npm run test:watch Run tests in watch mode
npm run test:coverage Run tests with coverage report
npm run lint Lint TypeScript
npm run lint:fix Lint and auto-fix
npm run format Format with Prettier
npm run type-check TypeScript type check

Project Structure

├── bin/
│   └── setup.sh                  # One-command setup script
├── patterns/                     # Block Editor patterns (auto-registered)
│   ├── hero.php
│   ├── cards.php
│   ├── cta-banner.php
│   ├── testimonial.php
│   ├── text-image.php
│   └── section-divider.php
├── woocommerce/                  # WooCommerce template overrides
│   ├── archive-product.php
│   ├── single-product.php
│   ├── cart/cart.php
│   └── checkout/form-checkout.php
├── src/
│   ├── acf-json/                 # ACF field group JSON (version-controlled)
│   ├── assets/
│   │   ├── scripts/
│   │   │   ├── main.ts           # Main JS entry point
│   │   │   ├── admin.ts          # Admin JS entry point
│   │   │   ├── islands.ts        # React island hydration entry
│   │   │   ├── animations/       # GSAP scroll animations
│   │   │   ├── components/       # Interactive components (forms, nav, dark mode)
│   │   │   └── gsap/             # GSAP setup and utilities
│   │   └── styles/
│   │       ├── main.scss         # Main stylesheet entry
│   │       ├── config/           # Variables and mixins
│   │       ├── base/             # Reset, typography, utilities
│   │       ├── components/       # Component styles
│   │       ├── layout/           # Header, footer, content
│   │       └── wordpress/        # Block editor styles
│   ├── components/
│   │   └── ui/                   # Pre-installed Shadcn components
│   │       ├── button.tsx
│   │       ├── card.tsx
│   │       ├── badge.tsx
│   │       ├── input.tsx
│   │       └── dialog.tsx
│   ├── inc/
│   │   ├── theme-functions.php   # Helper functions
│   │   ├── acf-setup.php         # ACF options page + JSON sync
│   │   ├── block-setup.php       # Block categories and styles
│   │   ├── cpt-setup.php         # Custom post type helpers
│   │   ├── woocommerce-setup.php # WooCommerce support
│   │   ├── schema.php            # Schema.org / JSON-LD output
│   │   ├── react-islands.php     # React island PHP helper
│   │   ├── performance.php       # Core Web Vitals defaults
│   │   ├── breadcrumbs.php       # Native PHP breadcrumbs
│   │   └── customizer.php        # WordPress Customizer options
│   ├── lib/
│   │   └── utils.ts              # cn(), debounce(), formatDate()
│   ├── templates/
│   │   ├── acf-layouts/          # ACF flexible content layouts
│   │   └── content-*.php         # Template parts
│   └── types/                    # TypeScript definitions
├── tests/                        # Vitest unit tests
├── theme.json                    # Block Editor design tokens
├── functions.php
├── style.css
├── page.php                      # Page template with ACF page builder loop
├── CHANGELOG.md                  # Version history
├── .wp-env.json                  # Docker-based local WordPress config
└── .env.example                  # Environment variable reference

Features

Block Editor

theme.json exposes WM design tokens directly in the editor — color palette, font families, font sizes, and spacing scale. Six block patterns are auto-registered from the patterns/ directory:

  • Hero — full-width heading + copy + dual CTA buttons
  • Three Column Cards — bordered feature grid
  • CTA Banner — dark background with yellow accent button
  • Testimonial — centered quote with rule framing
  • Text + Image — 50/50 two-column with CTA
  • Section Divider — ornamental dot separator

Three block styles are registered: core/button outline, core/separator thick, core/quote plain.

ACF Page Builder

page.php checks for a page_builder flexible content field and renders layouts from src/templates/acf-layouts/. Falls back to the_content() when ACF is inactive.

Field group JSON lives in src/acf-json/ and syncs automatically via the ACF sync screen.

Included layouts: hero, text_columns, image_text, cta_banner

Custom Post Types

src/inc/cpt-setup.php provides two helpers:

// Register a CPT
wm_register_cpt( 'project', 'Project', 'Projects' );

// Register a taxonomy
wm_register_taxonomy( 'project_type', 'Type', 'Types', ['project'] );

Both generate a full label set and sensible defaults. Pass an optional $args array to override anything. Add registrations to wm_register_post_types() in the same file.

WooCommerce

Add WooCommerce and the theme is ready. Template overrides in woocommerce/ cover the shop archive, single product, cart, and checkout — all with WC hooks intact. Styles are disabled by default so the theme's CSS takes full control.

Mobile Navigation

The header includes a fully-accessible mobile drawer built with GSAP:

  • Hamburger button (nav-toggle) with animated → × transition via CSS
  • Drawer slides in from the right (x: '100%' → '0%', 0.35s power3.out)
  • Focus trap cycles through all focusable elements within the open drawer
  • Closes on Escape, overlay click, or any nav link click; returns focus to the trigger
  • aria-expanded / aria-hidden / aria-controls wired throughout

Dark Mode

A dark mode toggle is included in the header. It reads prefers-color-scheme on first visit, then persists to localStorage. All CSS custom properties support both modes via the .dark class on <html>.

Toggle additional buttons anywhere by adding data-dark-toggle to any element.

WordPress Customizer

Theme options live under Appearance → Customize → Theme Options:

Section Settings
Brand Accent colour (live preview via postMessage)
Contact Phone, email, address
Social Links Instagram, LinkedIn, X/Twitter, Facebook, YouTube
Footer Copyright text

Helper functions for templates:

wm_get_contact( 'phone' );          // → escaped phone string
wm_get_social( 'instagram' );       // → escaped URL
wm_get_footer_text();               // → wp_kses_post'd copyright text

React Islands

Render a React component from PHP:

wm_render_react_component( 'MyComponent', [ 'label' => 'Hello' ] );

Register the component in src/assets/scripts/islands.ts, call wm_enqueue_islands() in your template, and the island hydrates client-side with the provided props.

Pre-installed Shadcn components in src/components/ui/:

import { Button } from '@/components/ui/button';
import { Card, CardHeader, CardTitle, CardContent } from '@/components/ui/card';
import { Badge } from '@/components/ui/badge';
import { Input } from '@/components/ui/input';
import { Dialog, DialogTrigger, DialogContent, DialogTitle } from '@/components/ui/dialog';

Schema.org / JSON-LD

Structured data is injected automatically in wp_head:

Context Schema types
Every page WebSite
Front page Organization (with logo if set)
Singular / archive / search BreadcrumbList
Single posts Article (headline, dates, author, image)

Breadcrumbs

Call wm_breadcrumbs() anywhere in a template. Outputs an accessible <nav> with Schema.org itemListElement microdata. Handles posts (with category ancestors), pages (with parent pages), CPT archives, taxonomy/tag/author/date archives, search, and 404.

wm_breadcrumbs(); // defaults
wm_breadcrumbs(['sep' => '/', 'class' => 'my-breadcrumbs']); // custom

Core Web Vitals

  • loading="lazy" and decoding="async" added automatically to archive thumbnails and the_content() images
  • Hero / LCP image gets fetchpriority="high" instead
  • wm_preconnect_origins filter lets you add <link rel="preconnect"> hints for third-party domains

GSAP Animations

ScrollTrigger is registered once in src/assets/scripts/gsap/init.ts. All animations respect prefers-reduced-motion.

import { animateOnScroll } from '@/assets/scripts/animations';

animateOnScroll('.my-element', { opacity: 1, y: 0, duration: 0.8 });

Configuration

Local Development with wp-env

The theme ships with a .wp-env.json for zero-dependency local WordPress via Docker:

npm run env:start   # start WordPress at http://localhost:8888
npm run env:stop    # stop containers
npm run env:clean   # clean environment (resets DB)

Requires Docker Desktop and @wordpress/env (npm i -g @wordpress/env or use npx). WordPress runs PHP 8.2 with WP_DEBUG enabled and the theme pre-mapped to wp-content/themes/wm-base-theme.

Environment Variables

Copy .env.example to .env:

VITE_PORT=3000
VITE_HMR_HOST=localhost   # Set to your local domain if using Valet/DDEV/Lando

Fonts

The theme defaults to Stack Sans Text / Stack Sans Headline (WM's internal typeface) with system-ui as fallback. Replace with your own fonts in src/assets/styles/config/_variables.scss:

Option A — Self-hosted (add to src/assets/styles/base/_typography.scss):

@font-face {
  font-family: 'My Font';
  src: url('../fonts/my-font.woff2') format('woff2');
  font-weight: 100 900;
  font-display: swap;
}

Option B — Google Fonts (enqueue in functions.php):

wp_enqueue_style('google-fonts', 'https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700&display=swap', [], null);

Then update $font-stack-text and $font-stack-headline in _variables.scss, and the fontFamilies array in theme.json.

Design Tokens

Edit tokens in two places — they're kept in sync:

  • SCSS variables: src/assets/styles/config/_variables.scss
  • Block Editor: theme.json (palette, spacing, font sizes)

Adding a Block Pattern

Create a PHP file in patterns/ with the standard header:

<?php
/**
 * Title: My Pattern
 * Slug: wm-base-theme/my-pattern
 * Categories: wm-blocks
 */
?>
<!-- wp:paragraph -->
<p>Pattern content here.</p>
<!-- /wp:paragraph -->

WordPress 6.0+ registers it automatically.

Adding an ACF Layout

  1. Add a layout to src/acf-json/group_wm_page_builder.json (or use the ACF UI and sync)
  2. Create src/templates/acf-layouts/your_layout.php
  3. Use wm_get_field() to fetch field values inside the template

Preconnect Hints

add_filter( 'wm_preconnect_origins', function ( array $origins ): array {
  $origins[] = 'https://fonts.googleapis.com';
  $origins[] = 'https://fonts.gstatic.com';
  return $origins;
} );

Testing

npm run test

Vitest runs 18 unit tests covering cn(), debounce(), form validation behaviour, dark mode toggle, and the wmTheme nonce guard. Tests use jsdom — no browser required.


Browser Support

Modern browsers (last 2 versions). IE 11 polyfills provided via @vitejs/plugin-legacy — remove from vite.config.ts if not needed.


License

MIT — see LICENSE for details.


Working Model Inc · workingmodel.co

About

A production-ready WordPress base theme built for modern development workflows. Ships with a Vite build pipeline, TypeScript, Tailwind CSS, GSAP, React islands (Shadcn UI), full Block Editor support, ACF page builder, WooCommerce templates, Schema.org structured data, and a one-command setup script. Developed by Working Model Inc

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors