Converting a WordPress theme to Blogger isn't just a matter of translating PHP to XML—it's about reimagining how minimalist design principles can survive within the constraints of a legacy platform. This technical guide documents the complete conversion of the Sten WordPress theme to Blogger, including every challenge encountered and the engineering solutions implemented.
The Sten theme represents peak minimalism: typography-focused, borderless design with subtle date formatting and clean post listings. Making this work on Blogger required fighting against the platform's default behaviors at every turn.
Visual Results
This shows the Sten theme running on Blogger with some enhancements to the original theme, including the the search box at the top and the option to switch between light and dark mode.
Then you can see the theme with "dark" mode enables which is persistent (more on that in the post)
The code blocks are also preserved as originally these code block were stripped of formatting:
Finally on the mobile if you tap the hamburger icon you get a mobile friendly menu to use:
Lets get into the deep dive with understanding the architectural differences:
WordPress Architecture:
- Language: PHP with MySQL database
- Structure: Multiple PHP files (header.php, footer.php, single.php)
- Styling: External CSS files, SASS/LESS preprocessing
- Data Access: Functions like
get_posts(),the_title(),the_content() - Flexibility: Complete control over HTML output
Blogger Architecture:
- Language: XML with proprietary Blogger tags
- Structure: Single monolithic XML file
- Styling: Embedded CSS within
<b:skin>tags - Data Access: Tags like
<data:post.title/>,<data:blog.url/> - Constraints: Platform-imposed limitations and default behaviors
Step 1: Analyzing the WordPress Theme Structure
The first step involves dissecting the WordPress theme to understand its components:
themes/
sten/
├── style.css // Main stylesheet
├── functions.php // Theme functions
├── index.php // Main template
├── header.php // Header partial
├── footer.php // Footer partial
├── single.php // Single post template
└── page.php // Page template
For the Sten theme, we identified:
- Minimalist design with focus on typography
- Date-based post listing (d.m format)
- No complex navigation menus
- Simple footer with search functionality
- Clean, borderless design aesthetic
Step 2: Creating the Blogger XML Structure
The Blogger template requires a specific XML structure that acts as the foundation:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE html>
<html b:version='2' xmlns='http://www.w3.org/1999/xhtml'
xmlns:b='http://www.google.com/2005/gml/b'
xmlns:data='http://www.google.com/2005/gml/data'
xmlns:expr='http://www.google.com/2005/gml/expr'>
This DOCTYPE declaration is crucial—it tells Blogger's parser how to interpret the template and enables the use of Blogger-specific tags.
Step 3: Converting the Styling
WordPress themes typically use external CSS files, but Blogger requires embedded styles within <b:skin> tags:
<b:skin><![CDATA[
/* All CSS must go here */
]]></b:skin>
Key conversions include:
- CSS Variables: Modern CSS custom properties work in both systems
- Responsive Design: Media queries transfer directly
- WordPress Classes:
.post,.entry-title→ Custom classes like.post-item,.post-title - Theme Customizer Variables: WordPress's
get_theme_mod()→ CSS variables
Step 4: Converting Template Logic
This is where the most significant translation occurs:
WordPress Loop
<?php if ( have_posts() ) : ?>
<?php while ( have_posts() ) : the_post(); ?>
<h2><?php the_title(); ?></h2>
<div><?php the_content(); ?></div>
<?php endwhile; ?>
<?php endif; ?>
Blogger Equivalent
<b:loop values='data:posts' var='post'>
<h2><data:post.title/></h2>
<div><data:post.body/></div>
</b:loop>
Step 5: Implementing Sections and Widgets
Blogger uses a section/widget architecture that's fundamentally different from WordPress:
<b:section class='main' id='main' showaddelement='yes'>
<b:widget id='Blog1' locked='true' type='Blog'>
<b:includable id='main'>
<!-- Widget content here -->
</b:includable>
</b:widget>
</b:section>
Key concepts:
- Sections: Container areas where widgets can be placed
- Widgets: Functional blocks (Blog posts, Header, HTML/JavaScript)
- Includables: Reusable template chunks within widgets
Critical Section Configuration
The showaddelement='yes' attribute is essential—it permits the Layout tab to inject multiple gadgets into the section. Without this, any HTML gadget added through Blogger's interface will be ignored by the rendering engine.
Step 6: Global Typography Synchronization (The "No-Bold" Logic)
A core challenge was the discrepancy between dates and titles. Browsers natively apply font-weight: 700 (bold) to h2 tags, making titles look heavy compared to the light, clean dates.
/* Forcing a unified "Date Style" across the entire site */
* {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif !important;
-webkit-font-smoothing: antialiased;
}
/* Killing the default bolding on post titles and pager links */
.post-title, .blog-pager a {
font-weight: 400 !important;
font-size: 1.5rem;
line-height: 1.2;
color: var(--text);
}
This ensures that the title and date share the exact same typographic profile, maintaining the minimalist aesthetic.
Step 7: Handling Data Access Patterns
WordPress Data Access
<?php echo get_the_date('d.m'); ?> // Date
<?php the_permalink(); ?> // Post URL
<?php the_author(); ?> // Author name
Blogger Data Access
<b:eval expr='data:post.timestamp format "dd.MM.yyyy"'/> <!-- Date -->
<data:post.url/> <!-- Post URL -->
<data:post.author/> <!-- Author -->
Step 8: Asynchronous Pager Logic (Dynamic Title Injection)
Blogger's XML tags (data:newerPageUrl and data:olderPageUrl) only provide URLs, not post titles. They default to generic "Newer Post" or "Older Post" text.
Since Blogger cannot "look ahead" server-side, I implemented a client-side scraping solution:
$(document).ready(function(){
var newerLink = $('#Blog1_blog-pager-newer-link');
var olderLink = $('#Blog1_blog-pager-older-link');
// Logic to fetch and inject the actual Post Title
if (newerLink.length) {
$.get(newerLink.attr('href'), function(data) {
var title = $(data).find('.post-title').first().text();
if(title) newerLink.text(title);
});
}
if (olderLink.length) {
$.get(olderLink.attr('href'), function(data) {
var title = $(data).find('.post-title').first().text();
if(title) olderLink.text(title);
});
}
});
This script visits the destination URL in the background, extracts the title from that page's HTML, and replaces the placeholder text on the current page.
Step 9: Background and Color State Management
To achieve a high-end minimalist look, I utilized CSS Custom Properties for centralized color control:
:root {
--background: #ffffff;
--text: #000000;
--tertiary: #f2f2f2; /* Light grey for thin separators */
}
[data-theme="dark"] {
--background: #1a1a1a;
--text: #ffffff;
--tertiary: #333333;
}
body {
background: var(--background);
color: var(--text);
}
The pure white background (#ffffff) emphasizes negative space, while variables ensure consistency across all components.
Step 10: State-Aware Dark Mode Persistence
A technical site requires a dark mode that doesn't "flicker." The script checks localStorage immediately upon page load:
function toggleTheme(){
const current = document.documentElement.getAttribute('data-theme');
const next = current === 'dark' ? 'light' : 'dark';
document.documentElement.setAttribute('data-theme', next);
localStorage.setItem('theme', next); // Ensures persistence
}
// On page load
document.addEventListener('DOMContentLoaded', function() {
const savedTheme = localStorage.getItem('theme') || 'light';
document.documentElement.setAttribute('data-theme', savedTheme);
});
Step 11: Managing Visual Noise (Single-Line Separator Logic)
Blogger widgets often inject multiple borders and margins, creating visual clutter. I explicitly disabled these and replaced them with a single clean separator:
.blog-pager {
display: flex;
justify-content: space-between;
padding: 40px 0;
margin-top: 40px;
border-top: 1px solid var(--tertiary) !important;
border-bottom: none !important; /* Removal of Blogger's 'ghost' line */
}
Step 12: Mobile Responsivity and Viewport Configuration
Desktop-first minimalism often fails on mobile. The horizontal relationship between date and title becomes a liability on narrow screens.
Viewport Meta Tag
<meta content='width=device-width, initial-scale=1, maximum-scale=1' name='viewport'/>
Responsive Breakpoints
@media screen and (max-width: 768px) {
/* Shift from horizontal to vertical flow */
.post-row {
flex-direction: column;
gap: 5px;
}
.post-date {
width: 100%;
font-size: 0.85rem;
}
}
This stacks the metadata (date) directly above the content (title), ensuring a clean vertical flow on mobile devices.
Step 13: Navigation Engineering (The Hamburger Toggle)
With multiple navigation links, a horizontal list on mobile would overflow. The solution: a hamburger toggle that maintains minimalist aesthetics:
function toggleMenu(){
const menu = document.getElementById('menu-list');
menu.classList.toggle('active'); // Swaps display between 'none' and 'flex'
}
.menu-list {
display: none;
}
.menu-list.active {
display: flex;
flex-direction: column;
}
Step 14: Conditional Rendering (Item vs. Index)
Blogger templates use conditional tags to separate views:
<b:if cond='data:blog.pageType == "index"'>
<!-- Homepage/Archive view -->
<div class='post-item'>...</div>
<b:elseif cond='data:blog.pageType == "item"'/>
<!-- Single post view -->
<div class='post-single'>...</div>
</b:if>
Elements placed inside the post-item loop but outside the pageType check will show on the homepage. Elements inside the check only appear on individual posts.
Step 15: Widget Visibility and Style Conflicts
Our minimalist port uses aggressive CSS resets that can accidentally hide Blogger's default elements:
The Problem
/* Global reset that might hide widgets */
* {
color: inherit !important;
}
#navbar-iframe, .quickedit, .admin-links {
display: none !important;
}
The Solution
Any HTML gadget added via Blogger's Layout tab needs specific classes to bypass the global reset:
.custom-html {
/* Widget-specific styles that override global resets */
color: initial;
font-weight: initial;
}
Step 16: Preformatted Text and Overflow Handling
Long lines of code can break the responsive layout. The solution:
pre, code {
overflow-x: auto;
white-space: pre;
display: block;
background: var(--tertiary);
padding: 1rem;
border-radius: 0.25rem;
}
This preserves code formatting while containing it within a scrollable box, maintaining the minimalist shell.
Step 17: Multi-Widget Section Configuration
To enable multiple widgets in a section:
<b:section class='main' id='main' showaddelement='yes' maxwidgets='10'>
<!-- Widgets can now be added via Layout tab -->
</b:section>
Positional Priority
In Blogger's Layout tab, drag gadgets above or below the Blog Posts widget. The order in the Layout tab is reflected on the live site because they share the same section ID.
Step 18: Mobile-Specific Overrides
Blogger's backend often filters out secondary widgets on mobile to save bandwidth. Override this behavior:
@media (max-width: 768px) {
.widget {
display: block !important;
}
}
Also add the mobile='yes' attribute to widgets:
<b:widget id='Blog1' locked='true' mobile='yes' type='Blog'>
Step 19: Search and Navigation Logic
The search bar and navigation were designed as "silent" interface elements:
.search-input {
flex: 1;
padding: 12px;
border: none;
background: var(--tertiary);
color: var(--text);
font-weight: 400 !important;
}
Step 20: Critical Mobile Settings
Essential Configuration Step:
- Go to Theme → Click the arrow next to Customize
- Select Mobile Settings
- Choose "No. Show desktop theme on mobile devices"
- Click Save
This ensures Blogger uses your custom responsive theme instead of its default mobile template.
Common Challenges and Solutions
Challenge 1: Posts Not Displaying
Problem: Posts don't appear after theme installation.
Solution:
- Ensure
data:postsloop is properly structured - Check that Blog widget has
mobile='yes'attribute - Verify section has proper ID and class attributes
- Confirm mobile settings are set to desktop theme
Challenge 2: Widgets Not Visible
Problem: HTML gadgets added via Layout don't appear.
Solution:
- Set
showaddelement='yes'on sections - Add specific CSS classes to bypass global resets
- Check for conflicting
display: nonerules - Ensure proper widget ordering in Layout tab
Challenge 3: Typography Inconsistencies
Problem: Default browser styles break minimalist design.
Solution:
- Use
!importantflags judiciously for typography - Apply consistent
font-weight: 400globally - Override browser defaults for headings
Challenge 4: Dark Mode Flickering
Problem: Theme flashes white before dark mode applies.
Solution:
- Check localStorage immediately on page load
- Set theme attribute before CSS loads
- Use inline critical CSS for initial theme state
Performance Optimizations
1. Minimize CSS
Keep styles concise within the <b:skin> tag to reduce parsing time.
2. Conditional Loading
Load resources only when needed:
<b:if cond='data:blog.pageType == "item"'>
<!-- Load comment system only on post pages -->
<script src='comment-system.js'/>
</b:if>
3. Optimize JavaScript
Combine multiple DOM queries and use event delegation where possible.
Testing Checklist
[ ] Posts display correctly on homepage[ ] Individual posts render properly
[ ] Dark mode persists across page loads
[ ] Mobile view shows all content
[ ] Search functionality works
[ ] Navigation menu toggles on mobile
[ ] Widgets added via Layout appear
[ ] Date formatting is consistent
[ ] Typography maintains weight consistency
[ ] Code blocks don't break layout
[ ] Pagination shows actual post titles
[ ] Theme works in preview mode
Conclusion
Converting the WordPress Sten theme to Blogger confirms that even legacy platforms can achieve high-end, minimalist design through careful engineering. The key principles that made this conversion successful:
- Typography Unity: Force
font-weight: 400globally to eliminate platform defaults - Data Enhancement: Use client-side scripting to fetch data the XML engine cannot provide
- Minimalist Borders: Single-sided borders with light colors for structural definition
- State Persistence: LocalStorage for user preferences that survive page loads
- Mobile-First Thinking: Responsive design that doesn't rely on Blogger's mobile templates
- Widget Flexibility: Proper section configuration to allow custom content
- Style Specificity: Targeted CSS that preserves minimalism while allowing functionality
While Blogger lacks WordPress's flexibility, this project demonstrates that with the right approach—combining XML manipulation, CSS engineering, and JavaScript enhancement—you can create a theme that rivals modern platforms in both aesthetics and functionality.
The minimalist philosophy isn't just about what you show; it's about what you remove. In Blogger's case, that meant stripping away years of accumulated defaults, legacy behaviors, and platform assumptions to reveal the clean, typography-focused experience that defines the Sten theme.