wp-admin-browser
Use when a WordPress admin panel needs real browser interaction via Chrome DevTools MCP — logging in, navigating admin menus,…
Use when adding or refactoring transactional emails in a WordPress plugin — extracting inline HTML strings into reusable templates sharing a branded base shell, implementing the render_template helper (ob_start / include / ob_get_clean), passing variables safely with
Claude Code
$ npx -y skills add mralaminahamed/wp-dev-skills --skill wp-email-templates --agent claude-code
Codex
$ npx skills add mralaminahamed/wp-dev-skills --skill wp-email-templates -a codex -y
Cursor
$ npx skills add mralaminahamed/wp-dev-skills --skill wp-email-templates -a cursor -y
Gemini CLI
$ npx skills add mralaminahamed/wp-dev-skills --skill wp-email-templates -a gemini-cli -y
GitHub Copilot
$ npx skills add mralaminahamed/wp-dev-skills --skill wp-email-templates -a github-copilot -y
OpenCode
$ npx skills add mralaminahamed/wp-dev-skills --skill wp-email-templates -a opencode -y
Windsurf
$ npx skills add mralaminahamed/wp-dev-skills --skill wp-email-templates -a windsurf -y
name: wp-email-templates description: "Use when adding or refactoring transactional emails in a WordPress plugin — extracting inline HTML strings into reusable templates sharing a branded base shell, implementing the render_template helper (ob_start / include / ob_get_clean), passing variables safely with extract(EXTR_SKIP), sending via wp_mail(), testing with MockPHPMailer / tests_retrieve_phpmailer_instance(), applying table-based HTML layout and inline CSS for email clients, or handling wp_mail send-failure branches. Triggers: \"send an email from my plugin\", \"wp_mail not working\", \"add an email template\", \"style my plugin emails\", \"HTML email in WordPress\", \"branded email wrapper\", \"email not arriving\", \"test my wp_mail\", \"add a CTA button to the email\", \"make emails look good in Outlook\", \"transactional email template\", \"render_template helper\", \"ob_start for email\", \"extract EXTR_SKIP\", \"MockPHPMailer in tests\", \"tests_retrieve_phpmailer_instance\", \"pre_wp_mail filter to test failure\", \"inline CSS email\", \"dark mode email support\", \"table-based email layout\", \"wp_mail headers\", \"email not sending in tests\". Not for: REST API webhooks, push notifications, or transactional email in themes."
> **Model note:** Mechanical refactoring — extract strings, create template files, wire `wp_mail()`. `haiku` handles end-to-end.
Move email bodies out of inline PHP strings into `templates/emails/`, where every message reuses one branded shell. Adding a new email = adding one content file.
**Not for:** REST API webhooks, push notifications, or Slack integrations. Transactional email in themes — this skill targets plugin-delivered mail only.
templates/emails/ base.php shared shell: header, body slot ($content), footer <name>.php per-email content (greeting, copy, CTA button)
public static function render_template( string $path, array $vars = [] ): string {
if ( ! is_readable( $path ) ) return '';
// phpcs:ignore WordPress.PHP.DontExtract.extract_extract -- controlled template vars
extract( $vars, EXTR_SKIP );
ob_start();
include $path;
return (string) ob_get_clean();
}
public static function render_email( string $template, array $vars = [] ): string {
$dir = (string) plugin_config( 'templates' ) . 'emails/'; // your assets/templates path helper
$vars['content'] = self::render_template( $dir . $template . '.php', $vars );
$vars['site_name'] = $vars['site_name'] ?? get_bloginfo( 'name' );
$vars['site_url'] = $vars['site_url'] ?? home_url( '/' );
return self::render_template( $dir . 'base.php', $vars ); // always wrap in the shell
}A missing content template yields an empty body but the shell still renders — callers always get a valid document.
$body = Helpers::render_email( 'welcome', [ 'subject' => $subject, 'user_name' => $u->display_name, ... ] ); $headers = [ 'Content-Type: text/html; charset=UTF-8' ]; // REQUIRED for HTML return wp_mail( $to, $subject, $body, $headers );
Drive any TTL/expiry copy from the same constant the code uses (e.g. a token transient TTL) so email text can't drift from behavior.
WP's test suite captures `wp_mail` via MockPHPMailer:
reset_phpmailer_instance(); // ... trigger the send ... $sent = tests_retrieve_phpmailer_instance()->get_sent(); $this->assertStringContainsString( 'text/html', $sent->header ); $this->assertStringContainsString( '<!DOCTYPE html', $sent->body ); // base shell used $this->assertStringContainsString( $expected_cta, $sent->body );
Force a send failure with `add_filter( 'pre_wp_mail', '__return_false' )` to test the error branch.
Covers the complete WordPress plugin development lifecycle — build, test, audit, release, and ship to WP.org — for Claude Code, Gemini CLI, Cursor, Windsurf, Cline, Codex, GitHub Copilot, opencode, and more.
Use when a WordPress admin panel needs real browser interaction via Chrome DevTools MCP — logging in, navigating admin menus,…
Use when a WordPress plugin needs to run work outside the HTTP request cycle — scheduling async or recurring jobs with Action…
Use when setting up, configuring, or debugging the JavaScript/CSS build pipeline for a WordPress plugin — @wordpress/scripts,…
Use when a pull request has QA failures, a \"Testing Failed\" label, or QA comments reporting broken features — reading QA…
Use when setting up PHPCS with WordPress Coding Standards (WPCS), configuring phpcs.xml.dist, running phpcs/phpcbf, fixing sniff…
Use when a WordPress plugin needs a custom database table — creating with dbDelta (strict SQL format: two spaces before PRIMARY…
You MUST use this before any creative work - creating features, building components, adding functionality, modifying behavior, or…
Ask which skill or flow fits your situation. A router over the skills in this repo.