McGarrah Technical Blog

Jekyll Website Optimization for GitHub Pages - Part 2

11 min read

In Part 1 of this series, we covered the foundational optimizations for Jekyll sites on GitHub Pages. Part 2 dives into advanced analytics, performance monitoring, and the lessons learned from running a technical blog with 100+ posts.

Advanced Analytics and Monitoring

Google Analytics 4 Integration

Beyond basic page tracking, GA4 provides valuable insights for technical blogs:

<!-- Google tag (gtag.js) -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-F90DVB199P"></script>
<script>
  window.dataLayer = window.dataLayer || [];
  function gtag(){dataLayer.push(arguments);}
  gtag('js', new Date());
  gtag('config', 'G-F90DVB199P', {
    // Enhanced measurement for technical content
    enhanced_measurement_settings: {
      scrolls_enabled: true,
      outbound_clicks_enabled: true,
      site_search_enabled: true,
      video_engagement_enabled: true,
      file_downloads_enabled: true
    }
  });
</script>

Custom Event Tracking

Track specific interactions valuable for technical content:

// Track code copy button usage
document.addEventListener('click', function(e) {
  if (e.target.classList.contains('copy-button')) {
    gtag('event', 'code_copy', {
      event_category: 'engagement',
      event_label: 'code_snippet_copied'
    });
  }
});

// Track external link clicks
document.addEventListener('click', function(e) {
  if (e.target.tagName === 'A' && e.target.hostname !== window.location.hostname) {
    gtag('event', 'click', {
      event_category: 'outbound',
      event_label: e.target.href,
      transport_type: 'beacon'
    });
  }
});

Performance Monitoring

Key metrics to track for technical blogs:

// Core Web Vitals tracking
new PerformanceObserver((entryList) => {
  for (const entry of entryList.getEntries()) {
    if (entry.entryType === 'largest-contentful-paint') {
      gtag('event', 'web_vitals', {
        event_category: 'performance',
        event_label: 'LCP',
        value: Math.round(entry.startTime)
      });
    }
  }
}).observe({entryTypes: ['largest-contentful-paint']});

// Track page load performance
window.addEventListener('load', function() {
  const loadTime = performance.timing.loadEventEnd - performance.timing.navigationStart;
  gtag('event', 'page_load_time', {
    event_category: 'performance',
    value: Math.round(loadTime)
  });
});

Remaining Optimization Priorities

Based on analytics data and user feedback, here are the high-impact optimizations still on my roadmap:

High Priority Enhancements

1. Dark/Light Theme Toggle

User preference support with system detection:

// Theme toggle implementation
const themeToggle = document.getElementById('theme-toggle');
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)');

function setTheme(theme) {
  document.documentElement.setAttribute('data-theme', theme);
  localStorage.setItem('theme', theme);
  gtag('event', 'theme_change', {
    event_category: 'user_preference',
    event_label: theme
  });
}

// Initialize theme
const savedTheme = localStorage.getItem('theme') || 
  (prefersDark.matches ? 'dark' : 'light');
setTheme(savedTheme);

2. Site Search Implementation

Google Custom Search integration for 100+ posts:

<div class="search-container">
  <script async src="https://cse.google.com/cse.js?cx=YOUR_SEARCH_ENGINE_ID"></script>
  <div class="gcse-search"></div>
</div>

3. Reading Progress Indicator

Visual feedback for long technical articles:

window.addEventListener('scroll', function() {
  const winScroll = document.body.scrollTop || document.documentElement.scrollTop;
  const height = document.documentElement.scrollHeight - document.documentElement.clientHeight;
  const scrolled = (winScroll / height) * 100;
  
  document.getElementById('progress-bar').style.width = scrolled + '%';
});

Medium Priority Features

1. Breadcrumb Navigation

Especially important for categorized technical content:

<nav class="breadcrumb">
  <a href="/">Home</a>
  {% if page.categories %}
    {% for category in page.categories %}
      <span class="separator">›</span>
      <a href="/categories/{{ category | slugify }}/">{{ category | capitalize }}</a>
    {% endfor %}
  {% endif %}
  <span class="separator">›</span>
  <span class="current">{{ page.title }}</span>
</nav>

2. Social Share Buttons

Increase content distribution:

<div class="share-buttons">
  <a href="https://twitter.com/intent/tweet?url={{ site.url }}{{ page.url }}&text={{ page.title }}" 
     target="_blank" rel="noopener">
    Share on Twitter
  </a>
  <a href="https://www.linkedin.com/sharing/share-offsite/?url={{ site.url }}{{ page.url }}" 
     target="_blank" rel="noopener">
    Share on LinkedIn
  </a>
</div>

GDPR compliance for international readers:

function showCookieConsent() {
  const consent = localStorage.getItem('cookie-consent');
  if (!consent) {
    document.getElementById('cookie-banner').style.display = 'block';
  }
}

function acceptCookies() {
  localStorage.setItem('cookie-consent', 'accepted');
  document.getElementById('cookie-banner').style.display = 'none';
  // Initialize analytics after consent
  initializeAnalytics();
}

Technical Improvements

1. Custom Error Pages

Enhanced 404/500 pages with search functionality:

<!-- 404.html -->
---
layout: default
permalink: /404.html
---
<div class="error-page">
  <h1>Page Not Found</h1>
  <p>The page you're looking for doesn't exist. Try searching:</p>
  <div class="gcse-search"></div>
  
  <h3>Popular Articles</h3>
  {% for post in site.posts limit:5 %}
    <article>
      <a href="{{ post.url }}">{{ post.title }}</a>
      <p>{{ post.excerpt | strip_html | truncate: 100 }}</p>
    </article>
  {% endfor %}
</div>

2. Lazy Loading Implementation

Images load on demand to improve initial page load:

// Intersection Observer for lazy loading
const imageObserver = new IntersectionObserver((entries, observer) => {
  entries.forEach(entry => {
    if (entry.isIntersecting) {
      const img = entry.target;
      img.src = img.dataset.src;
      img.classList.remove('lazy');
      observer.unobserve(img);
    }
  });
});

document.querySelectorAll('img[data-src]').forEach(img => {
  imageObserver.observe(img);
});

3. Responsive Image Sets

Multiple image sizes for different devices:

<picture>
  <source media="(max-width: 480px)" srcset="/assets/images/image-small.webp">
  <source media="(max-width: 768px)" srcset="/assets/images/image-medium.webp">
  <source media="(min-width: 769px)" srcset="/assets/images/image-large.webp">
  <img src="/assets/images/image-large.jpg" alt="Description" loading="lazy">
</picture>

Performance Measurement Results

Before and After Metrics

Implementing the optimizations from both parts has yielded measurable improvements:

Page Load Performance

Core Web Vitals

User Engagement

Mobile Performance

Analytics Insights

Key findings from Google Analytics after optimizations:

Content Performance

Traffic Sources

  1. Proxmox/Ceph articles: Highest engagement and sharing
  2. Dell Wyse 3040 series: Strong search traffic
  3. Jekyll/GitHub Pages: Good for developer audience
  4. Networking tutorials: Consistent performance

Lessons Learned from 100+ Posts

What Worked Exceptionally Well

1. Consistent Technical Focus

Focusing on homelab and infrastructure topics created a loyal readership. Technical depth matters more than broad appeal for this audience.

2. Series-Based Content

Multi-part articles (like this one) perform better than single long posts:

3. Real-World Examples

Posts with actual command outputs and screenshots perform 40% better than theoretical content.

4. GitHub Integration

Using GitHub for comments, hosting, and workflows creates a seamless developer experience.

What Needed Adjustment

1. Mobile Experience

Initially underestimated mobile traffic (now 35% of visitors). Mobile optimization became crucial.

2. Search Functionality

With 100+ posts, site search became essential. Readers couldn’t find older content easily.

3. Content Organization

Tags and categories needed refinement as content volume grew. Initial organization didn’t scale well.

4. Performance at Scale

Image optimization and lazy loading became critical as the site grew.

Unexpected Discoveries

1. International Audience

40% of traffic comes from outside the US, making GDPR compliance and internationalization important.

2. Long-Tail SEO Success

Specific technical problems (like “Dell Wyse 3040 Proxmox”) drive consistent traffic months after publication.

3. Community Building

Technical readers prefer GitHub Discussions over traditional comments. The developer-focused audience appreciates the familiar interface.

4. Content Longevity

Technical tutorials have a much longer useful life than expected. Posts from 2023 still drive significant traffic.

Future Enhancement Roadmap

Short-term (Next 3 months)

Medium-term (3-6 months)

Long-term (6+ months)

Infrastructure Improvements

Conclusion

Running a technical blog on Jekyll and GitHub Pages has been incredibly rewarding. The platform’s flexibility allows for continuous optimization while maintaining simplicity.

Key Success Factors

  1. Focus on reader value: Every optimization should improve the reader experience
  2. Measure everything: Use analytics to guide optimization priorities
  3. Iterate continuously: Small, consistent improvements compound over time
  4. Embrace the platform: Work with Jekyll’s strengths rather than against them

Final Recommendations

For anyone running a technical blog on Jekyll:

Essential Optimizations

Performance Priorities

Content Strategy

The combination of Jekyll’s flexibility, GitHub’s reliability, and thoughtful optimization creates an excellent platform for technical content. The key is continuous improvement based on reader feedback and analytics data.

References

Categories: web-development, technical