When we set out to build 7 interactive calculators for Optistream — a French streaming analytics site — we made a deliberate choice: no React, no Vue, no frameworks. Just pure HTML, CSS, and vanilla JavaScript, embedded directly into WordPress pages.
Here's what we learned.
Why No Framework?
Our calculators needed to:
- Load instantly (no 200KB+ bundle)
- Work inside WordPress content areas
- Be maintainable by a small team
- Support LiteSpeed Cache without breaking
A framework would have been overkill. These are single-purpose tools: input some numbers, get results. The DOM manipulation is minimal, the state is simple, and the logic is pure math.
The 7 Calculators We Built
We built tools for streamers to calculate their potential earnings, subscription revenue, and platform comparisons:
- Twitch Sub Calculator — Estimate earnings from subs at different tiers
- Twitch Revenue Calculator — Full revenue breakdown (subs, bits, ads, sponsors)
- Revenue comparison tool (Twitch vs Kick vs YouTube)
- Bits-to-dollars converter
- Stream schedule optimizer
- Donation goal tracker
- Channel growth estimator
Each one is a self-contained block of HTML/CSS/JS inside a WordPress page.
Technical Challenges
1. The wpautop Problem
WordPress automatically wraps content in <p> tags and converts double line breaks into paragraphs. This is called wpautop, and it destroys inline JavaScript.
// WordPress turns this:
if (x > 0) {
calculate();
}
// Into this mess:
<p>if (x > 0) {</p>
<p> calculate();</p>
<p>}</p>
Our solutions:
- Wrap all JS in
<script>tags (wpautop skips script blocks) - Use a custom shortcode that disables wpautop for calculator blocks
- Minify everything to reduce line break opportunities
2. CSS Scoping Without Shadow DOM
When your CSS lives inside a WordPress page alongside theme styles, specificity wars are real. Our approach:
/* Prefix everything with a unique calculator ID */
#calc-twitch-subs .input-group { ... }
#calc-twitch-subs .result-display { ... }
#calc-twitch-subs button.calculate-btn { ... }
No BEM, no CSS modules — just good old ID-scoped selectors with enough specificity to win against theme defaults.
3. LiteSpeed Cache Compatibility
LiteSpeed Cache is aggressive. It caches everything, including pages with dynamic JavaScript. Our fixes:
- All calculations happen client-side (no AJAX calls to cache-bust)
- No cookies or session-dependent content
- Used
data-*attributes for initial values instead of server-rendered PHP - Added calculator pages to the LiteSpeed "Do Not Cache" list only as a last resort
4. Mobile-First Input Design
Streamers check their stats on their phones. Every calculator uses:
<input type="number" inputmode="numeric" pattern="[0-9]*"
min="0" step="1" placeholder="Enter subscriber count">
The inputmode="numeric" ensures the number pad opens on mobile — a small detail that massively improves UX.
Architecture Pattern
Each calculator follows the same structure:
<div id="calc-[name]" class="optistream-calculator">
<!-- Inputs -->
<div class="calc-inputs">
<label>Subscribers <input type="number" id="subs"></label>
<label>Tier <select id="tier">...</select></label>
</div>
<!-- Results -->
<div class="calc-results" id="results" style="display:none">
<div class="result-card">
<span class="label">Monthly Revenue</span>
<span class="value" id="revenue">$0</span>
</div>
</div>
<!-- Calculate Button -->
<button onclick="calculate()">Calculate</button>
</div>
<style>
#calc-[name] { /* scoped styles */ }
</style>
<script>
(function() {
// IIFE to avoid global scope pollution
function calculate() {
const subs = parseInt(document.getElementById("subs").value) || 0;
// ... math ...
document.getElementById("results").style.display = "block";
}
// Expose to onclick
document.querySelector("#calc-[name] button").onclick = calculate;
})();
</script>
Key Takeaways
- You don't always need a framework — For focused, interactive widgets, vanilla JS is faster to load and easier to maintain
- WordPress is hostile to inline JS — Plan for wpautop and cache plugins from day one
- Scope your CSS aggressively — ID-prefix everything when living inside a CMS
-
Mobile input UX matters —
inputmodeand propertypeattributes make a huge difference - IIFE pattern is your friend — Wrap each calculator in an IIFE to avoid variable collisions
Performance Results
- 0 KB of framework JS loaded
- < 5KB per calculator (HTML + CSS + JS combined)
- LiteSpeed PageSpeed score: 98/100 on calculator pages
- Time to Interactive: < 1s on 3G
Sometimes the best tool is no tool at all.
Built for Optistream — streaming tools and analytics for content creators.
Top comments (0)