For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (
- [ ]) syntax for tracking.
Goal: Improve Lighthouse performance scores (mobile 51 -> ~70-75, desktop 78 -> ~85-90) by deferring render-blocking scripts, removing source maps from deployment, subsetting Google Fonts, and lazy-loading below-fold images.
Architecture: All changes are to HAML templates and git config. No SCSS, JS source, build pipeline, or infrastructure changes. The Gulp pipeline and Jenkins deploy are untouched.
Tech Stack: Awestruct 0.5 (HAML templates), Git
Spec: docs/superpowers/specs/2026-04-08-site-performance-optimization-design.md
Files:
- Modify: _layouts/base.html.haml:224 (jQuery script tag)
- Modify: _layouts/base.html.haml:229 (functions.min.js script tag)
defer to jQuery in base layoutIn _layouts/base.html.haml line 224, change:
haml
%script{:src => "https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"}
to:
haml
%script{:src => "https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js", :defer => "true"}
defer to functions.min.js in base layoutIn _layouts/base.html.haml line 229, change:
haml
%script{:src => "/js/functions.min.js"}
to:
haml
%script{:src => "/js/functions.min.js", :defer => "true"}
bash
git add _layouts/base.html.haml
git commit -m "perf: defer jQuery and functions.min.js in base layout"
Files:
- Modify: _layouts/home.html.haml:61 (jQuery script tag)
defer to jQuery in home layoutIn _layouts/home.html.haml line 61, change:
haml
%script{:src => "https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"}
to:
haml
%script{:src => "https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js", :defer => "true"}
Note: functions.min.js (line 68) and jquery.matchHeight-min.js (line 69) already have :defer => "true" in this layout.
bash
git add _layouts/home.html.haml
git commit -m "perf: defer jQuery in home layout"
With jQuery deferred, inline <script> blocks that call $() will break because inline scripts execute immediately when parsed, before deferred scripts run. Wrapping them in DOMContentLoaded works because deferred scripts execute before DOMContentLoaded fires.
Files:
- Modify: _layouts/home.html.haml:78-82 (matchHeight inline script)
- Modify: _layouts/base.html.haml:234-235 (page.javascript dynamic block)
- Modify: _layouts/base.html.haml:245-265 (customers page mixItUp inline script)
In _layouts/home.html.haml lines 78-82, change:
haml
:javascript
// Match Height Function for Community page
$(function() {
$('.equal-testimonials').matchHeight();
});
to:
haml
:javascript
document.addEventListener('DOMContentLoaded', function() {
$('.equal-testimonials').matchHeight();
});
In _layouts/base.html.haml lines 234-235, change:
haml
:javascript
#{page.javascript}
to:
haml
-if page.javascript
:javascript
document.addEventListener('DOMContentLoaded', function() { #{page.javascript} });
This handles _layouts/landing-event.html.haml which sets javascript: $(document).ready(function(){...}) in front matter. The inner $(document).ready() becomes redundant but harmless inside DOMContentLoaded.
In _layouts/base.html.haml lines 245-265, change:
```haml :javascript // Function to get query parameter by name function getParameterByName(name) { name = name.replace(/[[]/, "\[").replace(/[]]/, "\]"); var regex = new RegExp("[\?&]" + name + "=([^&#]*)"), results = regex.exec(location.search); return results === null ? "" : decodeURIComponent(results[1].replace(/+/g, " ")); }
// mixitup
$(function() {
var filterOnLoad = getParameterByName('filter') ? "." + getParameterByName('filter') : "all";
$('.customer-logos').mixItUp({
load: {
filter: filterOnLoad
}
});
if (filterOnLoad == '.case-study') {
$(".case-studies-accordion .accordion-content").toggleClass("active").removeClass("start-closed");
}
}); ```
to:
```haml :javascript // Function to get query parameter by name function getParameterByName(name) { name = name.replace(/[[]/, "\[").replace(/[]]/, "\]"); var regex = new RegExp("[\?&]" + name + "=([^&#]*)"), results = regex.exec(location.search); return results === null ? "" : decodeURIComponent(results[1].replace(/+/g, " ")); }
// mixitup
document.addEventListener('DOMContentLoaded', function() {
var filterOnLoad = getParameterByName('filter') ? "." + getParameterByName('filter') : "all";
$('.customer-logos').mixItUp({
load: {
filter: filterOnLoad
}
});
if (filterOnLoad == '.case-study') {
$(".case-studies-accordion .accordion-content").toggleClass("active").removeClass("start-closed");
}
}); ```
bash
git add _layouts/home.html.haml _layouts/base.html.haml
git commit -m "perf: wrap inline jQuery scripts in DOMContentLoaded for defer compat"
Files:
- Modify: .gitignore
- Remove from tracking: css/maps/
Append to .gitignore:
# CSS source maps (generated locally by Gulp, not needed in production)
css/maps/
bash
git rm -r --cached css/maps/
Expected output: lists css/maps/styles.css.map, css/maps/mmenu.css.map, and any subdirectory files as removed from tracking.
bash
git add .gitignore
git commit -m "perf: remove CSS source maps from git (saves 458KB from production)"
Files:
- Modify: _layouts/base.html.haml:11-14 (four Google Fonts references)
- Modify: _layouts/home.html.haml:15-18 (four Google Fonts references)
The current URL loads every weight 300-800 in both regular and italic. The SCSS only uses weights 400, 500, 600, 700, 800 with no italic.
Old URL: https://fonts.googleapis.com/css2?family=Open+Sans:ital,wght@0,300..800;1,300..800&display=swap
New URL: https://fonts.googleapis.com/css2?family=Open+Sans:wght@400;500;600;700;800&display=swap
In _layouts/base.html.haml lines 11-14, change:
haml
%link{rel: "preload", as: "style", href: "https://fonts.googleapis.com/css2?family=Open+Sans:ital,wght@0,300..800;1,300..800&display=swap"}
%link{rel: "stylesheet", href: "https://fonts.googleapis.com/css2?family=Open+Sans:ital,wght@0,300..800;1,300..800&display=swap", media: "print", onload: "this.onload=null;this.removeAttribute('media');"}
%noscript
%link{rel: "stylesheet", href: "https://fonts.googleapis.com/css2?family=Open+Sans:ital,wght@0,300..800;1,300..800&display=swap"}
to:
haml
%link{rel: "preload", as: "style", href: "https://fonts.googleapis.com/css2?family=Open+Sans:wght@400;500;600;700;800&display=swap"}
%link{rel: "stylesheet", href: "https://fonts.googleapis.com/css2?family=Open+Sans:wght@400;500;600;700;800&display=swap", media: "print", onload: "this.onload=null;this.removeAttribute('media');"}
%noscript
%link{rel: "stylesheet", href: "https://fonts.googleapis.com/css2?family=Open+Sans:wght@400;500;600;700;800&display=swap"}
In _layouts/home.html.haml lines 15-18, change:
haml
%link{rel: "preload", as: "style", href: "https://fonts.googleapis.com/css2?family=Open+Sans:ital,wght@0,300..800;1,300..800&display=swap"}
%link{rel: "stylesheet", href: "https://fonts.googleapis.com/css2?family=Open+Sans:ital,wght@0,300..800;1,300..800&display=swap", media: "print", onload: "this.onload=null;this.removeAttribute('media');"}
%noscript
%link{rel: "stylesheet", href: "https://fonts.googleapis.com/css2?family=Open+Sans:ital,wght@0,300..800;1,300..800&display=swap"}
to:
haml
%link{rel: "preload", as: "style", href: "https://fonts.googleapis.com/css2?family=Open+Sans:wght@400;500;600;700;800&display=swap"}
%link{rel: "stylesheet", href: "https://fonts.googleapis.com/css2?family=Open+Sans:wght@400;500;600;700;800&display=swap", media: "print", onload: "this.onload=null;this.removeAttribute('media');"}
%noscript
%link{rel: "stylesheet", href: "https://fonts.googleapis.com/css2?family=Open+Sans:wght@400;500;600;700;800&display=swap"}
bash
git add _layouts/base.html.haml _layouts/home.html.haml
git commit -m "perf: subset Google Fonts to 5 weights, drop italic"
Files:
- Modify: _partials/front_page/outcomes.html.haml:28,44,60 (3 customer logo images)
- Modify: _partials/front_page/scroller_compliance.html.haml:9,13 (2 compliance logo images)
- Modify: _partials/footer-home.html.haml:6,60 (footer logo + JASANZ logo)
In _partials/front_page/outcomes.html.haml:
Line 28 — change:
haml
%img.logo{:src => "/images/customers/tunedglobal.svg", :alt => "Tuned Global"}
to:
haml
%img.logo{:src => "/images/customers/tunedglobal.svg", :alt => "Tuned Global", :loading => "lazy"}
Line 44 — change:
haml
%img.logo{:src => "/images/customers/logo-boddle.svg", :alt => "Boddle"}
to:
haml
%img.logo{:src => "/images/customers/logo-boddle.svg", :alt => "Boddle", :loading => "lazy"}
Line 60 — change:
haml
%img.logo{:src => "/images/customers/parakeet-logo-pms-aero.svg", :alt => "Parakeet"}
to:
haml
%img.logo{:src => "/images/customers/parakeet-logo-pms-aero.svg", :alt => "Parakeet", :loading => "lazy"}
In _partials/front_page/scroller_compliance.html.haml:
Line 9 — change:
haml
%img(src="/images/ISO-27001-certified_col.svg" alt="ISO 27001 Certified" height="#{80}px" width="auto" data="no-retina" )/
to:
haml
%img(src="/images/ISO-27001-certified_col.svg" alt="ISO 27001 Certified" height="#{80}px" width="auto" data="no-retina" loading="lazy")/
Line 13 — change:
haml
%img(src="/images/news/jasanz.svg" alt="JASANZ Certified" height="#{80}px" width="auto" data="no-retina" )/
to:
haml
%img(src="/images/news/jasanz.svg" alt="JASANZ Certified" height="#{80}px" width="auto" data="no-retina" loading="lazy")/
In _partials/footer-home.html.haml:
Line 6 — change:
haml
%img{:align => "base2Services", :src => "/images/base2_white.svg", :height => "128", :width => "128", :alt => "base2Services - The Cloud Services People", :data => {:no_retina => ""}}/
to:
haml
%img{:align => "base2Services", :src => "/images/base2_white.svg", :height => "128", :width => "128", :alt => "base2Services - The Cloud Services People", :data => {:no_retina => ""}, :loading => "lazy"}/
Line 60 — the JASANZ logo in the footer already does NOT have lazy loading. Change:
haml
%img{:src => "/images/jasanz.svg", :alt => "JASANZ Certified", :height => "106", :width => "105", :data => {:no_retina => ""}, :style => "opacity: 0.98;", :class => "jasanz"}/
to:
haml
%img{:src => "/images/jasanz.svg", :alt => "JASANZ Certified", :height => "106", :width => "105", :data => {:no_retina => ""}, :style => "opacity: 0.98;", :class => "jasanz", :loading => "lazy"}/
bash
git add _partials/front_page/outcomes.html.haml _partials/front_page/scroller_compliance.html.haml _partials/footer-home.html.haml
git commit -m "perf: add lazy loading to below-fold images"
powershell
podman machine start
podman run --rm --name ruby -v ${PWD}:/home/dev/website base2/awestruct:Awestruct-0.5 -P development --force --verbose clean preview
Expected: Build completes without errors.
In a separate terminal:
powershell
docker run --rm -ti -p 8080:8080 --name http-server -v C:\Users\amari\development\base2website\_site:/public redsadic/docker-http-server
Open http://localhost:8080 and verify:
1. Homepage loads and renders correctly (typewriter animation works, menu works, accordions work)
2. Open DevTools > Network tab: confirm jQuery loads with defer attribute
3. Open DevTools > Network tab: confirm no .map files are requested
4. Navigate to /customers/?filter=case-study and verify filters work
5. Check footer renders correctly
In Chrome DevTools > Lighthouse tab, run audit for both mobile and desktop on http://localhost:8080. Compare scores to baseline (mobile: 51, desktop: 78).
In DevTools > Network tab, filter by "Font". Confirm only Open Sans regular weights (400, 500, 600, 700, 800) are requested — no italic variants.