---
name: wp-theme
description: Creates a WordPress theme based on Underscores (_s) with Vite + Tailwind CSS v4 + Alpine.js + Swiper + AOS, using a source folder as design reference. Use when building a new WP theme from a static HTML/Vite project.
argument-hint: [source-folder-path]
disable-model-invocation: true
allowed-tools: Bash Read Write Edit Glob Grep Agent AskUserQuestion
---

# WordPress Theme Generator (Underscores + Vite/Tailwind)

You are creating a WordPress theme based on the Underscores (_s) starter theme, integrating a Vite + Tailwind CSS v4 build system from an existing source folder.

## Input

The user provides a **source folder path** as `$ARGUMENTS`. This folder contains:
- `src/index.html` — Static HTML template for the home page
- `src/js/app.js` — Main JS entry point (Alpine.js, Swiper, AOS)
- `src/css/app.css` — Main CSS with Tailwind v4 config, theme tokens, custom utilities
- `src/css/*.css` — Additional CSS modules
- `public/img/` — Static images
- `vite.config.js` — Vite configuration
- `package.json` — Dependencies

## Step 0: Gather Information

Before starting, you MUST ask the user these questions using AskUserQuestion:

### Question 1: Theme Name
Ask: "What should the theme name be? (used for the folder name, function prefixes, and text domain)"
- This will be used as: folder name, function prefix (e.g., `mytheme_setup`), text domain, and version constant (e.g., `MYTHEME_VERSION`)

### Question 2: Theme Metadata
Ask: "What are the theme details?"
- Theme URI (website URL)
- Author name
- Author URI

### Question 3: WooCommerce Support
Ask: "Do you want to include WooCommerce support from Underscores?"
- Options: "Yes — Include WooCommerce integration (inc/woocommerce.php, WooCommerce CSS)" or "No — Skip WooCommerce support"
- If YES: Keep `inc/woocommerce.php`, copy WooCommerce CSS files from source (`woocommerce.css`, `custom-woocommerce.css`, `custom-booking.css`), uncomment WooCommerce imports in `app.css`, and keep the conditional require in `functions.php`
- If NO: Delete `inc/woocommerce.php`, `woocommerce.css` from theme root, and remove the WooCommerce conditional require from `functions.php`

### Question 4: Contact Form 7 Support
Ask: "Do you want to include Contact Form 7 CSS support?"
- If YES: Copy `contactForm7.css` and uncomment its import in `app.css`
- If NO: Skip copying `contactForm7.css`

## Step 1: Read the Source

Read and analyze the source folder provided:
1. Read `src/index.html` — Identify all sections (header, banner, content sections, footer)
2. Read `src/js/app.js` — Understand JS initialization (Alpine, Swiper instances, AOS, scroll handlers)
3. Read `src/css/app.css` — Get theme tokens, custom utilities, imports
4. Read `src/css/*.css` — All additional CSS modules
5. Read `vite.config.js` — Build configuration
6. Read `package.json` — Dependencies
7. List `public/img/` — Available images

## Step 2: Clone Underscores

Clone the Underscores starter theme into the WordPress themes directory:

```bash
cd "wp-content/themes" && git clone https://github.com/Automattic/_s.git {theme-name}
```

Then immediately remove the `.git` directory:
```bash
rm -rf "wp-content/themes/{theme-name}/.git"
```

## Step 3: Rename _s to Theme Name

Replace ALL references of `_s` with the theme name. This is a multi-pattern replacement:

```bash
# In all PHP files:
find . -name "*.php" -exec sed -i "s/'_s'/'{theme-name}'/g" {} +
find . -name "*.php" -exec sed -i "s/\"_s\"/\"{theme-name}\"/g" {} +
find . -name "*.php" -exec sed -i "s/_s_/{theme-name}_/g" {} +
find . -name "*.php" -exec sed -i "s/_s-/{theme-name}-/g" {} +
find . -name "*.php" -exec sed -i "s/ _s/ {theme-name}/g" {} +
```

**CRITICAL**: The above sed commands do NOT catch `_S_VERSION` (uppercase). You MUST also:
```bash
find . -name "*.php" -exec sed -i "s/_S_VERSION/{THEME-NAME}_VERSION/g" {} +
```
Where `{THEME-NAME}_VERSION` uses the uppercase version of the theme name (e.g., `MYTHEME_VERSION`).

**VERIFY** with: `grep -r "_S_VERSION" .` and `grep -r "_s" . --include="*.php"` — both should return empty.

## Step 4: Clean Up Underscores Files

Delete files that will be replaced by the Vite build system:

```bash
rm -rf sass/
rm -f style-rtl.css js/customizer.js js/navigation.js
rm -f composer.json phpcs.xml.dist .eslintrc .stylelintrc.json
rm -f README.md LICENSE
rmdir js/ 2>/dev/null
```

If WooCommerce support was declined, also:
```bash
rm -f inc/woocommerce.php woocommerce.css
```

## Step 5: Copy Source Files

```bash
mkdir -p src/js src/css public/img
cp {source}/src/js/app.js src/js/app.js
cp {source}/src/css/*.css src/css/
cp {source}/public/img/* public/img/
cp {source}/pnpm-lock.yaml ./
```

## Step 6: Create Build System Files

### `package.json`
Write the same package.json as the source with only `dev` and `build` scripts.

### `vite.config.js`
Write the Vite config adapted for WordPress (no index.html):

**CRITICAL**: Use `rollupOptions.input` as an OBJECT `{ app: 'js/app.js' }`, NOT a string. A string causes the dev server to fail with "failed to resolve rollupOptions.input".

```js
import { defineConfig } from 'vite';
import tailwindcss from '@tailwindcss/vite';

export default defineConfig({
    root: './src',
    publicDir: '../public',
    build: {
        manifest: true,
        outDir: '../dist',
        emptyOutDir: true,
        assetsDir: 'assets',
        rollupOptions: {
            input: { app: 'js/app.js' },
            output: {
                assetFileNames: (assetInfo) => {
                    const info = assetInfo.names[0].split('.')
                    let extType = info[info.length - 1]
                    if (/png|jpe?g|svg|gif|tiff|bmp|ico/i.test(extType)) {
                        extType = 'img'
                    } else if (/woff|woff2/.test(extType)) {
                        extType = 'css'
                    }
                    return `assets/${extType}/[name]-[hash][extname]`
                },
                chunkFileNames: 'assets/js/[name]-[hash].js',
                entryFileNames: 'assets/js/[name]-[hash].js',
            },
        },
    },
    server: { strictPort: true, port: 3000, cors: true },
    plugins: [tailwindcss()],
    resolve: { alias: { '@': '/src/js' } },
});
```

### `.gitignore`
```
/node_modules
/dist
.DS_Store
```

## Step 7: Configure Tailwind CSS v4 Source Scanning

**CRITICAL**: Tailwind CSS v4 only scans files inside Vite's `root` (`./src`) by default. PHP templates are OUTSIDE `src/`, so Tailwind won't detect their classes and the CSS output will be incomplete.

In `src/css/app.css`, modify the Tailwind import to include the theme folder:

```css
@import "tailwindcss" source("../../../{theme-name}");
```

The `source()` path is relative to the CSS file and must point to the theme's root directory so Tailwind scans all PHP files.

## Step 8: Rewrite `style.css`

Replace ALL content with only the theme metadata header (no CSS rules):

```css
/*
Theme Name: {Theme Display Name}
Theme URI: {theme-uri}
Author: {author}
Author URI: {author-uri}
Description: {description}
Version: 1.0.0
Tested up to: 6.7
Requires PHP: 7.4
License: GNU General Public License v2 or later
License URI: LICENSE
Text Domain: {theme-name}
Tags: custom-menu, featured-images, threaded-comments, translation-ready
*/
```

## Step 9: Rewrite `functions.php`

Replace the `{theme-name}_scripts()` function with a Vite asset loader:

1. **Define version constant**: `define('{THEME_NAME}_VERSION', '1.0.0');`
2. **Keep**: `{theme-name}_setup()`, `register_nav_menus`, theme supports, widget registration, all `require` statements for `inc/` files
3. **Remove**: The old `wp_enqueue_style` for `style.css`, `wp_style_add_data` for RTL, `wp_enqueue_script` for `navigation.js`
4. **Add `{theme-name}_vite_assets()`**:
   - If `VITE_DEV` constant is true: enqueue scripts from `http://localhost:3000/`
   - Else: read `dist/.vite/manifest.json`, enqueue hashed CSS/JS from manifest entry `js/app.js`
5. **Add `script_loader_tag` filter**: Add `type="module"` to Vite script tags
6. **Add Google Fonts enqueue**: Raleway (or whatever font the source uses)
7. **If NO WooCommerce**: Remove the `if (class_exists('WooCommerce'))` require block

## Step 10: Rewrite `header.php`

Convert the `<header>` section from `src/index.html` to WordPress PHP:

- `<!doctype html><html <?php language_attributes(); ?>>`
- `<head>` with `wp_head()`, Google Fonts preconnect links
- `<body <?php body_class('font-sans antialiased leading-loose'); ?> x-data="{ showMenu: false }">`
- `<?php wp_body_open(); ?>`
- Logo: `<img src="<?php echo esc_url(get_theme_file_uri('dist/img/{logo}')); ?>">`
- Desktop nav: `wp_nav_menu(['theme_location' => 'menu-1', 'container' => false, 'menu_class' => '{desktop-menu-classes}'])`
- Mobile hamburger button with Alpine.js `@click`
- Mobile nav: second `wp_nav_menu()` inside Alpine container

## Step 11: Rewrite `footer.php`

Convert the `<footer>` section from `src/index.html` to WordPress PHP:

- All static content from the source footer
- Images with `get_theme_file_uri('dist/img/...')`
- Dynamic year: `<?php echo esc_html(wp_date('Y')); ?>`
- `<?php wp_footer(); ?>` before `</body>`
- Close `</body></html>`

## Step 12: Create `front-page.php`

Create a new file with all content sections from `src/index.html` (everything between header and footer):

```php
<?php get_header(); ?>
<!-- All sections from index.html -->
<?php get_footer(); ?>
```

All image paths: `./img/X` becomes `<?php echo esc_url(get_theme_file_uri('dist/img/X')); ?>`
All inline style background images: `url('<?php echo esc_url(get_theme_file_uri('dist/img/X')); ?>')`

## Step 13: Build & Verify

```bash
cd "wp-content/themes/{theme-name}"
pnpm install
pnpm build
```

Verify:
1. `dist/.vite/manifest.json` exists with entry for `js/app.js` containing `css` array
2. `dist/assets/css/` and `dist/assets/js/` contain hashed files
3. `dist/img/` contains all images from `public/img/`

## Step 14: Post-Setup Instructions

Tell the user:
1. Activate the theme in WP Admin > Appearance > Themes
2. Create a page and set it as static front page in Settings > Reading
3. Create a menu in Appearance > Menus and assign to "Primary"
4. For development: add `define('VITE_DEV', true);` to `wp-config.php` and run `pnpm dev`
5. For production: run `pnpm build` (remove or set `VITE_DEV` to false)

## Important Rules

- Always use `esc_url()` for image URLs and `esc_html()` for text output in PHP templates
- Keep ALL Underscores template files (index.php, page.php, single.php, archive.php, search.php, 404.php, comments.php, sidebar.php, template-parts/) — only rename prefixes
- The `inc/` directory files (template-tags.php, template-functions.php, customizer.php, custom-header.php) are kept and renamed
- Use `get_theme_file_uri()` for all asset paths (not `get_template_directory_uri()`)
- The Vite manifest path is `dist/.vite/manifest.json` (Vite 5+ places it inside `.vite/` subfolder)
