惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

月光博客
月光博客
N
Netflix TechBlog - Medium
罗磊的独立博客
博客园 - 聂微东
美团技术团队
GbyAI
GbyAI
Microsoft Security Blog
Microsoft Security Blog
Recent Commits to openclaw:main
Recent Commits to openclaw:main
博客园_首页
宝玉的分享
宝玉的分享
G
GRAHAM CLULEY
Microsoft Azure Blog
Microsoft Azure Blog
量子位
SecWiki News
SecWiki News
F
Fortinet All Blogs
J
Java Code Geeks
S
SegmentFault 最新的问题
V
V2EX
Martin Fowler
Martin Fowler
F
Full Disclosure
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
P
Proofpoint News Feed
S
Security Affairs
Application and Cybersecurity Blog
Application and Cybersecurity Blog
K
Kaspersky official blog
S
Secure Thoughts
S
Schneier on Security
MongoDB | Blog
MongoDB | Blog
博客园 - 三生石上(FineUI控件)
Cloudbric
Cloudbric
雷峰网
雷峰网
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
The Cloudflare Blog
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
爱范儿
爱范儿
V2EX - 技术
V2EX - 技术
H
Hackread – Cybersecurity News, Data Breaches, AI and More
腾讯CDC
阮一峰的网络日志
阮一峰的网络日志
Apple Machine Learning Research
Apple Machine Learning Research
H
Help Net Security
C
Check Point Blog
T
The Blog of Author Tim Ferriss
D
DataBreaches.Net
Hacker News - Newest:
Hacker News - Newest: "LLM"
G
Google Developers Blog
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
T
Tenable Blog
博客园 - 【当耐特】

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant Common SOC 2 Failures (Real World) Stop Vibe-Checking Your AI App: A Practical Guide to Evals How to Use SonarQube and SonarScanner Locally to Level Up Your Code Quality Your Next To-Do App Is Dead — I Replaced Mine with an OpenClaw AI Sign a Nostr event in 60 lines of Python using coincurve — no nostr-sdk, no nbxplorer, no rust toolchain ITGC Audit Explained Like You’re in Big 4 Patch Tuesday abril 2026: Microsoft parcha 163 vulnerabilidades y un zero-day en SharePoint Stop scraping everything: a better way to track competitor price changes Listing on MCPize + the Official MCP Registry while routing payments OUTSIDE the marketplace — how I kept 100% of my x402 revenue Building an AI-Powered Risk Intelligence System Using Serverless Architecture Why We Ripped Function Overloading Out of Our AI Toolchain Testing AI-Generated Code: How to Actually Know If It Works SaaS Churn Is Killing Your Business. Here Is What to Do About It (Without a Support Team) The Speed of AI Is No Longer Linear - And Self-Improving Models Are Why How to Implement RBAC for MCP Tools: A Practical Guide for Engineering Teams From Standard Quote to Persuasive Proposal: AI Automation for Arborists I built a CLI that scaffolds complete multi-tenant SaaS apps Axios CVE-2025–62718: The Silent SSRF Bug That Could Be Hiding in Your Node.js App Right Now The dashboard that ended our friendship Data Pipelines Explained Simply (and How to Build Them with Python) The Hidden Cost of AI Systems Nobody Talks About. undefined vs undeclared, and how typeof behaves Switching from file-based jobs to NATS/Kafka in Rust without changing code io_uring Adventures: Rust Servers That Love Syscalls Why Agentic AI is Killing the Traditional Database The POUR principles of web accessibility for developers and designers Quantum Neural Network 3D — A Deep Dive into Interactive WebGL Visualization How To Install Caveman In Codex On macOS And Windows Automation Pipeline Reliability: Why Your Workflow Breaks When Nobody Is Watching I Built an 'Open World' AI Coding Agent — It Works From ANY Folder From Freelancing to Product: A Tech Service Company's SaaS Transformation China's AI Giants: Adding Tencent Hunyuan & ByteDance Doubao to AI University (74 Providers) On the Vibe Coders and Their Lies clerk: Auto-Summarize Your Claude Code Sessions AI Weekly — 2026/04/10–04/17 | The Model Lockdown Is Here, but the Toolchain Is the Real Battleground AI 週報 — 2026/04/10–2026/04/17 模型封鎖潮來了,但工具鏈才是真戰場 Maybe this is how Open-Source apps are born... 🚀 Fine-Tune LLMs with LoRA and QLoRA: 2026 Guide tRPC v11 + Next.js App Router: End-to-End Type Safety Without the Boilerplate ShadCN UI in 2026: Why I Stopped Installing Component Libraries and Started Owning My Components SaaS Billing in React Server Components: Stripe + Supabase Without a Single `useEffect` Join our DEV Weekend Challenge — $1,000 in Prizes Across TEN winners! Submissions Due April 20 at 6:59 AM UTC. Implementing FSRS Spaced Repetition in Flutter + Supabase — Adding Memory Science to an AI Learning App "I Texted My Localhost From the Train — Claude Code Fixed the Bug Before I Got Home" I Built a Sales Prep AI and It Went Deeper Than Expected Design to Code #2: One JSON, Eleven Outputs Solving the 100M-Row Problem: A Summary Table Pattern for High-Volume Push Notification Logs Flutter Web With Wasm: What Actually Changes For Developers I Built 50 Royalty-Free Soundtracks for My Side Project in a Weekend Using AI Music Generation The Vibe Coding Security Checklist: 7 Things to Check Before You Ship Stop Letting Googlebot Guess Fix Your React App's SEO Right Desconstruindo o Streaming do LinkedIn: Como Criar um Engine de Extração de Vídeo de Alta Performance com HLS e FFmpeg (EDA Part-1) EDA (Exploratory Data Analysis) Explained With Real Life — Why Looking at Your Data Is the Most Important Step in Machine Learning Brand Relationship Management at Scale: Our 4-Touch Outreach System for 200+ Brands Why String.fromEnvironment() Might Return an Empty String in Dart JGuardrails 1.0.0 — Hardening Java LLM Apps Against Jailbreaks, Toxicity, and Prompt Injection Plan and Schedule a Full Week of Threads Content From One Claude Conversation Coding Cat Oran Ep3, Five Tables Changed Everything Updated: BFF Pattern I'm done watching freelancers get buried by 200 proposals. So I'm building the alternative. This is my first post BFS Algorithm in Java Step by Step Tutorial with Examples Tracking LLM Pricing Monthly: An Open Dataset for 22 AI Models How We Measure Content ROI on a Comparison Site: Revenue Attribution Without Perfect Data Introducing Nova AI Ops: The AI-Native Operating System for SRE Teams I built a free desktop video downloader for Windows — Grabbit How Talkie OCR Helps Vision-Impaired & Dyslexic Users Read the World Around Them VRCFaceTracking安装和iPhone面捕配置教程,有bug Even CrowdStrike Can't See Your Agents The Automation Gold Rush: What n8n Workflows and Claude Are Opening Up for Developers Right Now
Rebuilding a Divi Blog with Custom Shortcodes: A Field Report
Vicente G. Reyes · 2026-06-20 · via DEV Community

A redesign of the blog list and post detail pages for Rev6, a fitness and wellness platform running WordPress + Divi on Cloudways. This is less a "look at the pretty result" piece and more an honest account of the architecture decisions and the bugs that taught me something — including the one that cost a chunk of a Saturday and had nothing to do with code at all.

The starting point

Rev6's blog needed a full visual and structural overhaul: a proper editorial list page with a category sidebar, sorting, and a card grid, plus a clean reading-focused detail page with an author bar, author bio, and related posts. The site is built on a Divi child theme, so the obvious first question was whether to lean on Divi's built-in Blog module.

I didn't. The Blog module is fine for generic listings, but it falls down on the things this project actually needed: URL-based category routing, custom card markup, and membership-aware post handling. Fighting a page builder to produce non-default markup is a losing game — you end up with a tangle of overrides that breaks on the next Divi update. So the list and detail pages are driven by a small system of custom shortcodes living in the child theme's inc/rev6-blog-functions.php, styled by a single assets/rev6-blog.css, and assembled inside Divi's Theme Builder.

The shortcode system

The whole front end is composed from a handful of focused shortcodes:

  • [rev6_blog_sidebar] — category navigation, search, branding
  • [rev6_sort_dropdown] — sort control
  • [rev6_post_grid] — the card grid
  • [rev6_author_bar] — byline on the detail page
  • [rev6_author_bio] — author block at the foot of a post
  • [rev6_related_posts] — "Keep reading" section

Keeping each piece as its own shortcode means the Theme Builder template is just a thin assembly layer — a Code module with the shortcodes in the order they should render — and all the logic stays in versioned PHP rather than locked inside the page builder's database.

One template for every category

The neat trick on the list side is that a single Theme Builder template serves both the main blog index and every category archive. The post grid reads the query context and filters itself:

$current_id = is_category() ? (int) get_queried_object_id() : null;
// ...
if (is_category()) {
    $args['cat'] = (int) get_queried_object_id();
}

Assign the template to Pages → Blog and Categories → All Category Pages, and the same markup produces the right results everywhere. On the index you get all posts; on /category/longevity/ the grid scopes itself to Longevity. No per-category templates, no duplication.

The editorial layout — and a 27,000-pixel bug

The list page uses a named-area CSS grid: a sticky sidebar spanning the full height, with the sort control, card grid, and pagination stacked in the main column.

.rev6-blog-layout {
    display: grid;
    grid-template-columns: var(--rev6-sidebar-w) 1fr;
    grid-template-rows: auto 1fr auto;
    grid-template-areas:
        "sidebar sort"
        "sidebar grid"
        "sidebar pagination";
    column-gap: var(--rev6-gutter);
    row-gap: 28px;
}

This is the v2.2 layout. v2.1 had a genuinely fun bug. To make the sidebar span the full content height, I'd reached for grid-row: 1 / span 999 — the lazy "just span everything" move. The problem: span 999 doesn't clamp to the number of real rows. It generates ~996 phantom implicit rows below the content, and every one of them inherited the 28px row-gap. The result was roughly 27,888px of dead whitespace hanging below the pagination. The fix was to stop being lazy and define an explicit three-row grid with named areas, letting the sidebar occupy a named region that spans the rows that actually exist. Lesson: span <big number> is a code smell in grid layouts; name your areas.

The detail page

The post detail template is four stacked pieces: the post title, an author bar, the post content (carrying a rev6-single-content class for typography), the author bio, and related posts.

Typography is the usual editorial setup — Roboto Serif headings in a deep blue (#1d5280), a navy body color (#232a43), generous line-height, a constrained measure for readability. The author system is custom: rather than rely on WordPress's native bio field, the theme registers a "Rev6 Author Profile" section on the user edit screen with dedicated Role and bio fields, and the author photo comes from Simple Local Avatars.

The related-posts block is small but worth showing, because it became the centerpiece of a debugging session that had nothing to do with the code:

function rev6_related_posts($atts) {
    $atts = shortcode_atts(['count' => 3], $atts);
    if (!is_single()) return '';
    $cat = rev6_primary_category();
    if (!$cat) return '';
    $q = new WP_Query([
        'post_type'           => 'post',
        'posts_per_page'      => (int) $atts['count'],
        'post_status'         => 'publish',
        'cat'                 => (int) $cat->term_id,
        'post__not_in'        => [get_the_ID()],
        'ignore_sticky_posts' => 1,
        'orderby'             => 'date',
        'order'               => 'DESC',
    ]);
    if (!$q->have_posts()) return '';
    // ...render cards...
}

It pulls up to three posts from the current post's primary category, excluding the current one. Primary category resolution is defensive — it prefers Yoast's designated primary term but falls back gracefully if that term no longer exists:

function rev6_primary_category($post_id = null) {
    $post_id = $post_id ?: get_the_ID();
    if (function_exists('yoast_get_primary_term_id')) {
        $term_id = yoast_get_primary_term_id('category', $post_id);
        if ($term_id) {
            $term = get_term($term_id, 'category');
            if (!is_wp_error($term) && $term) return $term;
        }
    }
    $cats = get_the_category($post_id);
    return !empty($cats) ? $cats[0] : null;
}

The "bug" that wasn't

On the live site, the related-posts section refused to render on the flagship post. The code looked correct. I'd already confirmed the categories existed. So where was the bug?

There wasn't one. The category existed but was empty — the only post assigned to "Longevity" was the very post being viewed. post__not_in excluded it, the query returned zero results, and the function did exactly what it should: returned an empty string. The moment I assigned a second post to the category, "Keep reading" appeared instantly.

This is a recurring trap with content-driven features: the function is correct, the data is incomplete, and the symptom looks like a code failure. Before debugging a query, check whether it has anything to query.

The cutover: code and layout move, content doesn't

The single most expensive lesson came during the staging-to-live migration, and it's the one I'd most want a past version of myself to internalize.

I'd pushed the theme files to live and imported the Divi Theme Builder templates from staging. Everything should have matched. It didn't — the detail page body sat in the wrong place, related posts were missing, the sidebar looked thin. My instinct was to keep editing CSS on live to force a match. That instinct was wrong, and it cost several round-trips.

The actual issue: imports move code and layout. They do not move content.

  • Theme files (.css, .php) carry presentation and logic.
  • A Divi Theme Builder export carries template structure — sections, modules, CSS classes.
  • Neither one carries posts, categories, or which post belongs to which category. Those are database rows.

So a body-width difference between staging and live wasn't a CSS problem at all — the width was set on a Divi module's Design tab, which lives in the database, not the stylesheet. The empty related-posts blocks weren't a code problem — the category-to-post assignments had never been recreated on live. I'd copied the stylesheet byte-for-byte and the two environments still diverged, which is the clearest possible signal that the difference lives somewhere a stylesheet can't reach.

Once you internalize the split — files and templates are the shell; the database is the contents — the whole class of "I imported it but it's different" problems becomes diagnosable in seconds instead of debugged in CSS for an hour.

A corollary, learned the hard way and stated here as a warning: never resolve a staging/live difference with a blanket database push to production. A full DB sync overwrites live's real data — orders, registrations, everything users created since the snapshot. Use targeted file copies and, where data genuinely must move, scoped table-level operations. The convenience of a one-click "push everything" is not worth what it can erase.

Caching will lie to you

One more environmental gremlin worth naming: aggressive CSS optimization. Cloudways' Breeze with "Used CSS" (RUCSS) strips selectors it doesn't detect as used when it generates its optimized stylesheet. If a template wasn't live when the cache was built, perfectly correct rules — list-style: none on pagination, container widths, an icon font — get pruned, and you get partial styling: some rules apply, others vanish. Partial application is the tell. A fully missing stylesheet looks different from a half-applied one.

The fix isn't to touch the CSS; it's to purge and regenerate the Used CSS after any template change, and to exclude the relevant selectors from optimization if they keep getting stripped. When debugging CSS on an optimized WordPress site, clearing cache is step zero, not step ten.

Takeaways

  • Don't fight the page builder. If you need non-default markup or custom query logic, own it in versioned PHP and let the builder assemble.
  • Name your grid areas. span 999 will find a way to embarrass you.
  • A correct function over empty data looks identical to a broken function. Check the data first.
  • Files and templates are the shell; the database is the contents. Most "I migrated it but it's different" bugs live in the gap between those two.
  • Never blanket-push a database to production.
  • On an optimized WordPress stack, purge the cache before you believe anything you see.

The redesign shipped: editorial list page with context-aware category routing, a clean reading-focused detail page, a working author system, and related posts that populate themselves from category data. The code was the easy part. The environment was the teacher.