# CLAUDE.md — Maple Local Fonts WordPress Plugin ## Quick Reference | Document | When to Reference | |----------|-------------------| | **CLAUDE.md** (this file) | Architecture, file structure, build order | | **SECURITY.md** | Writing ANY PHP code | | **GOOGLE_FONTS_API.md** | Building the font downloader class | | **WORDPRESS_COMPATIBILITY.md** | Building the font registry, FSE integration | --- ## Project Overview Build a WordPress plugin called **Maple Local Fonts** that: 1. Imports Google Fonts to local storage (one-time download) 2. Registers them with WordPress's native Font Library API 3. Fonts appear in FSE typography dropdowns alongside theme fonts **Key principle:** Work WITH WordPress, not around it. We're a font importer — WordPress handles everything else. --- ## Requirements - **Minimum PHP:** 7.4 - **Minimum WordPress:** 6.5 (required for Font Library API) - **License:** GPL-2.0-or-later --- ## User Flow ``` Admin enters "Open Sans" → selects weights (400, 700) → selects styles (normal, italic) ↓ Plugin hits Google Fonts CSS2 API (ONE TIME) ↓ Downloads WOFF2 files to wp-content/fonts/ ↓ Registers font via WP Font Library API ↓ Font appears in Appearance → Editor → Styles → Typography dropdown ↓ User applies font using standard WordPress FSE controls ↓ GOOGLE NEVER CONTACTED AGAIN — fonts served locally ``` --- ## File Structure ``` maple-local-fonts/ ├── maple-local-fonts.php # Main plugin file ├── index.php # Silence is golden ├── uninstall.php # Clean removal ├── readme.txt # WordPress.org readme ├── includes/ │ ├── index.php # Silence is golden │ ├── class-mlf-font-downloader.php │ ├── class-mlf-font-registry.php │ ├── class-mlf-admin-page.php │ └── class-mlf-ajax-handler.php ├── assets/ │ ├── index.php # Silence is golden │ ├── admin.css │ └── admin.js └── languages/ ├── index.php # Silence is golden └── maple-local-fonts.pot ``` --- ## Class Responsibilities ### MLF_Font_Downloader - Build Google Fonts CSS2 URL - Fetch CSS (with correct user-agent for WOFF2) - Parse CSS to extract font face data - Download WOFF2 files to wp-content/fonts/ - **Reference:** GOOGLE_FONTS_API.md ### MLF_Font_Registry - Register fonts with WP Font Library (wp_font_family, wp_font_face post types) - Delete fonts (remove posts and files) - List imported fonts - **Reference:** WORDPRESS_COMPATIBILITY.md ### MLF_Admin_Page - Render settings page under Appearance menu - Font name input, weight checkboxes, style checkboxes - Display installed fonts with delete buttons - **Reference:** SECURITY.md for output escaping ### MLF_Ajax_Handler - Handle download requests - Handle delete requests - **Reference:** SECURITY.md for nonce/capability checks --- ## Admin Page UI ``` ┌─────────────────────────────────────────────────────────┐ │ Maple Local Fonts │ ├─────────────────────────────────────────────────────────┤ │ IMPORT FROM GOOGLE FONTS │ │ ┌─────────────────────────────────────────────────────┐ │ │ │ Font Name: [Open Sans_________________________] │ │ │ │ │ │ │ │ Weights: │ │ │ │ ☑ 400 (Regular) ☐ 300 (Light) ☐ 500 (Medium) │ │ │ │ ☑ 700 (Bold) ☐ 600 (Semi) ☐ 800 (Extra) │ │ │ │ │ │ │ │ Styles: │ │ │ │ ☑ Normal ☑ Italic │ │ │ │ │ │ │ │ Files to download: 4 │ │ │ │ [Download & Install] │ │ │ └─────────────────────────────────────────────────────┘ │ │ │ │ INSTALLED FONTS │ │ ┌─────────────────────────────────────────────────────┐ │ │ │ Open Sans │ │ │ │ 400 normal, 400 italic, 700 normal, 700 italic │ │ │ │ [Delete] │ │ │ ├─────────────────────────────────────────────────────┤ │ │ │ Roboto │ │ │ │ 400 normal, 500 normal, 700 normal │ │ │ │ [Delete] │ │ │ └─────────────────────────────────────────────────────┘ │ │ │ │ ℹ️ Use Appearance → Editor → Styles → Typography to │ │ apply fonts to your site. │ └─────────────────────────────────────────────────────────┘ ``` --- ## Build Order ### Phase 1: Foundation 1. `maple-local-fonts.php` — Plugin header, constants, autoloader, activation hook 2. `index.php` files in all directories (silence is golden) 3. `includes/class-mlf-ajax-handler.php` — With full security checks (nonce, capability, validation) ### Phase 2: Core Functionality 4. `includes/class-mlf-font-downloader.php` — Google Fonts fetching/parsing/downloading 5. `includes/class-mlf-font-registry.php` — WP Font Library integration ### Phase 3: Admin Interface 6. `includes/class-mlf-admin-page.php` — Settings page render 7. `assets/admin.css` — Admin page styles 8. `assets/admin.js` — AJAX handling for download/delete ### Phase 4: Cleanup & Polish 9. `uninstall.php` — Clean removal of fonts and data 10. `readme.txt` — WordPress.org readme with privacy section 11. Testing against all compatibility targets --- ## Critical Reminders ### Security (see SECURITY.md) - ABSPATH check on EVERY PHP file - index.php in EVERY directory - Nonce verification FIRST in every AJAX handler - Capability check SECOND - Input validation THIRD - Escape ALL output ### Performance - Zero JavaScript on frontend - Zero CSS on frontend - All plugin code is admin-side only (except lightweight font registration) - Timeout handling on all external requests - Maximum limits to prevent infinite loops ### GDPR - No user data collected - External requests only during admin import - Fonts served locally after import - Document in readme.txt ### Compatibility (see WORDPRESS_COMPATIBILITY.md) - Declare WooCommerce HPOS compatibility - Use wp_get_font_dir() not hardcoded paths - Use Font Library API (post types) not theme.json filtering - Don't touch frontend, let Global Styles handle everything --- ## Constants ```php define('MLF_VERSION', '1.0.0'); define('MLF_PLUGIN_DIR', plugin_dir_path(__FILE__)); define('MLF_PLUGIN_URL', plugin_dir_url(__FILE__)); define('MLF_PLUGIN_BASENAME', plugin_basename(__FILE__)); // Limits define('MLF_MAX_FONTS_PER_REQUEST', 10); define('MLF_MAX_WEIGHTS_PER_FONT', 9); define('MLF_REQUEST_TIMEOUT', 30); ``` --- ## WordPress Hooks Used ```php // Activation register_activation_hook(__FILE__, 'mlf_activate'); // Admin menu add_action('admin_menu', 'mlf_register_menu'); // Admin assets (our page only) add_action('admin_enqueue_scripts', 'mlf_enqueue_admin_assets'); // AJAX (admin only, no nopriv) add_action('wp_ajax_mlf_download_font', 'mlf_ajax_download'); add_action('wp_ajax_mlf_delete_font', 'mlf_ajax_delete'); // WooCommerce HPOS add_action('before_woocommerce_init', 'mlf_declare_hpos_compatibility'); ``` --- ## Testing Checklist ### Functional - [ ] Import Open Sans 400, 700 normal + italic → 4 files created - [ ] Font appears in FSE typography dropdown - [ ] Apply font in Global Styles → frontend shows local font - [ ] Delete font → files and posts removed - [ ] Re-import same font → appropriate error message ### Security - [ ] AJAX without nonce → 403 - [ ] AJAX as subscriber → 403 - [ ] Path traversal in font name → rejected - [ ] XSS in font name → sanitized - [ ] Direct PHP file access → blank/exit ### Performance - [ ] No frontend network requests from plugin - [ ] Import 5 fonts → completes without timeout - [ ] Admin page load < 500ms added time ### Compatibility - [ ] WordPress 6.5, 6.6+ - [ ] Twenty Twenty-Five theme - [ ] WooCommerce (HPOS enabled) - [ ] Wordfence (enabled) - [ ] LearnDash - [ ] WPForms