Files
peptide-starter-theme/CONVENTIONS.md
peptiderepo 9da702e746 v1.5.0: extend theme with navigation dropdowns, 9 page templates, auth UI, newsletter, contact panel
Major theme extension adding full-featured navigation with dropdown submenus (Tools, My Data, Resources),
9 new page templates (Calculator, Protocol Builder, Tracker, Subject Log, Documentation, Directory,
Science Feed, Profile, Sign In/Register), documentation page with sidebar ToC and scroll spy,
branded AJAX authentication forms, newsletter signup with admin export, slide-out contact/support panel,
Research Modules 6-card grid on front page, and comprehensive footer update.

Split functions.php (554→206 lines) into 6 focused inc/ modules: helpers, customizer, auth-handlers,
contact-handler, page-setup, newsletter-admin. All new components include dark mode support, CSRF
protection, input sanitization, output escaping, and graceful plugin degradation via shortcode_exists().

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-14 08:38:30 +07:00

16 KiB
Raw Blame History

Peptide Starter Theme - Conventions & Development Guide

Naming Conventions

PHP Functions

All custom PHP functions are prefixed with peptide_starter_:

// Good
function peptide_starter_get_hero_title() { }
function peptide_starter_setup() { }
function peptide_starter_widgets_init() { }
function peptide_starter_pagination() { }

// Bad
function get_hero_title() { }
function setup_theme() { }

Pattern: peptide_starter_{verb}_{noun} or peptide_starter_get_{option}

Common function types:

  • peptide_starter_get_*() — Retrieve theme mod or option
  • peptide_starter_{noun}_init() — Initialization functions
  • peptide_starter_is_*() — Boolean checks
  • peptide_starter_render_*() — Output HTML

CSS Classes

All theme CSS classes are prefixed with .ps-:

/* Components */
.ps-btn               /* Button component */
.ps-btn-primary       /* Button variant */
.ps-btn-sm            /* Button size */
.ps-card              /* Card component */
.ps-badge             /* Badge component */
.ps-alert-info        /* Alert variant */

/* Utilities */
.ps-container         /* Max-width wrapper */
.ps-pagination        /* Pagination nav */
.ps-search-form       /* Search form */
.ps-search-input      /* Search input */

/* Regions */
.site-header          /* No prefix; global region */
.site-footer          /* No prefix; global region */
.site-main            /* No prefix; global region */
.hero-section         /* Hero region specific */

Pattern: .ps-{component} for components, .ps-{component}-{variant} for variants

Reserved prefixes (do not use):

  • .pn-* — Peptide News plugin
  • .psa-* — Peptide Search AI plugin
  • .prab-* — PRAutoBlogger plugin

CSS Variables (Custom Properties)

All design tokens are defined in style.css :root:

/* Colors */
--text-primary        /* Main text color */
--text-secondary      /* Secondary text color */
--bg-primary          /* Main background */
--bg-secondary        /* Secondary background */

/* Spacing (8px grid base) */
--spacing-xs: 4px     /* 0.5 × base */
--spacing-sm: 8px     /* 1 × base */
--spacing-md: 16px    /* 2 × base */
--spacing-lg: 24px    /* 3 × base */
--spacing-xl: 32px    /* 4 × base */

/* Typography */
--font-sans           /* Primary font family */
--font-mono           /* Monospace font */
--text-h1             /* H1 size */
--text-h1-weight      /* H1 weight */

Usage:

.ps-card {
  padding: var(--spacing-md);
  color: var(--text-primary);
}

Template Part Naming

Template parts in template-parts/ follow WordPress convention:

template-parts/content.php              /* Default loop item */
template-parts/content-{post-type}.php  /* CPT-specific */
template-parts/content-none.php         /* Empty state */
template-parts/{section}-{name}.php     /* Specific parts (if added) */

Calling template parts:

get_template_part( 'template-parts/content', get_post_type() );
get_template_part( 'template-parts/content', 'none' );

Hook Names

Custom hooks use the peptide_starter_ prefix:

// Filters
apply_filters( 'peptide_starter_show_newsletter', true );

// Actions (if any added in future)
do_action( 'peptide_starter_hero_before' );

File Organization

  • Root: Theme core files (functions.php, header.php, footer.php, etc.)
  • assets/js/: Client-side scripts only; no bundling
  • assets/css/: (None; all CSS in style.css)
  • languages/: Translation files
  • template-parts/: Reusable template parts

Step-by-Step Guides

How to Add a New Template

Scenario: Create a custom landing page template

  1. Create file: landing-{name}.php

    <?php
    /**
     * Landing Page: {Description}
     * Template Name: {Display Name}
     *
     * @package peptide-starter
     */
    
    get_header();
    ?>
    
    <main id="main" class="site-main">
      <!-- Your content here -->
    </main>
    
    <?php get_footer();
    
  2. Add template header comment:

    /**
     * Template Name: My Custom Landing Page
     * Template Post Type: page
     */
    
  3. The template will appear in WordPress Page > Template dropdown

  4. Test the template:

    • Create/edit a page and select the template
    • Verify hero, dark mode toggle, footer all appear correctly

Best practices:

  • Always include get_header() and get_footer()
  • Wrap main content in <main id="main" class="site-main">
  • Use .ps-container for consistent max-width
  • Use existing component classes (.ps-card, .ps-btn, etc.)
  • Keep templates under 150 lines; extract complex parts to template-parts/

How to Add a New Plugin Integration

Scenario: Integrate a new plugin (e.g., "Research Database") via shortcode

  1. In front-page.php, add a conditional shortcode block:

    <?php if ( shortcode_exists( 'research_db' ) ) { ?>
      <section class="research-db-section">
        <div class="ps-container">
          <h2><?php esc_html_e( 'Research Database', 'peptide-starter' ); ?></h2>
          <?php echo do_shortcode( '[research_db limit="10"]' ); ?>
        </div>
      </section>
    <?php } ?>
    
  2. In functions.php, optionally add inline overrides if plugin CSS needs adjustment:

    function peptide_starter_plugin_overrides() {
      wp_add_inline_style( 'peptide-starter-style', '
        /* Override plugin styles here */
        .rdb-card { /* example */ }
      ' );
    }
    add_action( 'wp_enqueue_scripts', 'peptide_starter_plugin_overrides', 99 );
    
  3. Document the integration in CLAUDE.md:

    • Plugin namespace (e.g., .rdb-*)
    • Shortcode name and parameters
    • Expected styling classes
  4. If plugin needs dark mode coordination:

    • Ask plugin author to listen to themechange event (see ARCHITECTURE.md)
    • Or have plugin read html[data-theme] attribute
    • Provide plugin with CSS custom properties: getComputedStyle(document.documentElement).getPropertyValue('--text-primary')
  5. Test:

    • Activate plugin and visit front page
    • Verify shortcode renders
    • Toggle dark mode; verify plugin responds
    • Deactivate plugin; verify page still renders (fallback gracefully)

Important: Always wrap shortcodes in shortcode_exists() checks so theme doesn't break if plugin is missing.

How to Add a New CSS Component

Scenario: Create a new "pill" button style (small, rounded, inline)

  1. In style.css, find the Components section (around line 500)

  2. Add new component class:

    /* Pill buttons */
    .ps-btn-pill {
      display: inline-block;
      padding: var(--spacing-xs) var(--spacing-sm);
      border-radius: 9999px;
      font-size: 12px;
      font-weight: 600;
      text-transform: uppercase;
      letter-spacing: 0.5px;
    }
    
    .ps-btn-pill.ps-btn-primary {
      background-color: var(--color-primary);
      color: white;
    }
    
    .ps-btn-pill.ps-btn-secondary {
      background-color: transparent;
      border: 1px solid var(--color-primary);
      color: var(--color-primary);
    }
    
  3. Add dark mode support (in Dark Mode section, around line 1200):

    html[data-theme="dark"] .ps-btn-pill.ps-btn-secondary {
      border-color: var(--color-primary-dark);
      color: var(--color-primary-dark);
    }
    
  4. Add responsive sizes if needed (in Responsive section, around line 1400):

    @media (max-width: 767px) {
      .ps-btn-pill {
        padding: var(--spacing-xs) var(--spacing-xs);
      }
    }
    
  5. Usage in templates:

    <span class="ps-btn ps-btn-pill ps-btn-primary">Beta</span>
    
  6. Test:

    • Light mode: verify colors, sizing, alignment
    • Dark mode: toggle and verify contrast
    • Mobile (480px): verify responsive adjustments
    • Keyboard: focus state should be visible

Best practices:

  • Use CSS variables (var(--spacing-*), var(--color-*)) not hardcoded values
  • Avoid !important unless absolutely necessary
  • Include dark mode support immediately (no .ps-* is light-only)
  • Group related rules with comments
  • Keep component definitions self-contained (<100 lines)

How to Add a New Customizer Setting

Scenario: Add a new "Primary Accent Color" customizer option

  1. In functions.php (around line 143), add a new section:

    $wp_customize->add_section( 'peptide_starter_colors', array(
      'title'    => esc_html__( 'Colors', 'peptide-starter' ),
      'priority' => 45,
    ) );
    
  2. Add a setting:

    $wp_customize->add_setting( 'primary_accent_color', array(
      'default'           => '#0066CC',
      'transport'         => 'postMessage',
      'sanitize_callback' => 'sanitize_hex_color',
    ) );
    
  3. Add a control:

    $wp_customize->add_control( new WP_Customize_Color_Control(
      $wp_customize,
      'primary_accent_color',
      array(
        'label'    => esc_html__( 'Primary Accent Color', 'peptide-starter' ),
        'section'  => 'peptide_starter_colors',
        'settings' => 'primary_accent_color',
      )
    ) );
    
  4. Create a getter function:

    function peptide_starter_get_primary_accent_color() {
      return get_theme_mod( 'primary_accent_color', '#0066CC' );
    }
    
  5. Output in front-end (e.g., in footer.php):

    <style>
      :root {
        --color-accent: <?php echo esc_attr( peptide_starter_get_primary_accent_color() ); ?>;
      }
    </style>
    

    Or better, use inline style in wp_head:

    function peptide_starter_custom_colors() {
      $accent = peptide_starter_get_primary_accent_color();
      ?>
      <style>
        :root {
          --color-accent: <?php echo esc_attr( $accent ); ?>;
        }
      </style>
      <?php
    }
    add_action( 'wp_head', 'peptide_starter_custom_colors' );
    
  6. Reference in CSS:

    .ps-btn-accent {
      background-color: var(--color-accent);
    }
    
  7. Test:

    • Appearance → Customize → Colors → Primary Accent Color
    • Change color and verify live preview
    • Save and reload page

Transport options:

  • 'transport' => 'postMessage' — Instant preview (needs JS listener)
  • 'transport' => 'refresh' — Full page refresh

For color/text changes, use postMessage with a JS listener in theme.js:

wp_customize.bind( 'setting:primary_accent_color', function( setting ) {
  setting.bind( function( newval ) {
    document.documentElement.style.setProperty('--color-accent', newval);
  });
});

Error Handling Patterns

Theme Doesn't Break If Plugin Missing

Pattern: Graceful Degradation

// Good — shortcode_exists check
if ( shortcode_exists( 'peptide_search' ) ) {
  echo do_shortcode( '[peptide_search]' );
} else {
  get_search_form();  // Fallback to WordPress default
}

// Bad — assumes plugin exists
echo do_shortcode( '[peptide_search]' );  // Renders [peptide_search] as text if missing

Menu Fallback

In header.php:

wp_nav_menu( array(
  'theme_location' => 'primary',
  'fallback_cb'    => 'peptide_starter_primary_menu_fallback',  // Function defined in header
  'walker'         => new Peptide_Starter_Nav_Walker(),
) );

function peptide_starter_primary_menu_fallback() {
  // Renders Home, About, Peptides links if no menu assigned
}

Widget Area Check

In footer.php:

if ( is_active_sidebar( 'footer-1' ) ) {
  dynamic_sidebar( 'footer-1' );
} else {
  // Render default content
  echo '<h3>' . esc_html__( 'About', 'peptide-starter' ) . '</h3>';
}

Class Existence Check

For WooCommerce:

if ( class_exists( 'WooCommerce' ) ) {
  // Render cart icon
}

For post types:

if ( post_type_exists( 'peptide' ) ) {
  echo '<a href="' . get_post_type_archive_link( 'peptide' ) . '">' . __( 'Peptides' ) . '</a>';
}

Input Validation

All user-facing output must be escaped:

// Good
echo esc_html( get_the_title() );
echo esc_url( home_url( '/' ) );
echo esc_attr( $html_attribute_value );

// Bad
echo get_the_title();
echo home_url( '/' );

Dark Mode Implementation Guide

How Dark Mode Works in Peptide Starter

  1. Inline script in wp_head (functions.php, line 406-422)

    • Runs immediately before CSS loads
    • Prevents white flash by setting html[data-theme] early
    • Checks in order: localStorage → system pref → customizer default
  2. CSS selectors (style.css, line ~1200 "Dark Mode")

    • Light mode: default colors in :root
    • Dark mode: html[data-theme="dark"] selector overrides
  3. JavaScript listener (theme.js, line 47-63)

    • Responds to dark mode toggle button
    • Updates localStorage
    • Dispatches themechange event to plugins
    • Updates sun/moon icon visibility

Adding Dark Mode to a New Component

Example: New Alert Style

  1. Define light mode in CSS:

    .ps-alert-custom {
      padding: var(--spacing-md);
      background-color: #E3F2FD;  /* Light blue */
      color: #0D47A1;              /* Dark blue text */
      border: 1px solid #90CAF9;
    }
    
  2. Add dark mode override:

    html[data-theme="dark"] .ps-alert-custom {
      background-color: #1A237E;  /* Dark blue bg */
      color: #E3F2FD;              /* Light text */
      border-color: #3F51B5;       /* Darker border */
    }
    
  3. Test:

    • Load page in light mode → verify colors
    • Toggle dark mode → verify override colors are applied
    • Open DevTools → verify html[data-theme="dark"] attribute set
    • Check localStorage peptide-starter-theme is updated

Helping Plugins Support Dark Mode

If a plugin doesn't listen to dark mode changes:

  1. Ask plugin author to listen to the themechange event:

    document.addEventListener('themechange', (e) => {
      const theme = e.detail.theme;
      // Update plugin styles based on theme
    });
    
  2. Or use a MutationObserver to watch for data-theme changes:

    const observer = new MutationObserver(() => {
      const theme = document.documentElement.getAttribute('data-theme');
      // Update styles
    });
    observer.observe(document.documentElement, { attributes: true });
    
  3. Or read CSS variables in the plugin stylesheet:

    .plugin-card {
      color: var(--text-primary);
      background-color: var(--bg-secondary);
    }
    

CSS Variables for Dark Mode

All available variables (defined in :root):

--text-primary          /* Main text (light: #111827, dark: #F9FAFB) */
--text-secondary        /* Secondary text (light: #6B7280, dark: #D1D5DB) */
--bg-primary            /* Main background (light: #FFFFFF, dark: #111827) */
--bg-secondary          /* Secondary bg (light: #F3F4F6, dark: #1F2937) */
--color-primary         /* Primary accent (light: #0066CC, dark: #3B82F6) */
--border-color          /* Border color (light: #E5E7EB, dark: #374151) */

Testing Checklist

When modifying the theme, verify:

  • Light mode — All text readable, colors correct
  • Dark mode — Toggle to dark, verify colors swap, no white flash on reload
  • Mobile (480px) — Navigation collapses, search/dark mode icons present
  • Tablet (768px) — Layout adapts, footer 2-column if needed
  • Desktop (1024px+) — Full layout renders correctly
  • Plugins active — Shortcodes render, no JS errors in console
  • Plugins inactive — Graceful fallbacks work (search form, no broken layout)
  • Accessibility — Tab through page, all interactive elements reachable
  • Focus states — Every button/link has visible focus outline
  • Keyboard navigation — Escape closes overlays, arrow keys in menu if relevant

Common Tasks & Commands

Check JavaScript syntax:

node --check assets/js/theme.js
node --check assets/js/navigation.js

View theme version:

echo PEPTIDE_STARTER_VERSION;  // Currently: 1.3.1

Update theme version (when releasing):

  1. Edit style.css line 7: Version: X.Y.Z
  2. Edit functions.php line 14: define( 'PEPTIDE_STARTER_VERSION', 'X.Y.Z' )

Deploy to production:

  • Push to main branch on GitHub
  • GitHub Actions runs rsync to Hostinger via SSH
  • LiteSpeed cache clears automatically

Debug active plugins:

// In functions.php or template
$active = get_option( 'active_plugins' );
error_log( print_r( $active, true ) );

View Customizer settings:

echo get_theme_mod( 'hero_title' );
echo get_theme_mod( 'dark_mode_default', false );
  • ARCHITECTURE.md — System design, data flows, integrations
  • CLAUDE.md — Developer context and quick reference
  • README.md — User-facing feature documentation
  • CHANGELOG.md — Version history