prod@blog:~$

Converting the WordPress Theme to Blogger: A Complete Technical Deep Dive

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:


This is the improved mobile theme and the final result:


Then the same again in dark mode which works well on the mobile as well:


Finally on the mobile if you tap the hamburger icon you get a  mobile friendly menu to use:


This also shows the implementation of widgets set working in mobile mode:


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:

  1. Go to Theme → Click the arrow next to Customize
  2. Select Mobile Settings
  3. Choose "No. Show desktop theme on mobile devices"
  4. 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:posts loop 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: none rules
  • Ensure proper widget ordering in Layout tab

Challenge 3: Typography Inconsistencies

Problem: Default browser styles break minimalist design.

Solution:

  • Use !important flags judiciously for typography
  • Apply consistent font-weight: 400 globally
  • 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:

  1. Typography Unity: Force font-weight: 400 globally to eliminate platform defaults
  2. Data Enhancement: Use client-side scripting to fetch data the XML engine cannot provide
  3. Minimalist Borders: Single-sided borders with light colors for structural definition
  4. State Persistence: LocalStorage for user preferences that survive page loads
  5. Mobile-First Thinking: Responsive design that doesn't rely on Blogger's mobile templates
  6. Widget Flexibility: Proper section configuration to allow custom content
  7. 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.