9.4 KiB
9.4 KiB
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:
- Imports Google Fonts to local storage (one-time download)
- Registers them with WordPress's native Font Library API
- 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
maple-local-fonts.php— Plugin header, constants, autoloader, activation hookindex.phpfiles in all directories (silence is golden)includes/class-mlf-ajax-handler.php— With full security checks (nonce, capability, validation)
Phase 2: Core Functionality
includes/class-mlf-font-downloader.php— Google Fonts fetching/parsing/downloadingincludes/class-mlf-font-registry.php— WP Font Library integration
Phase 3: Admin Interface
includes/class-mlf-admin-page.php— Settings page renderassets/admin.css— Admin page stylesassets/admin.js— AJAX handling for download/delete
Phase 4: Cleanup & Polish
uninstall.php— Clean removal of fonts and datareadme.txt— WordPress.org readme with privacy section- 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
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
// 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