Skip to content

Instantly share code, notes, and snippets.

@cyberfly
Last active May 14, 2026 04:22
Show Gist options
  • Select an option

  • Save cyberfly/05b52c1eddb01a8c36d4f19457ef7eb6 to your computer and use it in GitHub Desktop.

Select an option

Save cyberfly/05b52c1eddb01a8c36d4f19457ef7eb6 to your computer and use it in GitHub Desktop.
WordPress theme development with AI snippets

WordPress Theme Build Prompt — underscoretw

Create a WordPress theme named {THEME_SLUG} based on the underscoretw starter theme.

We will manually download the underscoretw starter theme from underscoretw.com and place it into the /themes/{THEME_SLUG}/ folder before development begins.

Your job is to continue from the existing scaffold already placed in the theme folder.

Follow this process carefully:


1. Prepare and verify the underscoretw setup

The downloaded underscoretw scaffold puts all PHP template files inside a theme/ subdirectory. WordPress scans only one level deep for style.css, so the theme is completely invisible to WordPress in this state.

Required first step — flatten the scaffold

  1. Move all contents of theme/ to the theme root
  2. Update package.json
    • change ./theme/style.css → ./style.css
    • change ./theme/style-editor.css → ./style-editor.css
    • change ./theme/js → ./js
  3. Update node_scripts/zip.js if present
    • change the themeDir path from ../theme to ..
  4. Delete the now-empty theme/ folder

Then confirm the structure is correct and run:

npm install
npm run dev

Ensure Tailwind compilation works and style.css appears at the theme root BEFORE editing any templates.


2. Use the HTML design from the wphtml/ folder

  • Preserve the design EXACTLY as provided
  • Do NOT redesign or simplify layouts
  • Preserve:
    • Tailwind classes
    • spacing
    • colors
    • gradients
    • animations
    • responsive behaviour
    • typography
    • hover effects
    • section ordering
  • Maintain semantic HTML where possible

3. Convert static HTML into WordPress templates

  • Break reusable sections into template-parts/ partials
  • Use proper WordPress template hierarchy
  • Keep underscoretw structure intact:
    • inc/
    • template-parts/
    • src/
    • compiled assets
    • functions.php organisation

Page title rules

For page <h1> headings:

  • Always use the_title()
  • Use ACF fields only for subtitles, body copy, and auxiliary content below it
  • Never echo a hardcoded title fallback alongside the_title()

4. Register editable content using ACF programmatically

  • Do NOT rely on ACF JSON import/export
  • Register all fields using PHP inside /inc/acf/
  • Use proper field groups and naming conventions

Important ACF safety rule

Always wrap every get_field() call with:

function_exists('get_field')

before calling it.

ACF may not yet be active on a fresh install or in the theme previewer. Unguarded calls can cause fatal PHP errors.

Example:

if ( function_exists( 'get_field' ) ) {
    $hero_title = get_field( 'hero_title' );
}

Options page rule

For options page fields, always pass 'option' as the second argument:

get_field( 'field_name', 'option' );

Editable content includes

  • headings
  • paragraphs
  • buttons
  • links
  • hero content
  • CTA sections
  • testimonials
  • FAQ content
  • settings pages where appropriate

5. Use Custom Post Types for repeatable content

Register CPTs in PHP inside /inc/post-types/

CPTs

  • Services
  • Portfolio
  • Team
  • Testimonials
  • FAQ

Portfolio taxonomy

Create:

portfolio_category

Rewrite rules note

After registering CPTs, the site owner must flush WordPress rewrite rules or archive/single URLs may return 404.

Add this comment to the CPT registration file:

// After activating this theme,
// go to Settings → Permalinks → Save Changes
// to flush rewrite rules.

5.5. Default content seeding from static HTML

The original HTML inside wphtml/ contains hardcoded demo content.

Do NOT leave CPT-driven sections empty after conversion.

The theme must automatically migrate the original hardcoded HTML content into WordPress content structures.

Required behaviour

For every repeatable section converted into a CPT:

  • Parse the original static HTML content
  • Create equivalent default CPT entries programmatically
  • Preserve:
    • titles
    • descriptions
    • button labels
    • categories
    • tags
    • ordering
    • image references
    • links
    • slugs

Examples

  • Service cards → service CPT
  • Portfolio cards → portfolio CPT
  • Team members → team CPT
  • Testimonials → testimonial CPT
  • FAQ accordion items → faq CPT

Seeder implementation requirements

Create a dedicated demo content seeding system:

/inc/demo-content/

Recommended structure:

/inc/demo-content/
    seed-demo-content.php
    seed-services.php
    seed-portfolio.php
    seed-team.php
    seed-testimonials.php
    seed-faq.php

Seeder behaviour

The seeder must:

  • Run only once after theme activation
  • Never create duplicate content
  • Check whether content already exists before inserting
  • Use stable slugs or unique meta flags for detection
  • Be safe to re-run manually if needed

Recommended detection pattern:

get_page_by_path( 'web-design', OBJECT, 'service' );

or:

meta_key = '_demo_seeded'

Theme activation hook

Use:

after_switch_theme

to trigger the initial seed process.

Example:

add_action( 'after_switch_theme', 'underscoretw_seed_demo_content' );

Demo images

If the original HTML references local images:

  • Import them into the Media Library if possible
  • Set them as featured images
  • Preserve original filenames where possible

If automatic importing is too fragile:

  • Keep placeholder image references
  • Add clear TODO comments

Taxonomy seeding

If portfolio/service categories exist in the original HTML:

  • Automatically create taxonomy terms
  • Assign seeded posts to proper terms

Example:

wp_insert_term( 'Web Design', 'portfolio_category' );

Content ownership rules

After seeding:

  • All frontend sections must render dynamically from WordPress data
  • Templates must NOT continue rendering hidden hardcoded fallback HTML
  • The original static HTML becomes only the source material for migration

Manual reseed utility

Provide a reusable helper function:

underscoretw_seed_demo_content();

This allows developers to regenerate demo content during development.


Important seeding goal

When the theme is activated:

  • the website should immediately resemble the original HTML design
  • without requiring the site owner to manually create CPT entries

6. Forms

  • Use Contact Form 7 ONLY
  • Do NOT build custom forms
  • Do NOT use raw <form> implementations

Insert shortcode placeholders:

[contact-form-7 id="REPLACE_ME" title="Contact Form"]

The site owner will configure forms later in wp-admin.


7. Global theme settings — site identity and logo

Never hardcode the site name, logo, or tagline.

Wire them to native WordPress settings.

Register custom logo support

In functions.php:

add_theme_support(
    'custom-logo',
    array(
        'height'      => 60,
        'width'       => 180,
        'flex-height' => true,
        'flex-width'  => true,
    )
);

Header logo implementation

In header.php:

if ( has_custom_logo() ) {
    the_custom_logo();
} else {
    echo '<a href="' . esc_url( home_url( '/' ) ) . '" class="text-2xl font-bold text-forest tracking-tight">'
        . esc_html( get_bloginfo( 'name' ) )
        . '</a>';
}

Apply the same fallback pattern in footer.php.


Settings mapping

What WordPress location Function
Logo image Appearance → Customize → Site Identity has_custom_logo() / the_custom_logo()
Site name Settings → General get_bloginfo('name')
Tagline Settings → General get_bloginfo('description')
Favicon Appearance → Customize → Site Identity Handled automatically

Never hardcode these values anywhere.


8. WordPress best practices

  • Escape all output properly
    • esc_html
    • esc_url
    • esc_attr
    • wp_kses_post
  • Use WordPress enqueue system correctly
  • Avoid duplicated markup
  • Avoid hardcoded URLs
  • Use featured images where suitable
  • Use dynamic menus and widget areas where appropriate

9. Tailwind handling

This project uses Tailwind v4 with @tailwindcss/postcss.

Important differences from Tailwind v3:

  • There is NO tailwind.config.js
  • Define custom tokens inside:
tailwind/tailwind-theme.css

using:

@theme {}
  • Opacity modifiers compile using color-mix(in oklab, ...)
  • Do NOT reference:
    • theme.extend
    • purge
    • old Tailwind v3 config patterns
  • Tailwind v4 auto-detects templates
  • Ensure all PHP templates are reachable from the theme root

10. Alpine.js

  • Alpine.js core is loaded from CDN
  • Alpine.js MUST use defer

Use:

add_filter( 'script_loader_tag', function( $tag, $handle ) {
    if ( 'alpinejs' === $handle ) {
        return str_replace( '<script ', '<script defer ', $tag );
    }
    return $tag;
}, 10, 2 );

Plugin rule

Alpine.js CDN core does NOT include plugins.

If using:

  • x-collapse
  • x-intersect
  • other plugins

explicitly enqueue the plugin CDN.

If plugin is not loaded:

  • do NOT use the directive
  • use plain x-show instead

11. Final verification

After all templates are completed:

npm run dev

Verify:

  • no Tailwind classes are missing
  • responsive layouts still match original HTML
  • templates render correctly in WordPress

12. Important implementation rules

  • Never overwrite core underscoretw functionality unless necessary
  • Reuse existing underscoretw features/components where possible
  • Extend the starter instead of rebuilding existing systems
  • Keep code modular and production-ready
  • Prefer reusable template parts over duplicated sections
  • Maintain clean separation between:
    • presentation
    • content
    • data structures
    • theme logic

Expected output

  • Fully functional custom WordPress theme
  • Based on underscoretw
  • Scaffold correctly flattened
  • Tailwind v4 compiled locally
  • All utility classes compiled correctly
  • Editable via ACF
  • All get_field() calls safely guarded
  • Dynamic CPT-powered sections
  • Automatic demo content seeding from original HTML
  • Compatible with standard WordPress workflows
  • Production-ready structure
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment