WordPress Theme · v2.5.0
PressGrid
A production-ready, security-hardened WordPress theme engineered for high-traffic news and magazine websites. Classic editorial newspaper design, modular layout system, OpenWeatherMap weather integration, Frankfurter ECB forex ticker, and enterprise-grade security — zero JS dependencies beyond WordPress core.
Core Features
What's Included
PressGrid v2.5.0 combines classic editorial newspaper design with engineering discipline. Every feature is purposefully built for modern news operations.
Prerequisites
Requirements
| Requirement | Minimum | Recommended |
|---|---|---|
| WordPress | 6.3 | 6.7+ |
| PHP | 8.0 | 8.2+ |
| MySQL / MariaDB | 5.7 / 10.3 | 8.0 / 10.6+ |
| Browser | All modern browsers (ES2017+, CSS Grid) | |
Setup
Installation
Method 1 — WordPress Admin Upload
- 1UploadGo to
Appearance → Themes → Add New → Upload Theme. Selectpressgrid-v2.5.0.zipand click Install Now. - 2ActivateClick Activate on the theme page, or go to
Appearance → Themes. - 3Configure ColorsGo to
Appearance → Customize → PressGrid: Colorsto adjust the default red/black palette. - 4Add Weather (optional)Go to
Appearance → Customize → PressGrid: Weather. Add your free OpenWeatherMap API key and set your city. - 5Build LayoutGo to
Appearance → Layout Builder. If you assign a Business/Finance category to any section, the forex ticker activates automatically. - 6Set Up MenusGo to
Appearance → Menus. Assign menus to Primary, Footer, and Secondary locations.
Method 2 — WP-CLI
wp theme install pressgrid-v2.5.0.zip --activate
Method 3 — FTP
# Unzip and upload to: /wp-content/themes/pressgrid/
Architecture
File Structure
Red = new or changed in v2.5.0
Appearance → Customize
Customizer
All visual settings are managed through the WordPress Customizer with live preview via postMessage CSS variable transport.
Color Panel
| Setting | CSS Variable | Default |
|---|---|---|
| Primary Color | --pg-primary | #cc0000 |
| Secondary Color | --pg-secondary | #990000 |
| Accent Color | --pg-accent | #cc0000 |
| Background Color | --pg-bg | #ffffff |
| Text Color | --pg-text | #1a1a1a |
| Link Hover Color | --pg-link-hover | #cc0000 |
Social Media Panel New in v2
Set social profile URLs at Appearance → Customize → PressGrid: Social Media. Configured URLs render as icon links in the black top bar.
| Setting | Mod Key | Icon |
|---|---|---|
| Facebook URL | pressgrid_social_facebook | f |
| X / Twitter URL | pressgrid_social_twitter | 𝕏 |
| YouTube URL | pressgrid_social_youtube | ▶ |
| Instagram URL | pressgrid_social_instagram | ◎ |
Layout Options
| Setting | Options |
|---|---|
| Sidebar Position | Right / Left / None (Full Width) |
| Breaking News Bar | Enable / Disable |
| Breaking News Category | Category ID — 0 = all posts |
| Developer Credit | Show / Hide in footer |
Typography
PressGrid ships with a full editorial font stack loaded from Google Fonts. Three font roles are defined as CSS custom properties:
| Variable | Font | Used For |
|---|---|---|
--font-display | Playfair Display (700/800/900) | Post titles, section headers, masthead |
--font-ui | Barlow Condensed (400–800) | Navigation, tabs, buttons, labels |
--font-body | Barlow (300–600) | Body copy, meta, excerpts, top bar |
Appearance → Layout Builder
Layout Builder
Configure the homepage at Appearance → Layout Builder. Each section can be independently enabled, assigned a layout type, source category, and post count.
Available Sections
| Section | Default Layout | State | Description |
|---|---|---|---|
hero | hero-grid | On | 1 large article left + 3 thumbnail sidebar stack right |
latest_posts | grid-3 | On | Most recent posts in configurable grid with sidebar |
category_grid | grid-4 | On | Posts from a specific category (transient cached) |
trending | list | On | 3-column tab block: Trending Now + Editor's Picks + Video |
editor_picks | grid-4 | Off | Standalone editor picks grid, random, 30-min cache |
opinion | grid-2 | On | 2-column opinion items with author avatar |
newsletter | newsletter | On | Full-width newsletter CTA section |
custom_html | custom_html | Off | Free-form HTML (wp_kses_post sanitized) |
ad_block | ad_block | Off | Renders the between-posts ad zone inline |
Appearance → Theme Ads
Advertisement System
Manage all ad placements at Appearance → Theme Ads. Each zone is independently configurable with HTML or ad network tags.
| Zone ID | Location | Typical Size |
|---|---|---|
header | Below sticky navigation | 728×90 |
sidebar_top | Top of sidebar | 300×250 |
sidebar_middle | Between sidebar widget areas | 300×250 or 300×600 |
in_article | Above article content on single posts | Responsive |
between_posts | Homepage Ad Block section | Responsive |
footer | Above site footer | 728×90 |
Rendering in Templates
pressgrid_render_ad( 'sidebar_top' ); pressgrid_render_ad( 'in_article' ); pressgrid_render_ad( 'footer' );
wp_kses() with a strict allowed-tags whitelist. Desktop/mobile visibility is handled with .pg-ad-desktop-only / .pg-ad-mobile-only CSS classes.New in v2.5 · Appearance → Customize → PressGrid: Weather
Weather Widget v2.5
Powered by OpenWeatherMap. Displays current conditions and a 5-day forecast in the sidebar, plus a compact mini widget in the top bar. Visitors can tap the geolocation button to see their local weather. All data is fetched server-side and cached — the visitor's browser never contacts OpenWeatherMap directly.
Configuration
| Setting | Where | Example |
|---|---|---|
| API Key | Customize → Weather | a1b2c3d4e5f6... |
| City | Customize → Weather | Sofia,BG or London,GB |
| Units | Customize → Weather | metric (°C) or imperial (°F) |
| Sidebar widget | Customize → Weather | Enable / Disable |
| Top bar mini widget | Customize → Weather | Enable / Disable |
What the Sidebar Widget Shows
- Current: City name, description, icon, temperature, feels like, hi/lo
- Details: Humidity, wind speed, cloud cover
- Forecast: 5 days with daily icon and temperature
- Geolocation: ⊕ button — asks browser permission, updates widget live, no data stored
Cache & API Usage
| Data | Cache TTL | Requests/day |
|---|---|---|
| Current conditions | 30 minutes | ~48 |
| 5-day forecast | 1 hour | ~24 |
| Free tier limit | — | 1,000 |
Endpoints Used
# Current weather GET https://api.openweathermap.org/data/2.5/weather ?q=Sofia,BG&appid=YOUR_KEY&units=metric # 5-day forecast GET https://api.openweathermap.org/data/2.5/forecast ?q=Sofia,BG&appid=YOUR_KEY&units=metric&cnt=40
New in v2.5 · Appearance → Customize → PressGrid: Currencies
Forex Ticker v2.5
Powered by Frankfurter — free, open-source, European Central Bank data. No API key required. The ticker automatically replaces the Breaking News bar when the Layout Builder has an active section targeting a Business or Finance category.
business, finance, economy, or similar. Assign it to a Layout Builder section. The ticker activates automatically — no API key, no further settings required.How the Auto-Detection Works
On every page load, pressgrid_has_business_section() checks all active Layout Builder sections for a category slug matching the built-in list. If found, the forex ticker is rendered in place of Breaking News. The result is cached for 5 minutes.
Auto-Detected Category Slugs
business, biznes, бизнес, finance, finances, finansi, финанси, economy, ikonomika, икономика, markets, pazari, пазари, money, pari, пари
Customizer Settings
| Setting | Default | Description |
|---|---|---|
| Base currency | EUR | Rates are calculated from this currency |
| Target currencies | USD,GBP,BGN,CHF,JPY | Comma-separated ISO 4217 codes |
| Business category slug | auto-detect | Override only if your slug is non-standard |
| Always show | false | Force ticker on regardless of Layout Builder |
API Details
| Data source | European Central Bank via frankfurter.app |
| API key required | No |
| Update frequency | Once per day (weekdays ~16:00 CET) |
| Cache TTL | 6 hours (~4 requests/day) |
| Rate limit | None for normal usage |
Endpoint & Response
GET https://api.frankfurter.app/latest?from=EUR&to=USD,GBP,BGN,CHF,JPY { "base": "EUR", "date": "2025-03-07", "rates": { "BGN": 1.9558, "CHF": 0.9305, "GBP": 0.8351, "USD": 1.0823 } }
New in v2.5 · single.php
Single Post UX v2.5
Five reader experience enhancements added to every single post. All implemented in vanilla JS — zero external scripts, zero SDKs, zero third-party requests from the browser.
Reading Progress Bar
A 3px red line at the top of the viewport tracks reading progress as the user scrolls. Only activates on single-post pages (detected via body class). Implemented in main.js and styled in style.css. Marked aria-hidden="true" as it is decorative.
Estimated Read Time
Displayed in post meta alongside author and date. Calculated server-side in functions.php:
str_word_count( wp_strip_all_tags( $content ) ) / 200 # Returns minimum 1 minute, rounded up
Native Share Buttons
| Button | Mechanism | Fallback |
|---|---|---|
| Share ↗ | Web Share API (mobile/desktop) | Hidden if not supported by browser |
| Copy link 🔗 | navigator.clipboard | document.execCommand('copy') |
Plain <a href> link | Always available, no JS needed |
Related Posts
3 posts from the same category shown after the article content. Uses a standard WP_Query with orderby=rand and post__not_in to exclude the current post. No plugin required. Only rendered if the current post has at least one category with other matching posts.
Back-to-Top Button
A fixed button (bottom-right) that appears after 400px of scroll and smoothly scrolls to the top. ~10 lines of JS, pure CSS styling, injected before get_footer() in single.php.
New in v2.5 · External Services
API Reference v2.5
All external API calls are made server-side via PHP wp_remote_get(). The visitor's browser never contacts these services directly. Data is stored as WordPress transients.
@import in CSS. For self-hosting, upload .woff2 files via Font Upload.API-GUIDE.md in the theme root for step-by-step registration instructions, endpoint documentation, and GDPR notes for each API.Appearance → Font Upload
Typography
PressGrid ships with a complete editorial Google Fonts stack. For custom brand fonts, upload a .woff2 file via Appearance → Font Upload.
Secure Upload Process
- 1Upload fontSelect a
.woff2file (max 1MB). MIME type is validated against WOFF2 magic bytes before saving. - 2Set family nameGo to
Appearance → Customize → Typographyand enter the font-family name (e.g. MyFont). - 3Apply to elementsCheck: Body, Headings, Menu, or Buttons. The fallback stack (Georgia / Arial) is always preserved.
.htaccess restricting access to .woff2 only.Engineering
Performance Architecture
v2.5 Performance Additions
- Preconnect hints —
<link rel="preconnect">for Google Fonts, OpenWeatherMap, and Frankfurter added conditionally only when those features are active. Saves 100–200ms on first external connection. - Sticky sidebar —
position:sticky, no JS needed - Card hover animations — pure CSS
translateY(-3px)+ box-shadow, no JS - fetchpriority="high" on hero image for LCP optimization
Transient Cache Strategy
| Transient Key | TTL | Cleared When |
|---|---|---|
pressgrid_hero_posts | 5 min | save_post / delete_post |
pressgrid_trending_posts | 10 min | save_post / delete_post |
pressgrid_cat_grid_{id} | 5 min | Per-category on post save |
pressgrid_editor_picks | 30 min | save_post / delete_post |
pressgrid_weather_* | 30 min / 1 hr | customize_save_after |
pressgrid_forex_* | 6 hours | customize_save_after |
pressgrid_has_biz_section | 5 min | Layout sections option update |
Query Optimization
- No query_posts() — All queries use WP_Query directly
- no_found_rows: true — Skips SQL_CALC_FOUND_ROWS on cached queries
- Post ID caching — Only IDs stored in transients, query reconstructed from cache
- Indexed columns only — No meta_query on unindexed keys
Hardening
Security
- ✓Every $_POST goes through wp_verify_nonce() + current_user_can()
- ✓All output escaped with esc_html(), esc_url(), esc_attr(), wp_kses_post()
- ✓Ad HTML filtered through wp_kses() with strict allowed-tags whitelist
- ✓Font upload: WOFF2 magic-byte validation, 1MB cap, random filename, protected dir
- ✓Security headers: X-Content-Type-Options, X-Frame-Options, Referrer-Policy, Permissions-Policy
- ✓WordPress version removed from head and all feeds
- ✓Author enumeration blocked via /?author=N redirect
- ✓Login error messages genericized to prevent username enumeration
- ✓No eval(), no direct DB queries, no obfuscated code
- ✓Direct file access blocked: if(!defined('ABSPATH')) exit; in every PHP file
- ✓Optional XML-RPC disable at Appearance → Theme Security
- ✓All functions prefixed pressgrid_ — no global namespace pollution
- ✓All external API calls server-side only (wp_remote_get) — browser never contacts APIs directly
- ✓Weather geolocation: browser Geolocation API, user consent required, no location data stored
Security Headers
X-Content-Type-Options: nosniff X-Frame-Options: SAMEORIGIN X-XSS-Protection: 1; mode=block Referrer-Policy: strict-origin-when-cross-origin Permissions-Policy: geolocation=(), microphone=(), camera=()
Search & Discovery
SEO & Schema
Structured data and social metadata output automatically on every applicable page type.
NewsArticle JSON-LD
{ "@context": "https://schema.org", "@type": "NewsArticle", "headline": "Article title", "datePublished": "2025-01-01T00:00:00+00:00", "dateModified": "2025-01-02T00:00:00+00:00", "author": { "@type": "Person" }, "publisher": { "@type": "Organization" } }
Other SEO Features
- Open Graph: og:title, og:description, og:image, og:type, og:site_name
- Twitter Cards: summary_large_image with auto-featured image
- Canonical <link> tag on every page
- Breadcrumb navigation with schema-ready markup
- Semantic HTML5: header, main, article, nav, aside, footer
- Reading time estimation shown in post meta
Developer API
Hooks & Public Functions
Key Public Functions
# Template functions pressgrid_breadcrumbs(); pressgrid_post_meta( $post_id ); # author, date, read time pressgrid_render_ad( 'sidebar_top' ); pressgrid_get_category_label( $post_id ); # Cached queries $q = pressgrid_get_hero_posts( 4 ); $q = pressgrid_get_trending_posts( 6 ); $q = pressgrid_get_editor_picks( 4 ); $q = pressgrid_get_category_posts( $cat_id, 4 ); # v2.5 — Weather $weather = pressgrid_get_weather(); $forecast = pressgrid_get_forecast(); # v2.5 — Forex $has_biz = pressgrid_has_business_section(); $rates = pressgrid_get_forex_rates(); # v2.5 — Read time $mins = pressgrid_reading_time( $post_id );
Filters
| Filter | Default | Description |
|---|---|---|
pressgrid_content_width | 860 | Global $content_width in pixels |
excerpt_length | 20 | Words in auto-excerpts |
wp_headers | — | Security headers added here |
i18n / l10n
Translation
Text domain: pressgrid. The .pot template (182 strings) is in /languages/. Two translations ship with the theme:
| Locale | File | Status |
|---|---|---|
bg_BG | pressgrid-bg_BG.po/mo | Complete (182 strings) |
en_GB | pressgrid-en_GB.po/mo | Complete |
# Generate fresh POT file wp i18n make-pot . languages/pressgrid.pot --domain=pressgrid # Compile PO to MO msgfmt languages/pressgrid-bg_BG.po -o languages/pressgrid-bg_BG.mo
Modular Architecture
Template Parts
Calling with Args
# Category grid with custom args get_template_part( 'template-parts/layout/category-grid', null, array( 'layout' => 'grid-3', 'category' => 5, 'post_count' => 6 ) ); # Opinion section filtered to a specific category get_template_part( 'template-parts/layout/opinion', null, array( 'category' => 7, 'post_count' => 4 ) );
Child Theme Override
# Copy at same relative path inside child theme: child-theme/template-parts/content/post-card.php child-theme/template-parts/layout/opinion.php
Infrastructure
Caching
Manual Cache Clear
delete_transient( 'pressgrid_hero_posts' ); delete_transient( 'pressgrid_trending_posts' ); delete_transient( 'pressgrid_editor_picks' ); delete_transient( 'pressgrid_cat_grid_' . $cat_id ); delete_transient( 'pressgrid_has_biz_section' ); # Weather and forex transients cleared automatically via customize_save_after
History
Changelog
- New — Frankfurter forex ticker Auto-activates when Layout Builder has a Business/Finance section. No API key required. ECB data, 6-hour cache.
- New — OpenWeatherMap weather widget Sidebar widget + top bar mini widget, 5-day forecast, browser geolocation support.
- New — Reading progress bar Thin red line at top of viewport on single posts only.
- New — Estimated read time Calculated from word count, shown in post meta.
- New — Native share buttons Web Share API + copy-to-clipboard + LinkedIn. Zero external JS or SDKs.
- New — Related posts 3 posts from same category after article content. No plugin required.
- New — Back-to-top button Appears at 400px scroll, smooth scroll.
- New — Sticky sidebar position:sticky, no JS needed.
- New — Card hover animations translateY(-3px) + box-shadow, pure CSS.
- New — Open Graph + Twitter Card Full social meta tags on all page types.
- New — NewsArticle JSON-LD Schema markup on all single posts.
- New — Breadcrumb navigation Semantic, accessible, schema-ready.
- New — Preconnect hints Google Fonts, OpenWeatherMap, Frankfurter (conditional).
- New — API-GUIDE.md Step-by-step documentation for all three APIs.
- New — bg_BG + en_GB translations 182 strings, compiled .mo files included.
- Improved — pressgrid_post_meta() Now includes read time and modified date.
- New design system Classic newspaper aesthetic: black/white/red palette, replacing previous blue-accent palette
- Font stack Playfair Display (headings) + Barlow Condensed (UI) + Barlow (body) via Google Fonts
- Top bar Black strip with current date and social icon links; URLs configurable in Customizer
- Masthead Centered newspaper-style logo with split-colour name and em-dash tagline
- Hero section Rebuilt as 3:2 newspaper grid: full-bleed image left, three thumbnail sidebar items right
- Trending section Rebuilt as 3-column tab block: Trending Now, Editor's Picks, and Video
- Opinion section New Layout Builder section: 2-column grid with author byline
- Social Media Customizer panel Facebook, X/Twitter, YouTube, Instagram URLs
- Layout Builder expanded 8 → 9 sections with the addition of opinion
- CSS overhaul Full rewrite using newspaper-appropriate typography scale and CSS custom properties
- Full theme architecture: Layout Builder, Ad Zones, Customizer
- Security hardening: headers, MIME validation, nonces, escaping throughout
- Transient caching for hero, trending, category, editor picks
- Vanilla JS: nav, lazy load, dropdowns, smooth scroll
- WCAG 2.1 AA accessibility compliance
- WordPress.org Theme Review guidelines compliance
- GPL-2.0-or-later license