Initial commit: peptide-starter theme v1.3.1
This commit is contained in:
58
.github/workflows/deploy.yml
vendored
Normal file
58
.github/workflows/deploy.yml
vendored
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
name: Deploy Theme to Hostinger
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [main]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
deploy:
|
||||||
|
name: Deploy to Production
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Deploy via SSH
|
||||||
|
uses: appleboy/ssh-action@v1
|
||||||
|
with:
|
||||||
|
host: ${{ secrets.SSH_HOST }}
|
||||||
|
username: ${{ secrets.SSH_USERNAME }}
|
||||||
|
key: ${{ secrets.SSH_PRIVATE_KEY }}
|
||||||
|
port: ${{ secrets.SSH_PORT }}
|
||||||
|
script: |
|
||||||
|
THEME_DIR="/home/${{ secrets.SSH_USERNAME }}/domains/peptiderepo.com/public_html/wp-content/themes/peptide-starter"
|
||||||
|
REPO_URL="https://github.com/peptiderepo/peptide-starter-theme.git"
|
||||||
|
|
||||||
|
# If this is the first deploy, clone the repo
|
||||||
|
if [ ! -d "$THEME_DIR/.git" ]; then
|
||||||
|
echo "First deploy — cloning repo..."
|
||||||
|
rm -rf "$THEME_DIR"
|
||||||
|
git clone "$REPO_URL" "$THEME_DIR"
|
||||||
|
else
|
||||||
|
echo "Pulling latest changes..."
|
||||||
|
cd "$THEME_DIR"
|
||||||
|
git fetch origin main
|
||||||
|
git reset --hard origin/main
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Theme deployed successfully."
|
||||||
|
|
||||||
|
- name: Purge LiteSpeed Cache
|
||||||
|
uses: appleboy/ssh-action@v1
|
||||||
|
with:
|
||||||
|
host: ${{ secrets.SSH_HOST }}
|
||||||
|
username: ${{ secrets.SSH_USERNAME }}
|
||||||
|
key: ${{ secrets.SSH_PRIVATE_KEY }}
|
||||||
|
port: ${{ secrets.SSH_PORT }}
|
||||||
|
script: |
|
||||||
|
# Purge LiteSpeed cache so visitors see the update immediately
|
||||||
|
CACHE_DIR="/home/${{ secrets.SSH_USERNAME }}/domains/peptiderepo.com/public_html/wp-content/litespeed/cssjs"
|
||||||
|
if [ -d "$CACHE_DIR" ]; then
|
||||||
|
rm -rf "$CACHE_DIR"/*
|
||||||
|
echo "LiteSpeed CSS/JS cache purged."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Touch a file to signal LiteSpeed to rebuild
|
||||||
|
touch "/home/${{ secrets.SSH_USERNAME }}/domains/peptiderepo.com/public_html/.lscache_purge" 2>/dev/null || true
|
||||||
|
echo "Cache purge signal sent."
|
||||||
17
.gitignore
vendored
Normal file
17
.gitignore
vendored
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
# OS files
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
|
|
||||||
|
# Editor files
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
|
*~
|
||||||
|
.vscode/
|
||||||
|
.idea/
|
||||||
|
|
||||||
|
# Node (if ever used for build tooling)
|
||||||
|
node_modules/
|
||||||
|
package-lock.json
|
||||||
|
|
||||||
|
# Zip artifacts (built by CI)
|
||||||
|
*.zip
|
||||||
36
404.php
Normal file
36
404.php
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* The 404 template
|
||||||
|
*
|
||||||
|
* @package peptide-starter
|
||||||
|
*/
|
||||||
|
|
||||||
|
get_header();
|
||||||
|
?>
|
||||||
|
|
||||||
|
<main id="main" class="site-main">
|
||||||
|
<div class="ps-container">
|
||||||
|
<div class="error-404 not-found">
|
||||||
|
<h1><?php esc_html_e( '404 - Page Not Found', 'peptide-starter' ); ?></h1>
|
||||||
|
<p><?php esc_html_e( "Sorry, the page you're looking for doesn't exist. It might have been moved or deleted.", 'peptide-starter' ); ?></p>
|
||||||
|
|
||||||
|
<form role="search" method="get" class="search-form ps-404-search-form" action="<?php echo esc_url( home_url( '/' ) ); ?>">
|
||||||
|
<label>
|
||||||
|
<span class="screen-reader-text"><?php esc_html_e( 'Search for:', 'peptide-starter' ); ?></span>
|
||||||
|
<input type="search" class="search-submit" placeholder="<?php esc_attr_e( 'Search peptides...', 'peptide-starter' ); ?>" value="<?php echo get_search_query(); ?>" name="s" title="<?php esc_attr_e( 'Search for:', 'peptide-starter' ); ?>" />
|
||||||
|
</label>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<div class="ps-404-cta-group">
|
||||||
|
<a href="<?php echo esc_url( home_url( '/' ) ); ?>" class="ps-btn ps-btn-primary">
|
||||||
|
<?php esc_html_e( 'Back to Home', 'peptide-starter' ); ?>
|
||||||
|
</a>
|
||||||
|
<a href="<?php echo esc_url( home_url( '/peptides' ) ); ?>" class="ps-btn ps-btn-secondary">
|
||||||
|
<?php esc_html_e( 'Browse Peptides', 'peptide-starter' ); ?>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<?php get_footer(); ?>
|
||||||
267
README.md
Normal file
267
README.md
Normal file
@@ -0,0 +1,267 @@
|
|||||||
|
# Peptide Starter Theme
|
||||||
|
|
||||||
|
A premium, accessible WordPress theme for the Peptide Repo scientific peptide reference database.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- **Modern Design** - Clean, minimalist aesthetic inspired by Apple's human interface guidelines
|
||||||
|
- **Dark Mode Support** - Full dark mode via `prefers-color-scheme` and manual toggle
|
||||||
|
- **Accessibility** - WCAG AA compliant with proper focus indicators and semantic HTML
|
||||||
|
- **Plugin Harmony** - Respects plugin CSS namespaces (`.pn-*` for Peptide News plugin)
|
||||||
|
- **Responsive Design** - Mobile-first approach with proper breakpoints (480px, 768px, 1024px, 1440px)
|
||||||
|
- **Typography** - Inter font with 1.125x modular scale for excellent readability
|
||||||
|
- **Component Library** - Pre-built buttons, cards, forms, badges, alerts, and pagination
|
||||||
|
- **Customizer Support** - Theme options for hero title, subtitle, search placeholder, footer text
|
||||||
|
- **Performance** - Lightweight CSS architecture using custom properties instead of !important
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
1. Download `peptide-starter.zip`
|
||||||
|
2. Go to WordPress Admin → Appearance → Themes
|
||||||
|
3. Click "Add New" → "Upload Theme"
|
||||||
|
4. Select the zip file and click "Install Now"
|
||||||
|
5. Click "Activate" to use the theme
|
||||||
|
|
||||||
|
## Theme Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
peptide-starter/
|
||||||
|
├── style.css # Main stylesheet with all CSS
|
||||||
|
├── functions.php # Theme setup and functionality
|
||||||
|
├── header.php # Global header with navigation
|
||||||
|
├── footer.php # Global footer with widgets
|
||||||
|
├── index.php # Fallback template
|
||||||
|
├── front-page.php # Home page with hero + news feed
|
||||||
|
├── page.php # Generic page template
|
||||||
|
├── archive-peptide.php # Peptide archive/database view
|
||||||
|
├── single-peptide.php # Single peptide detail page
|
||||||
|
├── 404.php # 404 error page
|
||||||
|
├── searchform.php # Search form template
|
||||||
|
├── screenshot.png # Theme screenshot for WordPress
|
||||||
|
├── assets/
|
||||||
|
│ └── js/
|
||||||
|
│ ├── navigation.js # Mobile menu and nav functionality
|
||||||
|
│ └── theme.js # Dark mode toggle and utilities
|
||||||
|
├── template-parts/
|
||||||
|
│ ├── content.php # Post/page content template
|
||||||
|
│ └── content-none.php # Empty state template
|
||||||
|
└── languages/ # Translation files
|
||||||
|
```
|
||||||
|
|
||||||
|
## Design System
|
||||||
|
|
||||||
|
### Color Palette
|
||||||
|
|
||||||
|
**Light Mode:**
|
||||||
|
- Primary: `#0066CC` (Blue)
|
||||||
|
- Text Primary: `#111827` (Near Black)
|
||||||
|
- Text Secondary: `#6B7280` (Gray)
|
||||||
|
- Background: `#FFFFFF` (White)
|
||||||
|
|
||||||
|
**Dark Mode:**
|
||||||
|
- Primary: `#3B82F6` (Bright Blue)
|
||||||
|
- Text Primary: `#F9FAFB` (Near White)
|
||||||
|
- Text Secondary: `#D1D5DB` (Light Gray)
|
||||||
|
- Background: `#111827` (Near Black)
|
||||||
|
|
||||||
|
### Typography
|
||||||
|
|
||||||
|
All sizes use a **1.125x modular scale** with **8px grid base**:
|
||||||
|
|
||||||
|
- **H1**: 48px, 700 weight
|
||||||
|
- **H2**: 40px, 700 weight
|
||||||
|
- **H3**: 32px, 600 weight
|
||||||
|
- **H4**: 24px, 600 weight
|
||||||
|
- **H5**: 20px, 700 weight (fixed from 18px per Design Director)
|
||||||
|
- **H6**: 16px, 500 weight
|
||||||
|
- **Body**: 14px, 400 weight (fixed from 16px per Design Director)
|
||||||
|
|
||||||
|
Font family: Inter (with system font fallback)
|
||||||
|
|
||||||
|
### Spacing
|
||||||
|
|
||||||
|
8px base unit grid:
|
||||||
|
- `--spacing-xs`: 4px
|
||||||
|
- `--spacing-sm`: 8px
|
||||||
|
- `--spacing-md`: 16px
|
||||||
|
- `--spacing-lg`: 24px
|
||||||
|
- `--spacing-xl`: 32px
|
||||||
|
- `--spacing-2xl`: 48px
|
||||||
|
- `--spacing-3xl`: 64px
|
||||||
|
- `--spacing-4xl`: 80px
|
||||||
|
- `--spacing-5xl`: 96px
|
||||||
|
|
||||||
|
### Components
|
||||||
|
|
||||||
|
**Buttons:**
|
||||||
|
- `.ps-btn` - Base button
|
||||||
|
- `.ps-btn-primary` - Primary action (blue)
|
||||||
|
- `.ps-btn-secondary` - Secondary action (outlined)
|
||||||
|
- `.ps-btn-tertiary` - Tertiary action (transparent)
|
||||||
|
- `.ps-btn-sm`, `.ps-btn-lg`, `.ps-btn-full` - Size variants
|
||||||
|
|
||||||
|
**Cards:**
|
||||||
|
- `.ps-card` - Standard card with border and hover effect
|
||||||
|
|
||||||
|
**Forms:**
|
||||||
|
- Proper focus states with 15% opacity focus ring
|
||||||
|
- Error states with red border and light red background
|
||||||
|
- Support for text, email, password, search, number, url inputs
|
||||||
|
|
||||||
|
**Alerts:**
|
||||||
|
- `.ps-alert-info` - Blue alert
|
||||||
|
- `.ps-alert-success` - Green alert
|
||||||
|
- `.ps-alert-warning` - Amber alert
|
||||||
|
- `.ps-alert-error` - Red alert
|
||||||
|
|
||||||
|
**Badges & Tags:**
|
||||||
|
- `.ps-badge` - Static badge
|
||||||
|
- `.ps-tag` - Dismissible tag
|
||||||
|
|
||||||
|
## Dark Mode
|
||||||
|
|
||||||
|
### How it Works
|
||||||
|
|
||||||
|
The theme detects dark mode in this order:
|
||||||
|
1. User's explicit choice via toggle (stored in `localStorage`)
|
||||||
|
2. System preference via `prefers-color-scheme: dark`
|
||||||
|
3. Default to light mode
|
||||||
|
|
||||||
|
The theme applies dark colors via:
|
||||||
|
- `@media (prefers-color-scheme: dark)` - System preference
|
||||||
|
- `html[data-theme="dark"]` - User override (takes precedence)
|
||||||
|
|
||||||
|
### Plugin Integration
|
||||||
|
|
||||||
|
The theme broadcasts dark mode changes to plugins via:
|
||||||
|
|
||||||
|
1. **Custom Event**: `themechange` event on `document`
|
||||||
|
```javascript
|
||||||
|
document.addEventListener('themechange', (e) => {
|
||||||
|
console.log('Theme is now:', e.detail.theme);
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Data Attribute**: `data-theme` on `<html>`
|
||||||
|
```javascript
|
||||||
|
const theme = document.documentElement.getAttribute('data-theme');
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **CSS Custom Properties**: All colors controlled via CSS variables
|
||||||
|
|
||||||
|
## Plugin Compatibility
|
||||||
|
|
||||||
|
### Peptide News Plugin
|
||||||
|
The theme provides optimal styling for the Peptide News plugin:
|
||||||
|
- News feed grid adapts to screen size (1-4 columns)
|
||||||
|
- Plugin `.pn-*` classes are untouched by theme
|
||||||
|
- Plugin handles its own card styling
|
||||||
|
- Dark mode colors available via CSS custom properties
|
||||||
|
|
||||||
|
### WooCommerce
|
||||||
|
- Cart icon in header (if WooCommerce is active)
|
||||||
|
- Lightweight integration without full theme styling
|
||||||
|
|
||||||
|
## Customizer Options
|
||||||
|
|
||||||
|
Go to **Appearance → Customize** to modify:
|
||||||
|
- **Hero Title** - Main headline on home page
|
||||||
|
- **Hero Subtitle** - Subheading under title
|
||||||
|
- **Search Placeholder** - Text in search input
|
||||||
|
- **Footer Copyright** - Copyright/attribution text
|
||||||
|
- **Dark Mode Default** - Enable dark mode on first visit
|
||||||
|
|
||||||
|
## Accessibility
|
||||||
|
|
||||||
|
The theme meets **WCAG AA standards**:
|
||||||
|
- All color combinations have 4.5:1+ contrast ratio
|
||||||
|
- Proper heading hierarchy (H1-H6)
|
||||||
|
- Focus indicators on all interactive elements (2px outline)
|
||||||
|
- Keyboard navigation support
|
||||||
|
- Skip-to-main-content link
|
||||||
|
- Semantic HTML with proper landmarks
|
||||||
|
- Form labels properly associated with inputs
|
||||||
|
- Alt text for images
|
||||||
|
|
||||||
|
## Responsive Breakpoints
|
||||||
|
|
||||||
|
- **Mobile**: 320px - 479px
|
||||||
|
- **Tablet**: 480px - 767px
|
||||||
|
- **Desktop**: 768px - 1023px
|
||||||
|
- **Large**: 1024px - 1439px
|
||||||
|
- **Extra Large**: 1440px+
|
||||||
|
|
||||||
|
The theme uses **mobile-first CSS** - styles start mobile and add complexity at larger breakpoints.
|
||||||
|
|
||||||
|
## Performance
|
||||||
|
|
||||||
|
- **No `!important` on global elements** - Prevents plugin conflicts
|
||||||
|
- **CSS Custom Properties** - Single source of truth for design tokens
|
||||||
|
- **Minimal JavaScript** - Only essential interactivity
|
||||||
|
- **Font loading** - Inter from Google Fonts with `display=swap`
|
||||||
|
- **Proper scoping** - Theme classes prefixed with `.ps-` to avoid conflicts
|
||||||
|
|
||||||
|
## Development
|
||||||
|
|
||||||
|
### Adding Custom Styles
|
||||||
|
|
||||||
|
Modify `style.css` sections:
|
||||||
|
1. Components - Add new component classes
|
||||||
|
2. Pages - Add page-specific styles
|
||||||
|
3. Responsive - Add mobile/tablet/desktop rules
|
||||||
|
|
||||||
|
### CSS Architecture
|
||||||
|
|
||||||
|
The stylesheet is organized in this order:
|
||||||
|
1. Reset
|
||||||
|
2. CSS Custom Properties (variables)
|
||||||
|
3. Typography
|
||||||
|
4. Global Styles
|
||||||
|
5. Components
|
||||||
|
6. Regions (header, footer)
|
||||||
|
7. Pages
|
||||||
|
8. Animations
|
||||||
|
9. Accessibility
|
||||||
|
10. Dark Mode
|
||||||
|
11. Responsive
|
||||||
|
|
||||||
|
### Adding New Templates
|
||||||
|
|
||||||
|
Create new template files following WordPress standards:
|
||||||
|
- `page-{slug}.php` - Specific page templates
|
||||||
|
- `archive-{post-type}.php` - Archive templates
|
||||||
|
- `single-{post-type}.php` - Single post templates
|
||||||
|
- `template-parts/{name}.php` - Reusable template parts
|
||||||
|
|
||||||
|
## Browser Support
|
||||||
|
|
||||||
|
- Chrome/Edge (latest 2 versions)
|
||||||
|
- Firefox (latest 2 versions)
|
||||||
|
- Safari (latest 2 versions)
|
||||||
|
- iOS Safari 12+
|
||||||
|
- Chrome Android
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
GPL v2 or later - See LICENSE file
|
||||||
|
|
||||||
|
## Credits
|
||||||
|
|
||||||
|
Designed with principles from:
|
||||||
|
- Apple Human Interface Guidelines (2025)
|
||||||
|
- Modern Scientific Databases (PubMed, Nature)
|
||||||
|
- Premium SaaS Design (Linear, Stripe, Vercel)
|
||||||
|
- WordPress Best Practices
|
||||||
|
|
||||||
|
## Support
|
||||||
|
|
||||||
|
For issues and feature requests, visit: https://github.com/peptiderepo/peptide-starter
|
||||||
|
|
||||||
|
## Changelog
|
||||||
|
|
||||||
|
### Version 1.0.0 (Initial Release)
|
||||||
|
- Initial theme launch
|
||||||
|
- All core features implemented
|
||||||
|
- Full dark mode support
|
||||||
|
- Plugin harmony architecture
|
||||||
|
- Accessibility compliance (WCAG AA)
|
||||||
73
archive-peptide.php
Normal file
73
archive-peptide.php
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* The template for displaying peptide archives
|
||||||
|
*
|
||||||
|
* @package peptide-starter
|
||||||
|
*/
|
||||||
|
|
||||||
|
get_header();
|
||||||
|
?>
|
||||||
|
|
||||||
|
<main id="main" class="site-main">
|
||||||
|
<!-- Archive Header -->
|
||||||
|
<header class="archive-header">
|
||||||
|
<div class="ps-container">
|
||||||
|
<h1 class="archive-title">
|
||||||
|
<?php
|
||||||
|
if ( is_tax( 'peptide-category' ) ) {
|
||||||
|
esc_html_e( 'Peptide Category', 'peptide-starter' );
|
||||||
|
echo ': ' . esc_html( single_term_title( '', false ) );
|
||||||
|
} elseif ( is_post_type_archive( 'peptide' ) ) {
|
||||||
|
esc_html_e( 'Peptide Database', 'peptide-starter' );
|
||||||
|
} else {
|
||||||
|
esc_html_e( 'Peptides', 'peptide-starter' );
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
</h1>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<!-- Archive Content -->
|
||||||
|
<div class="archive-content">
|
||||||
|
<!-- Search/Filter Bar -->
|
||||||
|
<?php if ( class_exists( 'WP_Widget_Search' ) ) { ?>
|
||||||
|
<div class="archive-filters">
|
||||||
|
<div class="archive-search">
|
||||||
|
<?php get_search_form(); ?>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<?php } ?>
|
||||||
|
|
||||||
|
<!-- Peptides Grid -->
|
||||||
|
<?php
|
||||||
|
if ( have_posts() ) {
|
||||||
|
?>
|
||||||
|
<div class="peptides-grid">
|
||||||
|
<?php
|
||||||
|
while ( have_posts() ) {
|
||||||
|
the_post();
|
||||||
|
get_template_part( 'template-parts/content', 'peptide' );
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Pagination -->
|
||||||
|
<?php
|
||||||
|
peptide_starter_pagination();
|
||||||
|
} else {
|
||||||
|
// No peptides found
|
||||||
|
?>
|
||||||
|
<div class="archive-empty">
|
||||||
|
<h2><?php esc_html_e( 'No Peptides Found', 'peptide-starter' ); ?></h2>
|
||||||
|
<p><?php esc_html_e( 'There are no peptides matching your search. Try a different search term or browse the full database.', 'peptide-starter' ); ?></p>
|
||||||
|
<a href="<?php echo esc_url( home_url( '/peptides' ) ); ?>" class="ps-btn ps-btn-primary">
|
||||||
|
<?php esc_html_e( 'View All Peptides', 'peptide-starter' ); ?>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<?php
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<?php get_footer();
|
||||||
84
assets/js/navigation.js
Normal file
84
assets/js/navigation.js
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
/**
|
||||||
|
* Navigation and Mobile Menu Toggle
|
||||||
|
* Peptide Starter Theme
|
||||||
|
*
|
||||||
|
* Handles:
|
||||||
|
* - Mobile hamburger menu toggle
|
||||||
|
* - Navigation overlay click handling
|
||||||
|
* - Menu state persistence
|
||||||
|
*/
|
||||||
|
|
||||||
|
(function() {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const menuToggle = document.querySelector('.menu-toggle');
|
||||||
|
const primaryNav = document.querySelector('.primary-navigation');
|
||||||
|
const navOverlay = document.querySelector('.nav-overlay');
|
||||||
|
|
||||||
|
if (!menuToggle || !primaryNav || !navOverlay) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Toggle mobile menu visibility
|
||||||
|
*/
|
||||||
|
function toggleMenu() {
|
||||||
|
const isActive = primaryNav.classList.contains('active');
|
||||||
|
|
||||||
|
if (isActive) {
|
||||||
|
closeMenu();
|
||||||
|
} else {
|
||||||
|
openMenu();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Open mobile menu
|
||||||
|
*/
|
||||||
|
function openMenu() {
|
||||||
|
primaryNav.classList.add('active');
|
||||||
|
navOverlay.classList.add('active');
|
||||||
|
document.body.style.overflow = 'hidden';
|
||||||
|
menuToggle.setAttribute('aria-expanded', 'true');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Close mobile menu
|
||||||
|
*/
|
||||||
|
function closeMenu() {
|
||||||
|
primaryNav.classList.remove('active');
|
||||||
|
navOverlay.classList.remove('active');
|
||||||
|
document.body.style.overflow = '';
|
||||||
|
menuToggle.setAttribute('aria-expanded', 'false');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Menu toggle button click
|
||||||
|
menuToggle.addEventListener('click', toggleMenu);
|
||||||
|
|
||||||
|
// Overlay click
|
||||||
|
navOverlay.addEventListener('click', closeMenu);
|
||||||
|
|
||||||
|
// Close menu when a nav link is clicked
|
||||||
|
const navLinks = primaryNav.querySelectorAll('a');
|
||||||
|
navLinks.forEach(link => {
|
||||||
|
link.addEventListener('click', closeMenu);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Close menu on Escape key
|
||||||
|
document.addEventListener('keydown', (e) => {
|
||||||
|
if (e.key === 'Escape') {
|
||||||
|
closeMenu();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Set initial aria-expanded state
|
||||||
|
menuToggle.setAttribute('aria-expanded', 'false');
|
||||||
|
|
||||||
|
// Close menu when window is resized above mobile breakpoint
|
||||||
|
const mediaQuery = window.matchMedia('(min-width: 768px)');
|
||||||
|
mediaQuery.addEventListener('change', (e) => {
|
||||||
|
if (e.matches) {
|
||||||
|
closeMenu();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})();
|
||||||
210
assets/js/theme.js
Normal file
210
assets/js/theme.js
Normal file
@@ -0,0 +1,210 @@
|
|||||||
|
/**
|
||||||
|
* Theme Utilities
|
||||||
|
* Peptide Starter Theme
|
||||||
|
*
|
||||||
|
* Handles:
|
||||||
|
* - Dark mode toggle functionality
|
||||||
|
* - Dark mode preference persistence
|
||||||
|
* - Plugin dark mode coordination
|
||||||
|
* - Smooth transitions
|
||||||
|
*/
|
||||||
|
|
||||||
|
(function() {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const html = document.documentElement;
|
||||||
|
const darkModeToggle = document.querySelector('.dark-mode-toggle');
|
||||||
|
const sunIcon = document.querySelector('.sun-icon');
|
||||||
|
const moonIcon = document.querySelector('.moon-icon');
|
||||||
|
|
||||||
|
if (!darkModeToggle) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get current theme preference
|
||||||
|
* @returns {string} 'dark' or 'light'
|
||||||
|
*/
|
||||||
|
function getCurrentTheme() {
|
||||||
|
const stored = localStorage.getItem('peptide-starter-theme');
|
||||||
|
if (stored) {
|
||||||
|
return stored;
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentAttr = html.getAttribute('data-theme');
|
||||||
|
if (currentAttr) {
|
||||||
|
return currentAttr;
|
||||||
|
}
|
||||||
|
|
||||||
|
const systemDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
||||||
|
return systemDark ? 'dark' : 'light';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set theme preference
|
||||||
|
* @param {string} theme - 'dark' or 'light'
|
||||||
|
*/
|
||||||
|
function setTheme(theme) {
|
||||||
|
html.setAttribute('data-theme', theme);
|
||||||
|
localStorage.setItem('peptide-starter-theme', theme);
|
||||||
|
|
||||||
|
// Update icon visibility
|
||||||
|
updateIcons(theme);
|
||||||
|
|
||||||
|
// Notify plugins about theme change via custom event
|
||||||
|
const event = new CustomEvent('themechange', {
|
||||||
|
detail: { theme: theme }
|
||||||
|
});
|
||||||
|
document.dispatchEvent(event);
|
||||||
|
|
||||||
|
// Also update body class for CSS hooks
|
||||||
|
document.body.classList.remove('dark-mode', 'light-mode');
|
||||||
|
document.body.classList.add(theme === 'dark' ? 'dark-mode' : 'light-mode');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update icon visibility based on theme
|
||||||
|
* @param {string} theme - 'dark' or 'light'
|
||||||
|
*/
|
||||||
|
function updateIcons(theme) {
|
||||||
|
if (theme === 'dark') {
|
||||||
|
sunIcon.style.display = 'block';
|
||||||
|
moonIcon.style.display = 'none';
|
||||||
|
} else {
|
||||||
|
sunIcon.style.display = 'none';
|
||||||
|
moonIcon.style.display = 'block';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Toggle between dark and light mode
|
||||||
|
*/
|
||||||
|
function toggleDarkMode() {
|
||||||
|
const currentTheme = getCurrentTheme();
|
||||||
|
const newTheme = currentTheme === 'dark' ? 'light' : 'dark';
|
||||||
|
setTheme(newTheme);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize theme on page load
|
||||||
|
const initialTheme = getCurrentTheme();
|
||||||
|
setTheme(initialTheme);
|
||||||
|
|
||||||
|
// Dark mode toggle button click
|
||||||
|
darkModeToggle.addEventListener('click', toggleDarkMode);
|
||||||
|
|
||||||
|
// Listen for system preference changes
|
||||||
|
const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
|
||||||
|
mediaQuery.addEventListener('change', (e) => {
|
||||||
|
const stored = localStorage.getItem('peptide-starter-theme');
|
||||||
|
if (!stored) {
|
||||||
|
setTheme(e.matches ? 'dark' : 'light');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Expose API for plugins to read/listen to theme changes
|
||||||
|
window.peptideStarterTheme = {
|
||||||
|
getCurrentTheme: getCurrentTheme,
|
||||||
|
setTheme: setTheme,
|
||||||
|
toggleDarkMode: toggleDarkMode,
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Plugin Integration Example:
|
||||||
|
* Plugins can listen to theme changes via:
|
||||||
|
*
|
||||||
|
* 1. Custom event listener:
|
||||||
|
* document.addEventListener('themechange', function(e) {
|
||||||
|
* console.log('Theme changed to:', e.detail.theme);
|
||||||
|
* });
|
||||||
|
*
|
||||||
|
* 2. MutationObserver on data-theme:
|
||||||
|
* const observer = new MutationObserver((mutations) => {
|
||||||
|
* const theme = html.getAttribute('data-theme');
|
||||||
|
* console.log('Theme is now:', theme);
|
||||||
|
* });
|
||||||
|
* observer.observe(html, { attributes: true });
|
||||||
|
*
|
||||||
|
* 3. Direct access to CSS custom properties which theme controls
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Smooth scroll behavior
|
||||||
|
*/
|
||||||
|
function initSmoothScroll() {
|
||||||
|
const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
|
||||||
|
if (!prefersReducedMotion) {
|
||||||
|
document.documentElement.style.scrollBehavior = 'smooth';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
initSmoothScroll();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add focus-visible polyfill for older browsers
|
||||||
|
*/
|
||||||
|
function initFocusVisible() {
|
||||||
|
const isKeyboardEvent = (e) => {
|
||||||
|
return e.mozInputSource === MOZ_SOURCE_KEYBOARD ||
|
||||||
|
e.detail === 0 ||
|
||||||
|
(e.sourceCapabilities && e.sourceCapabilities.firesTouchEvents === false);
|
||||||
|
};
|
||||||
|
|
||||||
|
document.addEventListener('mousedown', (e) => {
|
||||||
|
document.body.classList.remove('keyboard-focus');
|
||||||
|
}, true);
|
||||||
|
|
||||||
|
document.addEventListener('keydown', () => {
|
||||||
|
document.body.classList.add('keyboard-focus');
|
||||||
|
}, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Search overlay functionality (Peptide Search AI integration)
|
||||||
|
const searchToggle = document.querySelector('.search-toggle');
|
||||||
|
const searchOverlay = document.getElementById('search-overlay');
|
||||||
|
const searchOverlayClose = document.querySelector('.search-overlay-close');
|
||||||
|
|
||||||
|
function openSearchOverlay() {
|
||||||
|
if (!searchOverlay) return;
|
||||||
|
searchOverlay.classList.add('active');
|
||||||
|
searchOverlay.setAttribute('aria-hidden', 'false');
|
||||||
|
document.body.style.overflow = 'hidden';
|
||||||
|
// Focus the search input inside the overlay
|
||||||
|
const input = searchOverlay.querySelector('.psa-search-input, .ps-search-input, input[type="search"], input[type="text"]');
|
||||||
|
if (input) {
|
||||||
|
setTimeout(() => input.focus(), 100);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeSearchOverlay() {
|
||||||
|
if (!searchOverlay) return;
|
||||||
|
searchOverlay.classList.remove('active');
|
||||||
|
searchOverlay.setAttribute('aria-hidden', 'true');
|
||||||
|
document.body.style.overflow = '';
|
||||||
|
// Return focus to search toggle
|
||||||
|
if (searchToggle) searchToggle.focus();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (searchToggle) {
|
||||||
|
searchToggle.addEventListener('click', openSearchOverlay);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (searchOverlayClose) {
|
||||||
|
searchOverlayClose.addEventListener('click', closeSearchOverlay);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (searchOverlay) {
|
||||||
|
// Close overlay when clicking the backdrop
|
||||||
|
searchOverlay.addEventListener('click', (e) => {
|
||||||
|
if (e.target === searchOverlay) {
|
||||||
|
closeSearchOverlay();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Close on Escape key
|
||||||
|
document.addEventListener('keydown', (e) => {
|
||||||
|
if (e.key === 'Escape' && searchOverlay.classList.contains('active')) {
|
||||||
|
closeSearchOverlay();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})();
|
||||||
80
footer.php
Normal file
80
footer.php
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* The footer for Peptide Starter theme
|
||||||
|
*
|
||||||
|
* @package peptide-starter
|
||||||
|
*/
|
||||||
|
|
||||||
|
?>
|
||||||
|
</main><!-- #main -->
|
||||||
|
|
||||||
|
<footer class="site-footer">
|
||||||
|
<div class="footer-wrapper">
|
||||||
|
<!-- Newsletter Signup -->
|
||||||
|
<?php if ( peptide_starter_show_newsletter_form() ) { ?>
|
||||||
|
<div class="footer-newsletter">
|
||||||
|
<h3><?php esc_html_e( 'Stay Updated', 'peptide-starter' ); ?></h3>
|
||||||
|
<p><?php esc_html_e( 'Subscribe to our newsletter for the latest peptide research and updates.', 'peptide-starter' ); ?></p>
|
||||||
|
<form class="newsletter-form" action="#" method="post">
|
||||||
|
<input
|
||||||
|
type="email"
|
||||||
|
name="email"
|
||||||
|
class="newsletter-input"
|
||||||
|
placeholder="<?php esc_attr_e( 'your@email.com', 'peptide-starter' ); ?>"
|
||||||
|
required
|
||||||
|
>
|
||||||
|
<button type="submit" class="ps-btn ps-btn-primary">
|
||||||
|
<?php esc_html_e( 'Subscribe', 'peptide-starter' ); ?>
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<?php } ?>
|
||||||
|
|
||||||
|
<!-- Footer Grid -->
|
||||||
|
<div class="footer-grid">
|
||||||
|
<!-- Footer Widget Areas -->
|
||||||
|
<?php
|
||||||
|
for ( $i = 1; $i <= 4; $i++ ) {
|
||||||
|
echo '<div class="footer-section">';
|
||||||
|
if ( is_active_sidebar( 'footer-' . $i ) ) {
|
||||||
|
dynamic_sidebar( 'footer-' . $i );
|
||||||
|
} else {
|
||||||
|
// Default footer content if no widgets
|
||||||
|
if ( 1 === $i ) {
|
||||||
|
echo '<h3>' . esc_html__( 'About', 'peptide-starter' ) . '</h3>';
|
||||||
|
echo '<p>' . esc_html__( 'Peptide Repo is a comprehensive scientific database for peptide research.', 'peptide-starter' ) . '</p>';
|
||||||
|
} elseif ( 2 === $i ) {
|
||||||
|
echo '<h3>' . esc_html__( 'Quick Links', 'peptide-starter' ) . '</h3>';
|
||||||
|
echo '<ul>';
|
||||||
|
echo '<li><a href="' . esc_url( home_url( '/' ) ) . '">' . esc_html__( 'Home', 'peptide-starter' ) . '</a></li>';
|
||||||
|
echo '<li><a href="' . esc_url( home_url( '/peptides' ) ) . '">' . esc_html__( 'Peptides Database', 'peptide-starter' ) . '</a></li>';
|
||||||
|
echo '</ul>';
|
||||||
|
} elseif ( 3 === $i ) {
|
||||||
|
echo '<h3>' . esc_html__( 'Resources', 'peptide-starter' ) . '</h3>';
|
||||||
|
echo '<ul>';
|
||||||
|
echo '<li><a href="#">' . esc_html__( 'Research', 'peptide-starter' ) . '</a></li>';
|
||||||
|
echo '<li><a href="#">' . esc_html__( 'Documentation', 'peptide-starter' ) . '</a></li>';
|
||||||
|
echo '</ul>';
|
||||||
|
} elseif ( 4 === $i ) {
|
||||||
|
echo '<h3>' . esc_html__( 'Connect', 'peptide-starter' ) . '</h3>';
|
||||||
|
echo '<ul>';
|
||||||
|
echo '<li><a href="https://github.com/peptiderepo" target="_blank" rel="noopener">' . esc_html__( 'GitHub', 'peptide-starter' ) . '</a></li>';
|
||||||
|
echo '<li><a href="#">' . esc_html__( 'Twitter', 'peptide-starter' ) . '</a></li>';
|
||||||
|
echo '</ul>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
echo '</div>';
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Footer Bottom -->
|
||||||
|
<div class="footer-bottom">
|
||||||
|
<p><?php echo wp_kses_post( peptide_starter_get_footer_copyright() ); ?></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
|
||||||
|
<?php wp_footer(); ?>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
78
front-page.php
Normal file
78
front-page.php
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* The front page template
|
||||||
|
*
|
||||||
|
* @package peptide-starter
|
||||||
|
*/
|
||||||
|
|
||||||
|
get_header();
|
||||||
|
?>
|
||||||
|
|
||||||
|
<main id="main" class="site-main">
|
||||||
|
<!-- Hero Section -->
|
||||||
|
<section class="hero-section">
|
||||||
|
<div class="hero-content">
|
||||||
|
<h1 class="hero-title"><?php echo esc_html( peptide_starter_get_hero_title() ); ?></h1>
|
||||||
|
<p class="hero-subtitle"><?php echo esc_html( peptide_starter_get_hero_subtitle() ); ?></p>
|
||||||
|
|
||||||
|
<!-- Hero Search (Peptide Search AI) -->
|
||||||
|
<div class="hero-search">
|
||||||
|
<?php
|
||||||
|
if ( shortcode_exists( 'peptide_search' ) ) {
|
||||||
|
echo do_shortcode( '[peptide_search placeholder="' . esc_attr( peptide_starter_get_search_placeholder() ) . '"]' );
|
||||||
|
} else {
|
||||||
|
?>
|
||||||
|
<form class="ps-search-form" method="get" action="<?php echo esc_url( home_url( '/' ) ); ?>">
|
||||||
|
<input
|
||||||
|
type="search"
|
||||||
|
name="s"
|
||||||
|
class="ps-search-input"
|
||||||
|
placeholder="<?php echo esc_attr( peptide_starter_get_search_placeholder() ); ?>"
|
||||||
|
value="<?php echo get_search_query(); ?>"
|
||||||
|
>
|
||||||
|
</form>
|
||||||
|
<?php
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Hero CTAs -->
|
||||||
|
<div class="hero-ctas">
|
||||||
|
<a href="<?php echo esc_url( home_url( '/peptides' ) ); ?>" class="ps-btn ps-btn-primary">
|
||||||
|
<?php esc_html_e( 'Browse Peptides', 'peptide-starter' ); ?>
|
||||||
|
</a>
|
||||||
|
<a href="<?php echo esc_url( home_url( '/about' ) ); ?>" class="ps-btn ps-btn-secondary">
|
||||||
|
<?php esc_html_e( 'Learn More', 'peptide-starter' ); ?>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- News Feed Section -->
|
||||||
|
<?php
|
||||||
|
// Display the Peptide News plugin shortcode if available
|
||||||
|
if ( shortcode_exists( 'peptide_news' ) ) {
|
||||||
|
?>
|
||||||
|
<section class="news-feed-section">
|
||||||
|
<div class="ps-container">
|
||||||
|
<div class="news-feed-header">
|
||||||
|
<h2 class="news-feed-title"><?php esc_html_e( 'Latest Peptide Research', 'peptide-starter' ); ?></h2>
|
||||||
|
<p class="news-feed-description">
|
||||||
|
<?php esc_html_e( 'Stay updated with the latest developments in peptide science and research.', 'peptide-starter' ); ?>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<?php echo do_shortcode( '[peptide_news]' ); ?>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
<?php
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
|
||||||
|
<?php
|
||||||
|
// Note: The front page layout is handled entirely by this template.
|
||||||
|
// Old page content (e.g. from Elementor) is intentionally NOT rendered here
|
||||||
|
// to avoid duplicate hero sections and empty space.
|
||||||
|
?>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<?php get_footer(); ?>
|
||||||
429
functions.php
Normal file
429
functions.php
Normal file
@@ -0,0 +1,429 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Peptide Starter Theme Functions
|
||||||
|
*
|
||||||
|
* @package peptide-starter
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Exit if accessed directly
|
||||||
|
if ( ! defined( 'ABSPATH' ) ) {
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Define constants
|
||||||
|
define( 'PEPTIDE_STARTER_VERSION', '1.3.1' );
|
||||||
|
define( 'PEPTIDE_STARTER_DIR', get_template_directory() );
|
||||||
|
define( 'PEPTIDE_STARTER_URI', get_template_directory_uri() );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set up theme defaults and register support for various WordPress features
|
||||||
|
*/
|
||||||
|
function peptide_starter_setup() {
|
||||||
|
// Add theme support
|
||||||
|
add_theme_support( 'title-tag' );
|
||||||
|
add_theme_support( 'post-thumbnails' );
|
||||||
|
add_theme_support( 'responsive-embeds' );
|
||||||
|
add_theme_support( 'custom-logo' );
|
||||||
|
add_theme_support( 'html5', array(
|
||||||
|
'search-form',
|
||||||
|
'comment-form',
|
||||||
|
'comment-list',
|
||||||
|
'gallery',
|
||||||
|
'caption',
|
||||||
|
'style',
|
||||||
|
'script',
|
||||||
|
) );
|
||||||
|
|
||||||
|
// Register navigation menus
|
||||||
|
register_nav_menus( array(
|
||||||
|
'primary' => esc_html__( 'Primary Menu', 'peptide-starter' ),
|
||||||
|
'footer' => esc_html__( 'Footer Menu', 'peptide-starter' ),
|
||||||
|
) );
|
||||||
|
|
||||||
|
// Add support for wide blocks
|
||||||
|
add_theme_support( 'align-wide' );
|
||||||
|
|
||||||
|
// Load text domain
|
||||||
|
load_theme_textdomain( 'peptide-starter', PEPTIDE_STARTER_DIR . '/languages' );
|
||||||
|
}
|
||||||
|
add_action( 'after_setup_theme', 'peptide_starter_setup' );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register and enqueue stylesheets and scripts
|
||||||
|
*/
|
||||||
|
function peptide_starter_scripts() {
|
||||||
|
// Register and enqueue main stylesheet
|
||||||
|
wp_enqueue_style( 'peptide-starter-style', PEPTIDE_STARTER_URI . '/style.css', array(), PEPTIDE_STARTER_VERSION );
|
||||||
|
|
||||||
|
// Add conditional scripts
|
||||||
|
wp_enqueue_script( 'peptide-starter-navigation', PEPTIDE_STARTER_URI . '/assets/js/navigation.js', array(), PEPTIDE_STARTER_VERSION, true );
|
||||||
|
wp_enqueue_script( 'peptide-starter-theme', PEPTIDE_STARTER_URI . '/assets/js/theme.js', array(), PEPTIDE_STARTER_VERSION, true );
|
||||||
|
|
||||||
|
// Pass PHP data to JavaScript
|
||||||
|
wp_localize_script( 'peptide-starter-theme', 'peptideStarterData', array(
|
||||||
|
'siteUrl' => home_url( '/' ),
|
||||||
|
'isDarkMode' => get_theme_mod( 'dark_mode_default', false ),
|
||||||
|
) );
|
||||||
|
|
||||||
|
// Dequeue comment reply script if comments are not open
|
||||||
|
if ( is_singular() && comments_open() && get_option( 'thread_comments' ) ) {
|
||||||
|
wp_enqueue_script( 'comment-reply' );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
add_action( 'wp_enqueue_scripts', 'peptide_starter_scripts' );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enqueue plugin override styles at a late priority so they load AFTER plugin stylesheets.
|
||||||
|
* This ensures theme overrides (e.g. removing line-clamp on article excerpts) beat plugin CSS.
|
||||||
|
*/
|
||||||
|
function peptide_starter_plugin_overrides() {
|
||||||
|
wp_add_inline_style( 'peptide-starter-style', '
|
||||||
|
/* Override Peptide News plugin line-clamp — show full summaries */
|
||||||
|
p.pn-article-excerpt,
|
||||||
|
.pn-article-card p.pn-article-excerpt,
|
||||||
|
body .pn-article-excerpt {
|
||||||
|
-webkit-line-clamp: unset !important;
|
||||||
|
-webkit-box-orient: unset !important;
|
||||||
|
display: block !important;
|
||||||
|
overflow: visible !important;
|
||||||
|
}
|
||||||
|
' );
|
||||||
|
}
|
||||||
|
add_action( 'wp_enqueue_scripts', 'peptide_starter_plugin_overrides', 99 );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register widget areas
|
||||||
|
*/
|
||||||
|
function peptide_starter_widgets_init() {
|
||||||
|
register_sidebar( array(
|
||||||
|
'name' => esc_html__( 'Footer - Column 1', 'peptide-starter' ),
|
||||||
|
'id' => 'footer-1',
|
||||||
|
'description' => esc_html__( 'Footer widget area 1', 'peptide-starter' ),
|
||||||
|
'before_widget' => '<div id="%1$s" class="widget %2$s footer-widget">',
|
||||||
|
'after_widget' => '</div>',
|
||||||
|
'before_title' => '<h3 class="widget-title">',
|
||||||
|
'after_title' => '</h3>',
|
||||||
|
) );
|
||||||
|
|
||||||
|
register_sidebar( array(
|
||||||
|
'name' => esc_html__( 'Footer - Column 2', 'peptide-starter' ),
|
||||||
|
'id' => 'footer-2',
|
||||||
|
'description' => esc_html__( 'Footer widget area 2', 'peptide-starter' ),
|
||||||
|
'before_widget' => '<div id="%1$s" class="widget %2$s footer-widget">',
|
||||||
|
'after_widget' => '</div>',
|
||||||
|
'before_title' => '<h3 class="widget-title">',
|
||||||
|
'after_title' => '</h3>',
|
||||||
|
) );
|
||||||
|
|
||||||
|
register_sidebar( array(
|
||||||
|
'name' => esc_html__( 'Footer - Column 3', 'peptide-starter' ),
|
||||||
|
'id' => 'footer-3',
|
||||||
|
'description' => esc_html__( 'Footer widget area 3', 'peptide-starter' ),
|
||||||
|
'before_widget' => '<div id="%1$s" class="widget %2$s footer-widget">',
|
||||||
|
'after_widget' => '</div>',
|
||||||
|
'before_title' => '<h3 class="widget-title">',
|
||||||
|
'after_title' => '</h3>',
|
||||||
|
) );
|
||||||
|
|
||||||
|
register_sidebar( array(
|
||||||
|
'name' => esc_html__( 'Footer - Column 4', 'peptide-starter' ),
|
||||||
|
'id' => 'footer-4',
|
||||||
|
'description' => esc_html__( 'Footer widget area 4', 'peptide-starter' ),
|
||||||
|
'before_widget' => '<div id="%1$s" class="widget %2$s footer-widget">',
|
||||||
|
'after_widget' => '</div>',
|
||||||
|
'before_title' => '<h3 class="widget-title">',
|
||||||
|
'after_title' => '</h3>',
|
||||||
|
) );
|
||||||
|
}
|
||||||
|
add_action( 'widgets_init', 'peptide_starter_widgets_init' );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register theme customizer options
|
||||||
|
*/
|
||||||
|
function peptide_starter_customize_register( $wp_customize ) {
|
||||||
|
// Logo section
|
||||||
|
$wp_customize->add_section( 'peptide_starter_branding', array(
|
||||||
|
'title' => esc_html__( 'Branding', 'peptide-starter' ),
|
||||||
|
'priority' => 30,
|
||||||
|
) );
|
||||||
|
|
||||||
|
// Hero settings section
|
||||||
|
$wp_customize->add_section( 'peptide_starter_hero', array(
|
||||||
|
'title' => esc_html__( 'Hero Section', 'peptide-starter' ),
|
||||||
|
'priority' => 40,
|
||||||
|
) );
|
||||||
|
|
||||||
|
// Hero Title
|
||||||
|
$wp_customize->add_setting( 'hero_title', array(
|
||||||
|
'default' => get_bloginfo( 'name' ),
|
||||||
|
'transport' => 'postMessage',
|
||||||
|
) );
|
||||||
|
|
||||||
|
$wp_customize->add_control( 'hero_title', array(
|
||||||
|
'label' => esc_html__( 'Hero Title', 'peptide-starter' ),
|
||||||
|
'section' => 'peptide_starter_hero',
|
||||||
|
'type' => 'text',
|
||||||
|
) );
|
||||||
|
|
||||||
|
// Hero Subtitle
|
||||||
|
$wp_customize->add_setting( 'hero_subtitle', array(
|
||||||
|
'default' => esc_html__( 'A scientific peptide reference database', 'peptide-starter' ),
|
||||||
|
'transport' => 'postMessage',
|
||||||
|
) );
|
||||||
|
|
||||||
|
$wp_customize->add_control( 'hero_subtitle', array(
|
||||||
|
'label' => esc_html__( 'Hero Subtitle', 'peptide-starter' ),
|
||||||
|
'section' => 'peptide_starter_hero',
|
||||||
|
'type' => 'textarea',
|
||||||
|
) );
|
||||||
|
|
||||||
|
// Hero Search Placeholder
|
||||||
|
$wp_customize->add_setting( 'hero_search_placeholder', array(
|
||||||
|
'default' => esc_html__( 'Search peptides, sequences, or research...', 'peptide-starter' ),
|
||||||
|
'transport' => 'postMessage',
|
||||||
|
) );
|
||||||
|
|
||||||
|
$wp_customize->add_control( 'hero_search_placeholder', array(
|
||||||
|
'label' => esc_html__( 'Search Placeholder', 'peptide-starter' ),
|
||||||
|
'section' => 'peptide_starter_hero',
|
||||||
|
'type' => 'text',
|
||||||
|
) );
|
||||||
|
|
||||||
|
// Footer section
|
||||||
|
$wp_customize->add_section( 'peptide_starter_footer', array(
|
||||||
|
'title' => esc_html__( 'Footer', 'peptide-starter' ),
|
||||||
|
'priority' => 50,
|
||||||
|
) );
|
||||||
|
|
||||||
|
// Footer Copyright Text
|
||||||
|
$wp_customize->add_setting( 'footer_copyright', array(
|
||||||
|
'default' => esc_html__( 'Copyright © 2026 Peptide Repo. All rights reserved.', 'peptide-starter' ),
|
||||||
|
'transport' => 'postMessage',
|
||||||
|
) );
|
||||||
|
|
||||||
|
$wp_customize->add_control( 'footer_copyright', array(
|
||||||
|
'label' => esc_html__( 'Copyright Text', 'peptide-starter' ),
|
||||||
|
'section' => 'peptide_starter_footer',
|
||||||
|
'type' => 'textarea',
|
||||||
|
) );
|
||||||
|
|
||||||
|
// Dark mode section
|
||||||
|
$wp_customize->add_section( 'peptide_starter_theme_mode', array(
|
||||||
|
'title' => esc_html__( 'Theme Mode', 'peptide-starter' ),
|
||||||
|
'priority' => 60,
|
||||||
|
) );
|
||||||
|
|
||||||
|
// Dark mode default
|
||||||
|
$wp_customize->add_setting( 'dark_mode_default', array(
|
||||||
|
'default' => false,
|
||||||
|
'transport' => 'refresh',
|
||||||
|
) );
|
||||||
|
|
||||||
|
$wp_customize->add_control( 'dark_mode_default', array(
|
||||||
|
'label' => esc_html__( 'Enable Dark Mode by Default', 'peptide-starter' ),
|
||||||
|
'section' => 'peptide_starter_theme_mode',
|
||||||
|
'type' => 'checkbox',
|
||||||
|
) );
|
||||||
|
}
|
||||||
|
add_action( 'customize_register', 'peptide_starter_customize_register' );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add custom logo support
|
||||||
|
*/
|
||||||
|
function peptide_starter_get_custom_logo() {
|
||||||
|
if ( function_exists( 'get_custom_logo' ) ) {
|
||||||
|
return get_custom_logo();
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Navigation Menu Walker for handling active states
|
||||||
|
*/
|
||||||
|
class Peptide_Starter_Nav_Walker extends Walker_Nav_Menu {
|
||||||
|
public function start_el( &$output, $item, $depth = 0, $args = null, $id = 0 ) {
|
||||||
|
if ( isset( $args->item_spacing ) && 'discard' === $args->item_spacing ) {
|
||||||
|
$t = '';
|
||||||
|
$n = '';
|
||||||
|
} else {
|
||||||
|
$t = "\t";
|
||||||
|
$n = "\n";
|
||||||
|
}
|
||||||
|
$indent = ( $depth ) ? str_repeat( $t, $depth ) : '';
|
||||||
|
|
||||||
|
$classes = empty( $item->classes ) ? array() : (array) $item->classes;
|
||||||
|
$classes[] = 'menu-item-' . $item->ID;
|
||||||
|
|
||||||
|
if ( in_array( 'current-menu-item', $classes, true ) || in_array( 'current-menu-parent', $classes, true ) ) {
|
||||||
|
$classes[] = 'active';
|
||||||
|
}
|
||||||
|
|
||||||
|
$args = apply_filters( 'nav_menu_item_args', $args, $item, $depth );
|
||||||
|
|
||||||
|
$class_names = join( ' ', apply_filters( 'nav_menu_css_class', array_filter( $classes ), $item, $args, $depth ) );
|
||||||
|
$class_names = $class_names ? ' class="' . esc_attr( $class_names ) . '"' : '';
|
||||||
|
|
||||||
|
$id = apply_filters( 'nav_menu_item_id', 'menu-item-' . $item->ID, $item, $args, $depth );
|
||||||
|
$id = $id ? ' id="' . esc_attr( $id ) . '"' : '';
|
||||||
|
|
||||||
|
$output .= $indent . '<li' . $id . $class_names . '>';
|
||||||
|
|
||||||
|
$atts = array();
|
||||||
|
$atts['title'] = ! empty( $item->attr_title ) ? $item->attr_title : '';
|
||||||
|
$atts['target'] = ! empty( $item->target ) ? $item->target : '';
|
||||||
|
$atts['rel'] = ! empty( $item->xfn ) ? $item->xfn : '';
|
||||||
|
$atts['href'] = ! empty( $item->url ) ? $item->url : '';
|
||||||
|
|
||||||
|
$atts = apply_filters( 'nav_menu_link_attributes', $atts, $item, $args, $depth );
|
||||||
|
|
||||||
|
$attributes = '';
|
||||||
|
foreach ( $atts as $attr => $value ) {
|
||||||
|
if ( ! empty( $value ) ) {
|
||||||
|
$value = ( 'href' === $attr ) ? esc_url( $value ) : esc_attr( $value );
|
||||||
|
$attributes .= ' ' . $attr . '="' . $value . '"';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$title = apply_filters( 'nav_menu_item_title', $item->title, $item, $args, $depth );
|
||||||
|
$title = apply_filters( 'nav_menu_item_title', $title, $item, $args, $depth );
|
||||||
|
|
||||||
|
$link = $args->before;
|
||||||
|
$link .= '<a' . $attributes . '>';
|
||||||
|
$link .= $args->link_before . $title . $args->link_after;
|
||||||
|
$link .= '</a>';
|
||||||
|
$link .= $args->after;
|
||||||
|
|
||||||
|
$output .= apply_filters( 'nav_menu_item_title', $link, $item, $args, $depth );
|
||||||
|
|
||||||
|
$output .= '</li>' . $n;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Display custom logo
|
||||||
|
*/
|
||||||
|
function peptide_starter_the_custom_logo() {
|
||||||
|
if ( function_exists( 'the_custom_logo' ) ) {
|
||||||
|
the_custom_logo();
|
||||||
|
} else {
|
||||||
|
$site_name = get_bloginfo( 'name' );
|
||||||
|
echo '<a class="site-logo" href="' . esc_url( home_url( '/' ) ) . '" rel="home">' . esc_html( $site_name ) . '</a>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get hero title
|
||||||
|
*/
|
||||||
|
function peptide_starter_get_hero_title() {
|
||||||
|
return get_theme_mod( 'hero_title', get_bloginfo( 'name' ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get hero subtitle
|
||||||
|
*/
|
||||||
|
function peptide_starter_get_hero_subtitle() {
|
||||||
|
return get_theme_mod( 'hero_subtitle', esc_html__( 'A scientific peptide reference database', 'peptide-starter' ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get search placeholder
|
||||||
|
*/
|
||||||
|
function peptide_starter_get_search_placeholder() {
|
||||||
|
return get_theme_mod( 'hero_search_placeholder', esc_html__( 'Search peptides, sequences, or research...', 'peptide-starter' ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get footer copyright text
|
||||||
|
*/
|
||||||
|
function peptide_starter_get_footer_copyright() {
|
||||||
|
return get_theme_mod( 'footer_copyright', esc_html__( 'Copyright © 2026 Peptide Repo. All rights reserved.', 'peptide-starter' ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Apply dark mode class to body if enabled
|
||||||
|
*/
|
||||||
|
function peptide_starter_dark_mode_body_class( $classes ) {
|
||||||
|
if ( get_theme_mod( 'dark_mode_default', false ) ) {
|
||||||
|
$classes[] = 'dark-mode-default';
|
||||||
|
}
|
||||||
|
return $classes;
|
||||||
|
}
|
||||||
|
add_filter( 'body_class', 'peptide_starter_dark_mode_body_class' );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register pagination function
|
||||||
|
*/
|
||||||
|
function peptide_starter_pagination() {
|
||||||
|
global $wp_query;
|
||||||
|
|
||||||
|
if ( $wp_query->max_num_pages <= 1 ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$current_page = max( 1, get_query_var( 'paged' ) );
|
||||||
|
$total_pages = $wp_query->max_num_pages;
|
||||||
|
|
||||||
|
echo '<nav class="ps-pagination">';
|
||||||
|
echo paginate_links( array(
|
||||||
|
'base' => get_pagenum_link( 1 ) . '%_%',
|
||||||
|
'format' => 'page/%#%/',
|
||||||
|
'current' => $current_page,
|
||||||
|
'total' => $total_pages,
|
||||||
|
'prev_text' => esc_html__( '← Previous', 'peptide-starter' ),
|
||||||
|
'next_text' => esc_html__( 'Next →', 'peptide-starter' ),
|
||||||
|
'type' => 'plain',
|
||||||
|
) );
|
||||||
|
echo '</nav>';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the excerpt
|
||||||
|
*/
|
||||||
|
function peptide_starter_get_excerpt( $post_id = 0, $length = 20 ) {
|
||||||
|
if ( ! $post_id ) {
|
||||||
|
$post_id = get_the_ID();
|
||||||
|
}
|
||||||
|
|
||||||
|
$excerpt = get_the_excerpt( $post_id );
|
||||||
|
|
||||||
|
if ( ! $excerpt ) {
|
||||||
|
$excerpt = wp_trim_words( get_the_content( $post_id ), $length );
|
||||||
|
}
|
||||||
|
|
||||||
|
return $excerpt;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if we should show footer newsletter signup
|
||||||
|
*/
|
||||||
|
function peptide_starter_show_newsletter_form() {
|
||||||
|
return apply_filters( 'peptide_starter_show_newsletter', true );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enqueue inline script for dark mode toggle (output in footer)
|
||||||
|
*/
|
||||||
|
function peptide_starter_inline_dark_mode_script() {
|
||||||
|
$default_dark = get_theme_mod( 'dark_mode_default', false ) ? 'dark' : 'light';
|
||||||
|
?>
|
||||||
|
<script>
|
||||||
|
(function() {
|
||||||
|
const html = document.documentElement;
|
||||||
|
const stored = localStorage.getItem('peptide-starter-theme');
|
||||||
|
const default_theme = <?php echo wp_json_encode( $default_dark ); ?>;
|
||||||
|
const system_dark = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
||||||
|
|
||||||
|
let theme = stored || (system_dark ? 'dark' : default_theme);
|
||||||
|
html.setAttribute('data-theme', theme);
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
|
<?php
|
||||||
|
}
|
||||||
|
add_action( 'wp_head', 'peptide_starter_inline_dark_mode_script' );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Debug helper: check if a plugin is active
|
||||||
|
*/
|
||||||
|
function peptide_starter_is_plugin_active( $plugin ) {
|
||||||
|
return in_array( $plugin, (array) get_option( 'active_plugins', array() ), true );
|
||||||
|
}
|
||||||
130
header.php
Normal file
130
header.php
Normal file
@@ -0,0 +1,130 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* The header for Peptide Starter theme
|
||||||
|
*
|
||||||
|
* @package peptide-starter
|
||||||
|
*/
|
||||||
|
|
||||||
|
?><!doctype html>
|
||||||
|
<html <?php language_attributes(); ?>>
|
||||||
|
<head>
|
||||||
|
<meta charset="<?php bloginfo( 'charset' ); ?>">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<link rel="profile" href="https://gmpg.org/xfn/11">
|
||||||
|
<?php wp_head(); ?>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body <?php body_class(); ?>>
|
||||||
|
<?php wp_body_open(); ?>
|
||||||
|
|
||||||
|
<a href="#main" class="skip-link"><?php esc_html_e( 'Skip to main content', 'peptide-starter' ); ?></a>
|
||||||
|
|
||||||
|
<header class="site-header">
|
||||||
|
<div class="site-header-wrapper">
|
||||||
|
<!-- Logo -->
|
||||||
|
<div class="site-logo-wrapper">
|
||||||
|
<?php peptide_starter_the_custom_logo(); ?>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Primary Navigation -->
|
||||||
|
<nav class="primary-navigation">
|
||||||
|
<?php
|
||||||
|
wp_nav_menu( array(
|
||||||
|
'theme_location' => 'primary',
|
||||||
|
'fallback_cb' => 'peptide_starter_primary_menu_fallback',
|
||||||
|
'walker' => new Peptide_Starter_Nav_Walker(),
|
||||||
|
'depth' => 2,
|
||||||
|
'container' => false,
|
||||||
|
'items_wrap' => '<ul>%3$s</ul>',
|
||||||
|
) );
|
||||||
|
?>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<!-- Navigation Overlay (for mobile) -->
|
||||||
|
<div class="nav-overlay"></div>
|
||||||
|
|
||||||
|
<!-- Header Icons -->
|
||||||
|
<div class="header-icons">
|
||||||
|
<!-- Search Icon -->
|
||||||
|
<button class="header-icon-btn search-toggle" aria-label="<?php esc_attr_e( 'Search', 'peptide-starter' ); ?>">
|
||||||
|
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M9 17C13.4183 17 17 13.4183 17 9C17 4.58172 13.4183 1 9 1C4.58172 1 1 4.58172 1 9C1 13.4183 4.58172 17 9 17Z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path d="M19 19L14.65 14.65" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<!-- Dark Mode Toggle -->
|
||||||
|
<button class="header-icon-btn dark-mode-toggle" aria-label="<?php esc_attr_e( 'Toggle dark mode', 'peptide-starter' ); ?>">
|
||||||
|
<svg class="sun-icon" width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg" style="display: none;">
|
||||||
|
<circle cx="10" cy="10" r="4" stroke="currentColor" stroke-width="2"/>
|
||||||
|
<path d="M10 1V3M10 17V19M19 10H17M3 10H1M16.657 16.657L15.243 15.243M4.757 4.757L3.343 3.343M16.657 3.343L15.243 4.757M4.757 15.243L3.343 16.657" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
|
||||||
|
</svg>
|
||||||
|
<svg class="moon-icon" width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M17.293 13.293A7.002 7.002 0 101.725 2.725a7 7 0 0115.568 10.568z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<!-- Cart Icon (WooCommerce) -->
|
||||||
|
<?php if ( class_exists( 'WooCommerce' ) ) { ?>
|
||||||
|
<button class="header-icon-btn cart-toggle" aria-label="<?php esc_attr_e( 'View cart', 'peptide-starter' ); ?>">
|
||||||
|
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M1 1H3.27924M3.27924 1L5.20081 13.5631C5.35072 14.4571 5.80765 15.2614 6.4543 15.8285C7.10095 16.3956 7.92274 16.6882 8.76721 16.6882H16.7257M16.7257 16.6882C17.3169 16.6882 17.7793 17.1506 17.7793 17.7418C17.7793 18.333 17.3169 18.7955 16.7257 18.7955M16.7257 16.6882C15.6515 16.6882 14.4632 16.6882 8.76721 16.6882M8.76721 16.6882C8.17601 16.6882 7.71365 17.1506 7.71365 17.7418C7.71365 18.333 8.17601 18.7955 8.76721 18.7955M5.20081 13.5631H16.7257" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
<?php } ?>
|
||||||
|
|
||||||
|
<!-- Mobile Menu Toggle -->
|
||||||
|
<button class="menu-toggle" aria-label="<?php esc_attr_e( 'Toggle menu', 'peptide-starter' ); ?>">
|
||||||
|
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M3 6H21M3 12H21M3 18H21" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<!-- Search Overlay (Peptide Search AI) -->
|
||||||
|
<div class="search-overlay" id="search-overlay" aria-hidden="true">
|
||||||
|
<div class="search-overlay-inner">
|
||||||
|
<button class="search-overlay-close" aria-label="<?php esc_attr_e( 'Close search', 'peptide-starter' ); ?>">
|
||||||
|
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M18 6L6 18M6 6L18 18" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
<div class="search-overlay-content">
|
||||||
|
<?php
|
||||||
|
if ( shortcode_exists( 'peptide_search' ) ) {
|
||||||
|
echo do_shortcode( '[peptide_search placeholder="Search for a peptide (e.g., BPC-157, Thymosin Beta-4)..."]' );
|
||||||
|
} else {
|
||||||
|
get_search_form();
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Fallback menu if none is set
|
||||||
|
*/
|
||||||
|
function peptide_starter_primary_menu_fallback() {
|
||||||
|
?>
|
||||||
|
<ul>
|
||||||
|
<li><a href="<?php echo esc_url( home_url( '/' ) ); ?>"><?php esc_html_e( 'Home', 'peptide-starter' ); ?></a></li>
|
||||||
|
<?php
|
||||||
|
$about_page = get_page_by_path( 'about' );
|
||||||
|
if ( $about_page ) {
|
||||||
|
echo '<li><a href="' . esc_url( get_permalink( $about_page ) ) . '">' . esc_html__( 'About', 'peptide-starter' ) . '</a></li>';
|
||||||
|
}
|
||||||
|
// Display link to Peptides archive
|
||||||
|
if ( post_type_exists( 'peptide' ) ) {
|
||||||
|
echo '<li><a href="' . esc_url( get_post_type_archive_link( 'peptide' ) ) . '">' . esc_html__( 'Peptides', 'peptide-starter' ) . '</a></li>';
|
||||||
|
} else {
|
||||||
|
// Fallback to /peptides/ URL if CPT not registered yet
|
||||||
|
echo '<li><a href="' . esc_url( home_url( '/peptides/' ) ) . '">' . esc_html__( 'Peptides', 'peptide-starter' ) . '</a></li>';
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
</ul>
|
||||||
|
<?php
|
||||||
|
}
|
||||||
|
?>
|
||||||
27
index.php
Normal file
27
index.php
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* The main template file
|
||||||
|
*
|
||||||
|
* @package peptide-starter
|
||||||
|
*/
|
||||||
|
|
||||||
|
get_header();
|
||||||
|
?>
|
||||||
|
|
||||||
|
<main id="main" class="site-main">
|
||||||
|
<div class="ps-container">
|
||||||
|
<?php
|
||||||
|
if ( have_posts() ) {
|
||||||
|
while ( have_posts() ) {
|
||||||
|
the_post();
|
||||||
|
get_template_part( 'template-parts/content', get_post_type() );
|
||||||
|
}
|
||||||
|
peptide_starter_pagination();
|
||||||
|
} else {
|
||||||
|
get_template_part( 'template-parts/content', 'none' );
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<?php get_footer(); ?>
|
||||||
53
page.php
Normal file
53
page.php
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* The page template file
|
||||||
|
*
|
||||||
|
* @package peptide-starter
|
||||||
|
*/
|
||||||
|
|
||||||
|
get_header();
|
||||||
|
?>
|
||||||
|
|
||||||
|
<main id="main" class="site-main">
|
||||||
|
<!-- Page Header -->
|
||||||
|
<?php
|
||||||
|
if ( have_posts() ) {
|
||||||
|
while ( have_posts() ) {
|
||||||
|
the_post();
|
||||||
|
?>
|
||||||
|
<header class="page-header">
|
||||||
|
<div class="ps-container">
|
||||||
|
<h1 class="page-title"><?php the_title(); ?></h1>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<!-- Page Content -->
|
||||||
|
<div class="page-content">
|
||||||
|
<article class="post" id="post-<?php the_ID(); ?>" <?php post_class(); ?>>
|
||||||
|
<?php
|
||||||
|
the_content();
|
||||||
|
|
||||||
|
// Display edit link
|
||||||
|
edit_post_link(
|
||||||
|
sprintf(
|
||||||
|
wp_kses(
|
||||||
|
__( 'Edit <span class="screen-reader-text">%s</span>', 'peptide-starter' ),
|
||||||
|
array( 'span' => array( 'class' => array() ) )
|
||||||
|
),
|
||||||
|
get_the_title()
|
||||||
|
),
|
||||||
|
'<div class="edit-link">',
|
||||||
|
'</div>'
|
||||||
|
);
|
||||||
|
?>
|
||||||
|
</article>
|
||||||
|
</div>
|
||||||
|
<?php
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
get_template_part( 'template-parts/content', 'none' );
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<?php get_footer(); ?>
|
||||||
BIN
screenshot.png
Normal file
BIN
screenshot.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 32 KiB |
27
searchform.php
Normal file
27
searchform.php
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* The search form template
|
||||||
|
*
|
||||||
|
* @package peptide-starter
|
||||||
|
*/
|
||||||
|
|
||||||
|
?>
|
||||||
|
<form role="search" method="get" class="ps-search-form" action="<?php echo esc_url( home_url( '/' ) ); ?>">
|
||||||
|
<label for="search" class="sr-only"><?php esc_html_e( 'Search', 'peptide-starter' ); ?></label>
|
||||||
|
<div style="position: relative;">
|
||||||
|
<input
|
||||||
|
type="search"
|
||||||
|
id="search"
|
||||||
|
name="s"
|
||||||
|
class="ps-search-input"
|
||||||
|
placeholder="<?php esc_attr_e( 'Search...', 'peptide-starter' ); ?>"
|
||||||
|
value="<?php echo get_search_query(); ?>"
|
||||||
|
>
|
||||||
|
<button type="submit" class="ps-btn ps-btn-primary" style="position: absolute; right: 0; top: 0; border-radius: 0 var(--radius-lg) var(--radius-lg) 0;">
|
||||||
|
<svg width="20" height="20" viewBox="0 0 20 20" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M9 17C13.4183 17 17 13.4183 17 9C17 4.58172 13.4183 1 9 1C4.58172 1 1 4.58172 1 9C1 13.4183 4.58172 17 9 17Z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path d="M19 19L14.65 14.65" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
100
single-peptide.php
Normal file
100
single-peptide.php
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* The single peptide template
|
||||||
|
*
|
||||||
|
* @package peptide-starter
|
||||||
|
*/
|
||||||
|
|
||||||
|
get_header();
|
||||||
|
?>
|
||||||
|
|
||||||
|
<main id="main" class="site-main">
|
||||||
|
<?php
|
||||||
|
if ( have_posts() ) {
|
||||||
|
while ( have_posts() ) {
|
||||||
|
the_post();
|
||||||
|
?>
|
||||||
|
<!-- Page Header -->
|
||||||
|
<header class="page-header">
|
||||||
|
<div class="ps-container">
|
||||||
|
<h1 class="page-title"><?php the_title(); ?></h1>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<!-- Single Peptide Content -->
|
||||||
|
<div class="page-content">
|
||||||
|
<article class="peptide-single" id="post-<?php the_ID(); ?>" <?php post_class(); ?>>
|
||||||
|
<!-- Featured Image -->
|
||||||
|
<?php if ( has_post_thumbnail() ) { ?>
|
||||||
|
<div class="peptide-featured-image" style="margin-bottom: var(--spacing-2xl);">
|
||||||
|
<?php the_post_thumbnail( 'large' ); ?>
|
||||||
|
</div>
|
||||||
|
<?php } ?>
|
||||||
|
|
||||||
|
<!-- Post Content -->
|
||||||
|
<div class="peptide-content">
|
||||||
|
<?php the_content(); ?>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Meta Information -->
|
||||||
|
<div class="peptide-meta" style="margin-top: var(--spacing-3xl); padding-top: var(--spacing-2xl); border-top: 1px solid var(--color-border-default);">
|
||||||
|
<dl>
|
||||||
|
<?php if ( $formula = get_post_meta( get_the_ID(), 'peptide_formula', true ) ) { ?>
|
||||||
|
<dt><strong><?php esc_html_e( 'Molecular Formula:', 'peptide-starter' ); ?></strong></dt>
|
||||||
|
<dd><code><?php echo esc_html( $formula ); ?></code></dd>
|
||||||
|
<?php } ?>
|
||||||
|
|
||||||
|
<?php if ( $sequence = get_post_meta( get_the_ID(), 'peptide_sequence', true ) ) { ?>
|
||||||
|
<dt><strong><?php esc_html_e( 'Sequence:', 'peptide-starter' ); ?></strong></dt>
|
||||||
|
<dd><code><?php echo esc_html( $sequence ); ?></code></dd>
|
||||||
|
<?php } ?>
|
||||||
|
|
||||||
|
<?php if ( $molecular_weight = get_post_meta( get_the_ID(), 'peptide_molecular_weight', true ) ) { ?>
|
||||||
|
<dt><strong><?php esc_html_e( 'Molecular Weight:', 'peptide-starter' ); ?></strong></dt>
|
||||||
|
<dd><?php echo esc_html( $molecular_weight ); ?> Da</dd>
|
||||||
|
<?php } ?>
|
||||||
|
|
||||||
|
<?php if ( $therapeutic_use = get_post_meta( get_the_ID(), 'peptide_therapeutic_use', true ) ) { ?>
|
||||||
|
<dt><strong><?php esc_html_e( 'Therapeutic Use:', 'peptide-starter' ); ?></strong></dt>
|
||||||
|
<dd><?php echo esc_html( $therapeutic_use ); ?></dd>
|
||||||
|
<?php } ?>
|
||||||
|
</dl>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Edit Link -->
|
||||||
|
<?php
|
||||||
|
edit_post_link(
|
||||||
|
sprintf(
|
||||||
|
wp_kses(
|
||||||
|
__( 'Edit <span class="screen-reader-text">%s</span>', 'peptide-starter' ),
|
||||||
|
array( 'span' => array( 'class' => array() ) )
|
||||||
|
),
|
||||||
|
get_the_title()
|
||||||
|
),
|
||||||
|
'<div class="edit-link" style="margin-top: var(--spacing-2xl);">',
|
||||||
|
'</div>'
|
||||||
|
);
|
||||||
|
?>
|
||||||
|
</article>
|
||||||
|
|
||||||
|
<!-- Navigation -->
|
||||||
|
<nav class="post-navigation" style="margin-top: var(--spacing-3xl); padding-top: var(--spacing-2xl); border-top: 1px solid var(--color-border-default);">
|
||||||
|
<div style="display: flex; justify-content: space-between; gap: var(--spacing-lg);">
|
||||||
|
<div>
|
||||||
|
<?php previous_post_link( '%link', '← %title' ); ?>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<?php next_post_link( '%link', '%title →' ); ?>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
<?php
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
get_template_part( 'template-parts/content', 'none' );
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<?php get_footer(); ?>
|
||||||
51
template-parts/content-none.php
Normal file
51
template-parts/content-none.php
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Template part for displaying content when nothing is found
|
||||||
|
*
|
||||||
|
* @package peptide-starter
|
||||||
|
*/
|
||||||
|
|
||||||
|
?>
|
||||||
|
<div class="ps-card">
|
||||||
|
<header class="entry-header">
|
||||||
|
<h1 class="page-title"><?php esc_html_e( 'Nothing Found', 'peptide-starter' ); ?></h1>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<div class="entry-content">
|
||||||
|
<?php
|
||||||
|
if ( is_search() ) {
|
||||||
|
?>
|
||||||
|
<p><?php esc_html_e( "Sorry, but nothing matched your search terms. Please try again with some different keywords.", 'peptide-starter' ); ?></p>
|
||||||
|
<?php
|
||||||
|
get_search_form();
|
||||||
|
} elseif ( is_home() && current_user_can( 'publish_posts' ) ) {
|
||||||
|
?>
|
||||||
|
<p>
|
||||||
|
<?php
|
||||||
|
printf(
|
||||||
|
wp_kses(
|
||||||
|
__( 'Ready to publish your first post? <a href="%s">Get started here</a>.', 'peptide-starter' ),
|
||||||
|
array(
|
||||||
|
'a' => array(
|
||||||
|
'href' => array(),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
),
|
||||||
|
esc_url( admin_url( 'post-new.php' ) )
|
||||||
|
);
|
||||||
|
?>
|
||||||
|
</p>
|
||||||
|
<?php
|
||||||
|
} elseif ( is_archive() ) {
|
||||||
|
?>
|
||||||
|
<p><?php esc_html_e( "It looks like there are no posts for this archive. Try a different category or date.", 'peptide-starter' ); ?></p>
|
||||||
|
<?php
|
||||||
|
} else {
|
||||||
|
?>
|
||||||
|
<p><?php esc_html_e( "It seems we can't find what you're looking for. Perhaps searching can help.", 'peptide-starter' ); ?></p>
|
||||||
|
<?php
|
||||||
|
get_search_form();
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
48
template-parts/content-peptide.php
Normal file
48
template-parts/content-peptide.php
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Template part for displaying peptide cards in archive views
|
||||||
|
*
|
||||||
|
* @package peptide-starter
|
||||||
|
*/
|
||||||
|
|
||||||
|
if ( ! defined( 'ABSPATH' ) ) {
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
|
||||||
|
<article id="post-<?php the_ID(); ?>" <?php post_class( 'peptide-card' ); ?>>
|
||||||
|
<header class="peptide-card__header">
|
||||||
|
<h2 class="peptide-card__title">
|
||||||
|
<a href="<?php the_permalink(); ?>"><?php the_title(); ?></a>
|
||||||
|
</h2>
|
||||||
|
<?php
|
||||||
|
$categories = get_the_terms( get_the_ID(), 'peptide-category' );
|
||||||
|
if ( $categories && ! is_wp_error( $categories ) ) {
|
||||||
|
echo '<div class="peptide-card__meta">';
|
||||||
|
foreach ( array_slice( $categories, 0, 2 ) as $cat ) {
|
||||||
|
echo '<span class="peptide-card__badge">' . esc_html( $cat->name ) . '</span>';
|
||||||
|
}
|
||||||
|
echo '</div>';
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<div class="peptide-card__excerpt">
|
||||||
|
<?php
|
||||||
|
if ( has_excerpt() ) {
|
||||||
|
echo wp_kses_post( wp_trim_words( get_the_excerpt(), 25, '…' ) );
|
||||||
|
} else {
|
||||||
|
echo wp_kses_post( wp_trim_words( get_the_content(), 25, '…' ) );
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<footer class="peptide-card__footer">
|
||||||
|
<a href="<?php the_permalink(); ?>" class="peptide-card__link">
|
||||||
|
<?php esc_html_e( 'Learn More', 'peptide-starter' ); ?>
|
||||||
|
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
|
||||||
|
<path d="M3 8H13M13 8L9 4M13 8L9 12" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
</svg>
|
||||||
|
</a>
|
||||||
|
</footer>
|
||||||
|
</article>
|
||||||
96
template-parts/content.php
Normal file
96
template-parts/content.php
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Template part for displaying a single post/page
|
||||||
|
*
|
||||||
|
* @package peptide-starter
|
||||||
|
*/
|
||||||
|
|
||||||
|
?>
|
||||||
|
<article <?php post_class( 'ps-card' ); ?> id="post-<?php the_ID(); ?>">
|
||||||
|
<header class="entry-header" style="margin-bottom: var(--spacing-lg);">
|
||||||
|
<?php
|
||||||
|
if ( is_singular() ) {
|
||||||
|
the_title( '<h1 class="entry-title">', '</h1>' );
|
||||||
|
} else {
|
||||||
|
the_title( '<h2 class="entry-title"><a href="' . esc_url( get_permalink() ) . '" rel="bookmark">', '</a></h2>' );
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( 'post' === get_post_type() ) {
|
||||||
|
?>
|
||||||
|
<div class="entry-meta" style="color: var(--color-text-secondary); font-size: var(--text-body-sm); margin-top: var(--spacing-sm);">
|
||||||
|
<?php
|
||||||
|
printf(
|
||||||
|
esc_html__( 'Posted on %s', 'peptide-starter' ),
|
||||||
|
'<time datetime="' . esc_attr( get_the_date( 'c' ) ) . '">' . esc_html( get_the_date() ) . '</time>'
|
||||||
|
);
|
||||||
|
echo ' ';
|
||||||
|
printf(
|
||||||
|
esc_html__( 'by %s', 'peptide-starter' ),
|
||||||
|
'<span class="author vcard"><a class="url fn n" href="' . esc_url( get_author_posts_url( get_the_author_meta( 'ID' ) ) ) . '">' . esc_html( get_the_author() ) . '</a></span>'
|
||||||
|
);
|
||||||
|
?>
|
||||||
|
</div>
|
||||||
|
<?php
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<div class="entry-content" style="margin-bottom: var(--spacing-lg);">
|
||||||
|
<?php
|
||||||
|
if ( has_post_thumbnail() ) {
|
||||||
|
?>
|
||||||
|
<div style="margin-bottom: var(--spacing-lg);">
|
||||||
|
<?php the_post_thumbnail( 'medium' ); ?>
|
||||||
|
</div>
|
||||||
|
<?php
|
||||||
|
}
|
||||||
|
|
||||||
|
the_content(
|
||||||
|
sprintf(
|
||||||
|
wp_kses(
|
||||||
|
__( 'Continue reading<span class="screen-reader-text"> "%s"</span>', 'peptide-starter' ),
|
||||||
|
array(
|
||||||
|
'span' => array(
|
||||||
|
'class' => array(),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
),
|
||||||
|
wp_kses_post( get_the_title() )
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
wp_link_pages(
|
||||||
|
array(
|
||||||
|
'before' => '<div class="page-links">' . esc_html__( 'Pages:', 'peptide-starter' ),
|
||||||
|
'after' => '</div>',
|
||||||
|
)
|
||||||
|
);
|
||||||
|
?>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?php if ( 'post' === get_post_type() ) { ?>
|
||||||
|
<footer class="entry-footer" style="color: var(--color-text-secondary); font-size: var(--text-body-sm); border-top: 1px solid var(--color-border-default); padding-top: var(--spacing-md);">
|
||||||
|
<?php
|
||||||
|
$categories = get_the_category();
|
||||||
|
if ( $categories ) {
|
||||||
|
echo '<div style="margin-bottom: var(--spacing-sm);">';
|
||||||
|
echo esc_html__( 'Categories: ', 'peptide-starter' );
|
||||||
|
foreach ( $categories as $category ) {
|
||||||
|
echo '<a href="' . esc_url( get_category_link( $category->term_id ) ) . '" class="ps-badge">' . esc_html( $category->name ) . '</a> ';
|
||||||
|
}
|
||||||
|
echo '</div>';
|
||||||
|
}
|
||||||
|
|
||||||
|
$tags = get_the_tags();
|
||||||
|
if ( $tags ) {
|
||||||
|
echo '<div>';
|
||||||
|
echo esc_html__( 'Tags: ', 'peptide-starter' );
|
||||||
|
foreach ( $tags as $tag ) {
|
||||||
|
echo '<a href="' . esc_url( get_tag_link( $tag->term_id ) ) . '" class="ps-badge">' . esc_html( $tag->name ) . '</a> ';
|
||||||
|
}
|
||||||
|
echo '</div>';
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
</footer>
|
||||||
|
<?php } ?>
|
||||||
|
</article>
|
||||||
Reference in New Issue
Block a user