Introduction
During a routine code review of the Zer0-Mistakes Jekyll theme, we discovered that the page-loading progress bar (nanobar) was implemented as ~60 lines of hardcoded HTML, CSS, and JavaScript inlined directly in head.html. Values like colors, heights, and animation steps were scattered across three files with no central configuration.
At the same time, a screenshot revealed that the footer’s dark section wasn’t reaching the viewport edges—it was constrained by nested Bootstrap .container classes that had accumulated over multiple PRs.
This article chronicles how both issues were diagnosed and fixed in a single AI-assisted development session.
🌟 Why This Matters
Component modularization and config-driven design are foundational practices for maintainable theme development. This session demonstrates:
- How to identify refactoring candidates — inline code with magic numbers is a code smell
- How to design config-driven components — using
_config.ymlas a single source of truth - How to diagnose CSS layout bugs — systematic elimination via DevTools and git history
- How AI agents approach UI debugging — screenshot analysis, DOM inspection, git archaeology
🎯 What You’ll Learn
- Refactor inline HTML/CSS/JS into a modular Jekyll include
- Bridge
_config.ymlvalues to CSS custom properties via Liquid - Diagnose nested Bootstrap container issues
- Verify changes with Docker-based Jekyll builds
📋 Before We Begin
Prerequisites:
- Jekyll 3.9+ or 4.x with a working Docker development environment
- Bootstrap 5.x integrated in your theme
- Basic Liquid template knowledge
Phase 1: Diagnosing the Nanobar Problem
🔍 The Existing State
The nanobar was spread across three files:
| File | What it contained |
|---|---|
head.html |
~60 lines: inline <style>, inline <script>, hardcoded config |
header.html |
Hardcoded <div class="nanobar" id="top-progress-bar"> inside the navbar |
nanobar.min.js |
Third-party library (with a stray P character prepended) |
Problems identified:
- No central configuration — changing the bar color required editing HTML
- Tight coupling — CSS, JS, and markup scattered across unrelated files
- Parse error — a stray
Pcharacter innanobar.min.jscaused a silent JS failure - No positional flexibility — the bar was hardcoded inside the navbar div
💡 The Design Decision
We chose a config-driven single-include pattern:
_config.yml (values) → nanobar.html (CSS + JS + bridge) → rendered page
One file owns the entire subsystem. All behavior is controlled by site.nanobar.* keys.
Phase 2: Implementing the Nanobar Refactoring
🏗️ Step 1: Create the Config Block
# _config.yml
nanobar:
enabled : true
color : "var(--bs-primary)"
background : "transparent"
height : "3px"
position : "navbar" # top | bottom | navbar
z_index : 9999
steps : [20, 55, 85, 100]
step_delay_ms : 180
classname : "nanobar"
id : "top-progress-bar"
target : ""
Every value that was previously hardcoded now lives here.
🔧 Step 2: Build the Include
_includes/components/nanobar.html contains three sections:
CSS custom properties — map config → CSS variables:
<style id="nanobar-theme">
:root {
--nanobar-color: var(--bs-primary);
--nanobar-bg: transparent;
--nanobar-height:3px;
--nanobar-z: 9999;
}
</style>
Config bridge — pass values to JS:
<script>
window.zer0Nanobar = {
position: "top",
steps: [20,55,85,100],
stepDelay: 0,
classname: "nanobar",
id: "top-progress-bar",
target: ""
};
</script>
JS loading — library + initializer:
<script defer src="/assets/js/nanobar.min.js"></script>
<script defer src="/assets/js/nanobar-init.js"></script>
⚡ Step 3: Create the Initializer
assets/js/nanobar-init.js reads window.zer0Nanobar and:
- Determines the mount target based on
position - Instantiates
new Nanobar(opts) - Runs the step animation on
DOMContentLoaded
🎯 Step 4: Update the Mount Point
In header.html, removed the old hardcoded bar and added a conditional mount:
### ✅ Step 5: Clean Up head.html
Replaced ~60 lines with one line:
```liquid
{% include components/nanobar.html %}
Phase 3: Diagnosing the Footer Problem
🔍 How the Issue Was Found
After completing the nanobar refactoring, a visual check revealed the footer’s dark section had visible gaps on both sides—the dark background didn’t reach the viewport edges.
🕵️ Root Cause Analysis
The AI agent used systematic elimination:
- Git archaeology —
git log --oneline -5 -- _includes/core/footer.htmlconfirmed the file wasn’t modified by the nanobar changes - CSS audit — searched nanobar styles for any selectors that could affect footer elements (none found)
- DOM inspection — identified the real cause: four levels of width-constraining containers:
<footer class="bd-footer container-xl border-top"> <!-- Level 1: max-width -->
<div class="container row my-3"> <!-- Level 2: max-width -->
<div class="container bg-dark text-light rounded-3"> <!-- Level 3: max-width -->
<div class="container"> <!-- Level 4: max-width -->
Each Bootstrap .container class adds max-width + auto margins, preventing edge-to-edge rendering.
Phase 4: Fixing the Footer
The Fix
<footer class="bd-footer border-top"> <!-- No container class -->
<div class="container-xl my-3"> <!-- Powered-by row, centered -->
<!-- powered-by content -->
</div>
<div class="bg-dark text-light py-5"> <!-- Full-width dark bg -->
<div class="container-xl"> <!-- Content centered inside -->
<!-- branding, links, social, subscribe -->
</div>
</div>
</footer>
Key changes:
- Removed
container-xlfrom<footer>— the footer element now spans full viewport width - Dark section uses
bg-darkat full width withcontainer-xlinside for content centering - Reduced nesting from 4 levels to 2
- Removed
rounded-3— edges should be flush, not rounded
✅ Validation
Build Verification
docker-compose exec -T jekyll bundle exec jekyll build \
--config '_config.yml,_config_dev.yml'
Automated Checks
# Nanobar include present
grep -c 'nanobar-init.js' _site/index.html # → 1
# Mount point renders
grep 'top-progress-target' _site/index.html # → div.nanobar-mount
# Footer structure correct
grep '<footer' _site/index.html # → class="bd-footer border-top"
Visual Verification
Screenshots confirmed:
- Nanobar renders as a thin blue strip under the navbar
- Footer dark section extends edge-to-edge
- No CSS side-effects on other components
🧠 Key Takeaways
- Config-driven beats hardcoded — a 22-line YAML block replaced 60+ lines of scattered HTML/CSS/JS
- CSS custom properties bridge config to styles — Liquid injects values at build time, CSS consumes them at render time
- Nested Bootstrap containers compound width constraints — each
.containeradds its ownmax-width - Git archaeology is a debugging tool —
git log -- <file>andgit diffhelp isolate when a bug was introduced - AI agents debug systematically — screenshot → DOM inspection → git history → CSS audit → targeted fix
🚀 Next Steps
📚 Further Learning
- Bootstrap 5 Container docs — understand
containervscontainer-fluidvscontainer-xl - CSS Custom Properties — bridging config to styles
- Jekyll Includes — modular template composition
🎯 Apply It Yourself
- Beginner: Find an inline
<style>block in your Jekyll theme and extract it into a separate include - Intermediate: Create a config-driven component where all values come from
_config.yml - Advanced: Build a component with multiple position modes (like the nanobar’s
top/bottom/navbar)