Table of Contents
- Architecture
- High-level component map
- The four load-bearing decisions
- 1. Per-user state in wp_usermeta (not wp_options)
- 2. Inline-SVG character (no GIFs, no sprite sheets, no PNGs)
- 3. CSS-only animations (no JavaScript)
- 4. Tier-1 admin discipline
- File-by-file walkthrough
- Permission profile (deliberately narrow)
- What's coming (Phase B onwards)
Architecture
RangerHQ Buddy is hand-rolled PHP — no frameworks, no Composer dependencies, no build step. The whole plugin is ~6 PHP files, one stylesheet, and zero JavaScript. Currently ~28 KB packaged.
High-level component map
wp-admin
┌────────────────────────────────────────────────────────────────┐
│ WP Dashboard │
│ ┌──────────────────────────────────┐ │
│ │ Buddy Dashboard Widget │ inc/dashboard-widget.php│
│ │ - sprite (sm) │ │
│ │ - name + mood │ │
│ │ - four stat bars (h/h/h/e) │ │
│ └──────────────────────────────────┘ │
│ │
│ Sidebar: 🐾 Buddy ▾ │
│ ├─ My Buddy inc/admin-page.php │
│ ├─ Settings inc/settings.php (rename Buddy) │
│ └─ About inc/about.php (roadmap + version) │
└────────────────────────────────────────────────────────────────┘
│
│ buddy_get_state / buddy_update_state
▼
┌────────────────────────────────────────────────────────────────┐
│ inc/state.php — per-user storage in wp_usermeta │
│ Key: 'buddy_state' │
│ Shape: { name, species, hunger, happiness, health, energy, │
│ born_at, last_tick } │
└────────────────────────────────────────────────────────────────┘
│
│ values feed → mood label + tone selection
▼
┌────────────────────────────────────────────────────────────────┐
│ inc/sprite.php — inline-SVG renderer │
│ buddy_render_sprite( $tone, $size ) │
│ - tones: happy / neutral / sad / wink (cheeky surprise) │
│ - sizes: sm / md / lg │
│ - no image files, no GIFs — every pixel is SVG │
│ - CSS keyframes drive bobbing + blinking, NOT JS │
└────────────────────────────────────────────────────────────────┘
The four load-bearing decisions
1. Per-user state in wp_usermeta (not wp_options)
Each WP admin user gets their own Buddy. Storage is update_user_meta( $user_id, 'buddy_state', $state ). This means multi-admin sites work perfectly — Alice's Buddy is independent of Bob's. Nothing is shared between accounts.
The alternative (a single wp_options row for the whole site) would have meant either everyone shares one Buddy or a complex multi-row schema. wp_usermeta makes per-user a one-liner.
2. Inline-SVG character (no GIFs, no sprite sheets, no PNGs)
inc/sprite.php outputs an <svg> element directly into the page HTML. Eye shapes, mouth curves, cheek dots — all paths/circles in SVG, all parameterised by the $tone and $size arguments. Switching mood doesn't change the image; it changes a couple of SVG element attributes.
Why: zero asset weight, zero HTTP requests for the character, infinite scalability, easy to extend (add a new mood by adding a few SVG elements, not by drawing a new sprite sheet).
3. CSS-only animations (no JavaScript)
Bobbing motion and periodic blinking are CSS keyframes in assets/css/buddy.css. The wink animation that landed in v0.1.3 is also a CSS animation, not a JS timer. The plugin ships zero JavaScript for the character itself.
The only JS in the whole plugin is the WP-native admin form scaffolding (the rename form on the Settings page uses standard WordPress nonces and a POST submission — no AJAX).
4. Tier-1 admin discipline
Carried forward from the broader RangerHQ family submission discipline (see Family):
- Single
<h1>per admin page (accessibility + WordPress conventions) - No nested toggle boxes
- No duplicate sections
- Output escaping on every user-facing string (
esc_html_e,esc_attr_e, etc.) - Text-domain alignment across every translation call (
'rangerhq-buddy') wp_rand()instead ofmt_rand()for any randomness- All CSS in enqueued stylesheets — no inline
<style>blocks, no inlinestyle="..."attributes (lesson hard-won in v0.1.5 after wp.org reviewer flagged it)
File-by-file walkthrough
If you've cloned the repo and want to understand the flow, read these files in order:
buddy.php— the entry point WordPress sees. Plugin header (Plugin Name, version, text domain), constants (BUDDY_VERSION, paths),require_oncefor theinc/*.phpfiles,admin_menuregistration,admin_enqueue_scriptsenqueue, activation hook.inc/state.php—buddy_default_state(),buddy_get_state(),buddy_update_state(). The data layer.inc/sprite.php—buddy_render_sprite( $tone, $size ). The SVG character renderer.inc/dashboard-widget.php— registers the WP Dashboard widget viawp_add_dashboard_widget(). Callsbuddy_get_state()+buddy_render_sprite('happy', 'sm')+ renders stat bars.inc/admin-page.php— the dedicated "My Buddy" page in the sidebar. Larger sprite + same data.inc/about.php— the About page (intro, "what Buddy does today", roadmap, version history). All inline styles removed in v0.1.5.inc/settings.php— the rename-Buddy form. Nonce + POST handling.
Total reading time end-to-end: about 15 minutes.
Permission profile (deliberately narrow)
Buddy is a wp-admin-only plugin. It:
- ❌ Does NOT register front-end shortcodes
- ❌ Does NOT enqueue assets on the public site
- ❌ Does NOT create custom post types, taxonomies, or REST API endpoints
- ❌ Does NOT call any external service (no API requests, no telemetry, no CDN fonts)
- ✅ Only loads its CSS on the four admin pages it owns (Dashboard, My Buddy, Settings, About)
- ✅ Stores everything in
wp_usermetaon the user's own WP site
What's coming (Phase B onwards)
See Roadmap for the full phase plan. Architectural changes anticipated:
- Phase B (Interactions): Feed / Play / Clean / Sleep buttons — will add a small
inc/actions.phpwith cooldown timer logic, AJAX endpoints, and a single new JS file (the first JS the plugin will ever ship). - Phase C (Decay): WP-cron event that lowers stats over time when the user is away. Pure server-side, no JS.
- Phase D (Species): Multi-species sprite renderer.
inc/sprite.phpwill grow species-specific path libraries. - Phase E (Site health):
wp_get_site_health()results feed into mood/stat calculations. Pure server-side hook. - Phase F (Pro): Custom skins, multi-pet farm, social visits.
The core architecture (per-user state, inline-SVG, CSS animations) is locked and won't change across phases.