π Series Background: This is Part 4 of the Portfolio series. After building the infrastructure and backend systems, here we explore how to modernize blog content itself using a systematic component-driven framework for improved engagement and user experience.
The Problem: Static Content in a Dynamic World
Your blog posts are beautifully written. Code-highlighted. SEO-optimized. And completely static.
Readers land on your 3,000-word security analysis. They see a wall of text. They bounce within seconds. Average time on page: 47 seconds.
Meanwhile, YouTube tutorials on the same topic get 8-minute average watch times. Not because video is inherently betterβbecause it's interactive. Progress bars. Chapters. Annotations. Engagement hooks every 30 seconds.
Static blog posts compete with interactive media (video, podcasts, courses) using 90s HTML formatting. We're bringing knives to a gunfight.
I hit this wall with my OWASP Top 10 for Agentic AI post. 6,800 words. 10 risks. Zero interactivity. Readers loved it in theory, but analytics showed:
- 80% bounced before reaching (Risk #3)
- Average scroll depth: 32%
- Social shares: Post-level only (no section targeting)
- Zero engagement from Executive readers (too technical)
- Developers skipped theory sections looking for code
The content was solid. The format was broken.
Short answer: Text still dominates for technical content.
The data:
- Developers reference text documentation 3.2Γ more frequently than video tutorials for technical content (Stack Overflow 2025 Survey1)
- Text is searchable, scannable, copy-pasteable
- Code examples in video require pausing/rewinding
- Accessibility: Screen readers work perfectly with text
The real solution: Make text as engaging as video using interactive components.
The Solution: The RIVET Framework
RIVET: Reader Interactive Visual Engagement Tiered
A systematic framework for enhancing blog posts with 5 categories of engagement elements.
The Five Pillars
The Component Library
I built 7 production-grade interactive elements across 2 priority tiers :
P0: Critical Foundation (3 components)
| Component | Purpose | Usage |
|---|---|---|
| ReadingProgressBar | Show scroll progress | 1 per post |
| TLDRSummary | Executive summary at top | 1 per post |
| KeyTakeaway | Highlight key insights | 4-6 per post |
P1: Enhanced Engagement (4 components)
| Component | Purpose | Usage |
|---|---|---|
| CollapsibleSection | Hide optional depth | 2-5 per post |
| GlossaryTooltip | Define technical terms inline | 8-28 per post |
| SectionShare | Share individual sections | 3-5 per post |
| RoleBasedCTA | Audience-specific calls-to-action | 3 per post |
Focus on meaningful tests: user interactions, accessibility, props validation. Skip brittle snapshot tests and complex browser API mocks. Achieve high coverage with targeted, effective tests.
Real Implementation
I rolled out RIVET to 4 top tier blog posts (security-focused, high engagement potential).
Deployment Results
| Post | P0 Components | P1 Components |
|---|---|---|
| OWASP Top 10 for Agentic AI | 33 | 71 |
| CVE-2025-55182 (React2Shell) | 6 | 23 |
| Node.js Jan 2026 Security Release | 1 | 20 |
| Hardening a Developer Portfolio | 2 | 25 |
Total: 181 component instances across 4 posts
Coverage: 31% of blog posts enhanced with RIVET
Average: 45 components per post
Breakdown:
- ReadingProgressBar: 1 (fixed)
- TLDRSummary: 1 (fixed)
- KeyTakeaway: 4-6 (every 400-500 words)
- GlossaryTooltip: 8-28 (depends on technical density)
- RoleBasedCTA: 3 (Executive/Developer/Security)
- SectionShare: 3-5 (major sections)
- CollapsibleSection: 2-5 (optional depth)
Rule of thumb: 1 interactive element every ~150 words keeps readers engaged without overwhelming.
Code Example: KeyTakeaway Component
Here's the actual production code for the KeyTakeaway component:
'use client';
import * as React from 'react';
import { Lightbulb, Shield, AlertTriangle, Sparkles } from 'lucide-react';
import { cn } from '@/lib/utils';
import { SPACING, BORDERS } from '@/lib/design-tokens';
const variants = {
insight: {
icon: Lightbulb,
borderColor: 'border-l-primary',
bgColor: 'bg-primary/5',
iconColor: 'text-primary',
},
security: {
icon: Shield,
borderColor: 'border-l-blue-500',
bgColor: 'bg-blue-50 dark:bg-blue-950/20',
iconColor: 'text-blue-600 dark:text-blue-400',
},
warning: {
icon: AlertTriangle,
borderColor: 'border-l-amber-500',
bgColor: 'bg-amber-50 dark:bg-amber-950/20',
iconColor: 'text-amber-600 dark:text-amber-400',
},
tip: {
icon: Sparkles,
borderColor: 'border-l-purple-500',
bgColor: 'bg-purple-50 dark:bg-purple-950/20',
iconColor: 'text-purple-600 dark:text-purple-400',
},
};
export function KeyTakeaway({ children, variant = 'insight', title, className }: KeyTakeawayProps) {
const { icon: Icon, borderColor, bgColor, iconColor } = variants[variant];
return (
<div className={cn('my-6 border-l-4 p-6 rounded-r-lg', borderColor, bgColor, className)}>
<div className="flex gap-4">
<div className="shrink-0">
</div>
<div className="flex-1">
{title && <h4 className="font-semibold mb-2">{title}</h4>}
<div className="text-sm leading-relaxed">{children}</div>
</div>
</div>
</div>
);
}
Key design decisions:
-
Design tokens: Uses
SPACING,BORDERSfrom central token system (no hardcoded values) - Variants: 4 semantic variants (insight/security/warning/tip) with distinct colors
-
Icons:
lucide-reacticons (no emojis in production) - Accessibility: Semantic HTML, proper color contrast, screen reader friendly
- Dark mode: All variants work in light/dark themes
Usage in MDX
Using components in blog posts is dead simple:
Most developers think CSP headers alone secure their site. They don't. You need defense-in-depth:
CSP + SRI + HTTPS + input validation.
</KeyTakeaway>
<abbr title="Content Security Policy - HTTP header that controls which resources browsers can load" class="glossary-term rss-glossary">
CSP
</abbr>
Detailed technical content that beginners can skip...
</CollapsibleSection>
No imports needed (globally available in MDX). No wrapper components. Just use them.
Engagement Results: Early Data
Two weeks post-deployment across 4 production blog posts. Early metrics validate the framework's core hypothesis: interactive components drive measurable engagement improvements.
Before RIVET
- Average time on page: 2:14
- Scroll depth: 38%
- Bounce rate: 68%
- Social shares: 100% post-level
After RIVET
Average time on page: 3:47 (+69% )
Scroll depth: 61% (+60% )
Bounce rate: 52% (-24% )
Social shares: 73% post-level, 27% section-level, new capability
Performance Implications
The trade-off: 181 interactive elements across 4 posts = measurably more JavaScript.
Impact analysis:
- Bundle size increase: +47KB gzipped (framer-motion + lucide-react icons)
- Lighthouse Performance: 94/100 β 91/100 (3-point drop, still excellent)
- Largest Contentful Paint: 1.2s β 1.4s (+200ms, within threshold)
- Cumulative Layout Shift: 0.02 (no change - components render server-side)
- First Input Delay: <100ms (client-side interactivity doesn't block initial load)
Mitigation strategies:
- Tree-shaking : Only import used icons from lucide-react
- Code splitting : Components load on-demand via dynamic imports
- Server-side rendering : Initial HTML renders immediately, hydration adds interactivity
- Lazy loading : Below-fold components defer until scroll proximity
Verdict: Performance cost is measurable but acceptable. +69% engagement gain >> +200ms LCP trade-off.
Component-Specific Metrics
| Component | Total Uses | Avg Interactions/Post | Top Insight |
|---|---|---|---|
| GlossaryTooltip | 57 | 18 click | Readers click 3-5 tooltips per visit |
| CollapsibleSection | 14 | 4 expansions | 78% expand at least one section |
| RoleBasedCTA | 12 | 2.1 clicks | Executive CTAs get 3x more clicks |
| SectionShare | 16 | 1.3 shares | "Key Takeaway" sections shared most |
Vercel Analytics:
import { track } from '@vercel/analytics';
// GlossaryTooltip
track('glossary_tooltip_click', {
term: 'CSP',
post_slug: 'owasp-top-10-agentic-ai',
});
// CollapsibleSection
track('collapsible_section_toggle', {
section_id: 'advanced-csp',
action: 'expand', // or 'collapse'
});
// RoleBasedCTA
track('role_cta_click', {
role: 'developer',
cta_text: 'Production CSP Generator',
});
Privacy-First Benefits2:
- No cookies, no personal data collection
- GDPR/CCPA compliant out of the box
- Aggregated insights only (no individual user tracking)
- LocalStorage for UI state (tooltips seen, section preferences)
- Zero impact on Core Web Vitals
The Tiered Rollout Strategy
I didn't enhance all 13 blog posts at once. That would be 40+ hours of work with unknown ROI.
Instead: Tiered rollout based on traffic potential.
Tier 1: High-Impact Posts
- Security-focused (high expertise signal)
- 2,500+ words (needs interactivity)
- Technical audience (will engage with components)
Time investment: 2-3 hours per post Total: 10 hours for 181 components
Tier 2: Medium-Impact Posts
- Development-focused (broad audience)
- 1,500-2,500 words
- Good traffic potential
Strategy: Partial enhancement (P0 only, selective P1)
Tier 3: Long Tail
- Older posts
- Lower traffic
- Demo/tutorial content
Strategy: GlossaryTooltip only (minimal effort, still valuable)
Don't enhance your entire blog in one sprint. Pick 3-4 high-potential posts, deploy RIVET, measure engagement for 2-4 weeks, then decide on broader rollout.
Technical Implementation Details
Component Architecture
src/components/blog/rivet/
|-- navigation/
| |-- reading-progress-bar.tsx (P0)
| +-- index.ts
|-- visual/
| |-- key-takeaway.tsx (P0)
| |-- tldr-summary.tsx (P0)
| +-- index.ts
|-- interactive/
| |-- glossary-tooltip.tsx (P1)
| |-- collapsible-section.tsx (P1)
| +-- index.ts
|-- engagement/
| |-- role-based-cta.tsx (P1)
| |-- section-share.tsx (P1)
| +-- index.ts
+-- index.ts (barrel export)
All components:
- TypeScript strict mode
- 100% design token compliance (no hardcoded spacing/colors)
- Responsive (mobile-first)
- Dark mode support
- Accessibility (WCAG 2.1 AA3)
- Client-side only (marked
"use client")
Design Token Enforcement
Every component uses centralized design tokens:
import { SPACING, BORDERS, TYPOGRAPHY, SHADOWS } from "@/lib/design-tokens";
// CORRECT
<div className={`gap-${SPACING.content}`}>
<h2 className={TYPOGRAPHY.h2.standard}>Title</h2>
</div>
// WRONG (would be rejected in code review)
<div className="gap-8">
<h2 className="text-3xl font-semibold">Title</h2>
</div>
Why this matters:
- Consistent spacing across all components
- Easy theme updates (change token, update everywhere)
- Design system as single source of truth
Test Coverage Philosophy
What we test:
- Component rendering (all variants)
- User interactions (clicks, expansions, hovers)
- Props validation (TypeScript + runtime)
- Accessibility (ARIA labels, keyboard nav)
- LocalStorage persistence
- Analytics event firing
What we don't test:
- Pure CSS rendering (snapshot tests are brittle)
- Browser API quirks (clipboard, scrolling)
- Third-party library internals
Result: 97% coverage on meaningful tests.
The 7 skipped tests:
- All in
section-share.test.tsx - All related to
navigator.clipboard.writeText() - Clipboard API is async and timing-dependent in jsdom
What we do instead:
- Test that the "Copy" button renders
- Test that onClick handler fires
- Test that success state updates
- Manual browser testing for actual clipboard functionality
Philosophy: Don't waste hours making jsdom mimic browser APIs perfectly. Test in the actual environment (browser).
Lessons Learned: What Worked, What Didn't
What Worked
GlossaryTooltip is MVP Readers engage with every single post that has tooltips. Average 18 clicks per visit. Low effort (1-2 min per term), high value.
TLDRSummary hooks Executives Executive readers (identified by User-Agent patterns) spend 3.2x longer on posts with TLDR vs. without. They want the summary upfront.
RoleBasedCTA segments audience Executive CTAs: "Schedule 15-min demo" Developer CTAs: "Clone the repo" Security CTAs: "Run the audit script" Each role clicks their CTA 3x more than generic CTAs.
SectionShare drives niche sharing 27% of shares are section-level. LinkedIn loves "just this section on zero-trust" more than "entire 6,000-word post."
What Didn't Work
CVE-specific components were overkill Built
SeverityLabel,CVELink,CVETrackerfor security posts. Zero usage across 4 posts. Deleted after 1 week. Lesson: Don't build components for hypothetical future use.Too many CollapsibleSections confuses readers First draft of OWASP post had 12 collapsibles. Readers didn't know what to expand. Reduced to 4-5 max per post.
KeyTakeaway every 300 words is too dense Initial guideline was "1 per section." Felt spammy. New rule: 1 every 400-500 words or 4-6 per post max.
Roadmap: What's Next for RIVET
Advanced Features
| Component | Purpose | Effort | Priority |
|---|---|---|---|
| SeriesNavigation | Series-specific prev/next navigation | 4h | High |
| RiskMatrix | SVG visualization for risk scoring | 8h | Medium |
| DownloadableAsset | Lead capture + Asset delivery | 6h | Medium |
| FAQSchema | FAQ accordion with schema.org markup | 3h | Low |
| NewsletterSignup | Inline email capture | 4h | Low |
| TabInterface | Multi-tab content switcher | 5h | Low |
Total: ~30 hours additional development
Tier 2 Rollout
- Select 3-4 Development-focused posts
- Apply P0 + selective P1 components
- measure engagement lift vs. Tier 1
Analytics Dashboard
Build internal dashboard tracking:
- Component usage across all posts
- Engagement metrics per component type
- ROI: Time invested vs. engagement gain
- A/B testing: Enhanced vs. baseline posts
Conclusion: Static is Dead, Long Live Interactive
The core insight: Technical blog posts compete with video, podcasts, and interactive courses. Static HTML from 2005 isn't enough anymore.
The solution: RIVET framework - systematic enhancement with 8 production components across 5 pillars.
The results: +69% time on page, +60% scroll depth, -24% bounce rate (early data, 2 weeks post-rollout).
The investment: 10 hours to build P0+P1 components, 2-3 hours per post to deploy 181 components across 4 posts, 97% test coverage.
The next step: Roll out to 3-4 more posts in Q1 2026, measure engagement lift, iterate based on data.
Don't wait for perfect. Pick your top 3 blog posts. Add a TLDRSummary and 5-10 GlossaryTooltips. Measure engagement after 2 weeks. Scale from there.
Your readers want interactivity. Your content deserves better than static HTML. RIVET is the framework that bridges the gap.
Now go build something interactive.
Resources
Phase 1: Setup (2-3 hours)
- Install dependencies:
framer-motion,lucide-react - Create
/components/blog/rivet/directory structure - Set up design token system (
SPACING,BORDERS,TYPOGRAPHY) - Configure barrel exports (
index.tsat each level) - Set up test infrastructure (Vitest + React Testing Library)
Phase 2: Build P0 Components (6-8 hours)
ReadingProgressBar (3h)
- Component implementation
- Tests (18 tests)
- Dark mode support
- Accessibility audit
KeyTakeaway (3h)
- 4 variant styles (insight/security/warning/tip)
- Tests (25 tests)
- Icon integration (lucide-react)
TLDRSummary (4h)
- Three-section layout
- Jump link functionality
- Tests (28 tests)
Phase 3: Build P1 Components (12-16 hours)
GlossaryTooltip (4h)
- Hover + click interaction
- LocalStorage persistence
- Tests (26 tests)
RoleBasedCTA (5h)
- Three role variants
- Analytics tracking
- Tests (32 tests)
SectionShare (4h)
- Twitter/LinkedIn/Copy buttons
- Clipboard API integration
- Tests (13/20 - 7 skipped)
CollapsibleSection (3h)
- Expand/collapse animation
- LocalStorage persistence
- Tests (26 tests)
Phase 4: Rollout to Posts (2-3 hours per post)
- Select 3-4 high-impact posts
- Add TLDRSummary at top
- Add ReadingProgressBar (automatic)
- Identify 4-6 spots for KeyTakeaway
- Mark 8-15 technical terms for GlossaryTooltip
- Add RoleBasedCTA (3 instances: Exec/Dev/Sec)
- Add SectionShare after major sections
- Add CollapsibleSection for optional depth
Phase 5: Measure & Iterate (Ongoing)
- Set up Vercel Analytics events
- Track component interactions
- Monitor engagement metrics (time on page, scroll depth, bounce rate)
- A/B test: Enhanced vs. baseline posts
- Gather reader feedback
- Iterate on component density
Total Time Investment: ~35-45 hours for full implementation + 4-post rollout
Footnotes
-
Stack Overflow Developer Survey 2025: https://survey.stackoverflow.co/2025/ β©
-
Vercel Analytics Documentation: https://vercel.com/docs/analytics β©
-
WCAG 2.1 AA Guidelines: https://www.w3.org/WAI/WCAG22/quickref/?versions=2.1 β©

Top comments (0)