Posts

Showing posts with the label static-interaction

customizing jekyll navigation menus for better user experience

Importance of Navigation in Website UX

Navigation menus are critical to guiding visitors through your website. A well-designed menu improves engagement, reduces bounce rates, and helps users find information quickly. Jekyll, being a static site generator, allows flexible customization of navigation through layouts, includes, and data files.

Basic Navigation Setup in Jekyll

At the simplest level, a navigation menu can be hardcoded in a layout or include file:


This approach works for small sites but quickly becomes cumbersome as pages grow.

Using Data Files for Dynamic Menus

Store menu items in a _data/navigation.yml file:

- title: "Home"
  url: "/"
- title: "About"
  url: "/about/"
- title: "Services"
  url: "/services/"
  children:
    - title: "SEO"
      url: "/services/seo/"
    - title: "Content Marketing"
      url: "/services/content-marketing/"
- title: "Blog"
  url: "/blog/"
- title: "Contact"
  url: "/contact/"

Render this in your layout or include with Liquid:

{% raw %}

{% endraw %}

Highlighting Active Pages

To improve UX, highlight the current page's navigation item. This can be done by comparing the page URL with menu URLs:

{% raw %}
  • {{ item.title }}
  • {% endraw %}

    Apply CSS styles to the .active class for visual feedback.

    Responsive Navigation

    Mobile-friendly menus are essential. Common techniques include:

    • Hamburger Menus: Hide navigation behind a toggle button on small screens.
    • Dropdowns: Use nested lists for submenus with hover or click triggers.
    • CSS Flexbox/Grid: Create flexible layouts that adjust based on screen size.

    Example: Simple Hamburger Toggle with JavaScript

    
    
    
    

    Accessibility Considerations

    • Use semantic HTML elements like <nav>.
    • Ensure keyboard navigation works for dropdowns and toggles.
    • Use ARIA attributes where necessary (e.g., aria-expanded).

    Case Study: E-Commerce Site Navigation

    An online store revamped their Jekyll navigation to use data-driven menus with nested categories. This allowed marketing teams to easily update categories without touching code, improved user retention by 20%, and lowered bounce rates on product pages.

    Tips for Managing Navigation at Scale

    • Use clear naming conventions for menu item keys and URLs.
    • Keep menus concise to avoid overwhelming users.
    • Test navigation across devices and screen sizes regularly.
    • Leverage includes for reusable navigation components.

    Conclusion

    Customizing navigation menus in Jekyll with data files and templates leads to a more manageable, scalable, and user-friendly site. Thoughtful UX design combined with clean code improves both visitor satisfaction and SEO performance.

    leveraging data files for dynamic content in jekyll

    Introduction to Data-Driven Content in Jekyll

    Jekyll is traditionally a static site generator, but its ability to consume data files allows you to inject dynamic content at build time. Using YAML, JSON, or CSV data files stored in the _data directory, you can create templates that pull from structured data sources, greatly enhancing content maintainability and flexibility.

    Why Use Data Files in Jekyll?

    • Centralized Content Management: Manage repetitive or related content in a single file.
    • Consistency: Avoid duplicate content entry by referencing a common data source.
    • Scalability: Easily add or update items without touching template code.
    • SEO Benefits: Structured data can improve semantic markup and search appearance.

    Setting Up Data Files

    Create a _data folder in your Jekyll site root. Add YAML (.yml), JSON (.json), or CSV (.csv) files. For example:

    _data/products.yml
    

    Example content for products.yml:

    - name: "SEO Audit Service"
      price: 299
      features:
        - "Comprehensive site analysis"
        - "Competitor benchmarking"
        - "Actionable report"
    - name: "Content Marketing Package"
      price: 499
      features:
        - "Monthly blog posts"
        - "Social media promotion"
        - "Monthly analytics"
    

    Accessing Data in Templates

    You can iterate over data entries with Liquid templating in your pages or includes:

    {% raw %}
    
      {% for product in site.data.products %}
    • {{ product.name }}

      Price: ${{ product.price }}

        {% for feature in product.features %}
      • {{ feature }}
      • {% endfor %}
    • {% endfor %}
    {% endraw %}

    Use Cases for Data-Driven Content

    • Service or Product Listings: Centralize product info for easy updates.
    • Team Member Profiles: Showcase bios and roles from a single YAML file.
    • FAQs: Manage question-answer pairs efficiently.
    • Pricing Tables: Generate tables dynamically without repetitive markup.

    SEO Advantages of Data Files

    When combined with proper semantic HTML, data-driven content can help search engines better understand your site structure and offerings. For instance, generating schema.org structured data dynamically improves rich snippet eligibility.

    Example: Generating JSON-LD from Data

    {% raw %}
    
    {% endraw %}
    

    Best Practices

    • Keep data files organized: Use descriptive filenames and folder structures.
    • Validate data formats: Avoid errors by ensuring YAML/JSON syntax correctness.
    • Use includes: Create reusable templates to render data uniformly.
    • Version control: Keep data files under git to track changes.

    Case Study: Dynamic Pricing Page

    A SaaS startup built their pricing page entirely from YAML data files. This allowed marketing to update packages without developer intervention. The site maintained consistent design and SEO-friendly markup, reducing update time by 70%.

    Conclusion

    Leveraging data files in Jekyll enables content teams to manage large or repetitive content efficiently, while preserving SEO and design consistency. This data-driven approach is a powerful technique to scale static sites with dynamic flexibility.

    Understanding Navigation Breadcrumbs in Jekyll

    Navigation breadcrumbs are an essential UX feature that helps visitors understand their current location within a website’s structure and easily navigate back to previous pages. For static site generators like Jekyll, implementing breadcrumbs effectively can boost usability and reduce bounce rates.

    Why Customize Breadcrumbs?

    • Improved User Navigation: Customized breadcrumbs can reflect your site’s hierarchy accurately, making navigation intuitive.
    • Better SEO: Search engines appreciate structured navigation, which can enhance site indexing.
    • Consistent Branding: Styling breadcrumbs to match your site’s design improves visual coherence.

    Case Study: How Custom Breadcrumbs Improved User Engagement

    In a recent project, a tech blog implemented tailored breadcrumbs on their Jekyll site. Before customization, users reported difficulties in tracing back their navigation path, leading to increased exit rates on category pages.

    After deploying a breadcrumb system that dynamically reflected the content hierarchy and applied clear styling, the site observed a 20% increase in page views per session and a 15% reduction in bounce rates over three months.

    Analyzing User Behavior Before and After

    • Before: Visitors often landed on detailed posts without context, missing easy access to category pages.
    • After: Breadcrumbs provided clear paths to higher-level categories, encouraging further exploration.

    Step-by-Step Guide to Customize Breadcrumbs in Jekyll

    1. Define Site Hierarchy Using Jekyll Data Files

    Start by creating a structured data file (_data/navigation.yml) that represents your site’s pages and their relationships.

    • Use YAML syntax to define categories, subcategories, and pages.
    • This structure enables dynamic breadcrumb generation based on page context.

    2. Create a Breadcrumb Include

    Build a reusable breadcrumb snippet in _includes/breadcrumb.html that reads the current page’s path and outputs the navigation trail.

    • Leverage Liquid tags to loop through the hierarchy and generate links.
    • Ensure the current page is displayed as plain text, without a link.

    3. Add Breadcrumbs to Your Layouts

    Include the breadcrumb snippet in your default layout, typically right below the main header or page title.

    4. Style Breadcrumbs for Clarity and Consistency

    Use CSS to make breadcrumbs visually distinct but unobtrusive.

    • Employ separators such as arrows or slashes.
    • Match font and colors to your site’s theme.
    • Ensure responsive design for mobile users.

    Advanced Techniques for Breadcrumb Customization

    Dynamic Breadcrumbs Based on Front Matter

    Utilize front matter variables like category and parent to dynamically construct breadcrumbs without manual updates to navigation data files.

    Multilingual Breadcrumbs

    If your site supports multiple languages, generate breadcrumbs that adapt to the current locale, enhancing usability for international audiences.

    Breadcrumb Schema Markup

    Integrate Schema.org breadcrumb structured data to improve search engine understanding and potentially enhance search result snippets.

    Common Challenges and How to Overcome Them

    Handling Deeply Nested Pages

    When your site structure grows complex, breadcrumbs can become unwieldy. Limit breadcrumb length or collapse intermediate levels to maintain usability.

    Maintaining Breadcrumb Accuracy

    Regularly update your navigation data files or front matter to reflect new pages or changed hierarchy. Automate validation during your build process where possible.

    Conclusion

    Customizing Jekyll navigation breadcrumbs is a powerful way to enhance user experience and site navigation. By carefully structuring your site hierarchy, creating dynamic breadcrumb components, and styling them thoughtfully, you can significantly improve how visitors interact with your content.

    As demonstrated in the case study, the impact extends beyond UX into measurable engagement improvements. Implementing these practices will ensure your Jekyll site is both user-friendly and optimized for search engines.

    custom pagination for improved user experience in jekyll

    Why Customize Pagination in Jekyll

    Pagination plays a crucial role in how visitors navigate through your blog posts. Default pagination often lacks flexibility and UX polish. Customizing pagination improves site usability by providing clearer navigation cues and faster access to content, which can decrease bounce rates and increase page views.

    Goals for Custom Pagination

    • Make navigation intuitive and easy to use
    • Keep the design consistent with your blog’s theme
    • Ensure responsiveness across all devices
    • Improve SEO with semantic markup

    Step 1: Understanding Default Jekyll Pagination

    Jekyll uses the jekyll-paginate plugin by default, which generates simple numbered page links. However, this default setup is minimalistic and may not offer the best user experience or visual appeal.

    Step 2: Creating a Custom Pagination Layout

    To build a better pagination system, create a custom pagination include file:

    {% raw %}
    
    
    {% endraw %}

    Step 3: Styling the Pagination

    Next, add CSS to style the pagination controls elegantly:

    
    .pagination {
      display: flex;
      align-items: center;
      justify-content: center;
      gap: 1rem;
      margin: 2rem 0;
      font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
    }
    
    .pagination-previous,
    .pagination-next {
      padding: 0.5rem 1rem;
      background-color: #007acc;
      color: white;
      border-radius: 5px;
      text-decoration: none;
      transition: background-color 0.3s ease;
    }
    
    .pagination-previous.disabled,
    .pagination-next.disabled {
      background-color: #cccccc;
      pointer-events: none;
      cursor: default;
    }
    
    .pagination-previous:hover:not(.disabled),
    .pagination-next:hover:not(.disabled) {
      background-color: #005fa3;
    }
    
    .pagination-list {
      list-style: none;
      display: flex;
      gap: 0.5rem;
      padding: 0;
      margin: 0;
    }
    
    .pagination-link {
      display: block;
      padding: 0.5rem 0.75rem;
      border-radius: 5px;
      text-decoration: none;
      color: #007acc;
      border: 1px solid transparent;
      transition: background-color 0.3s ease, border-color 0.3s ease;
    }
    
    .pagination-link:hover {
      background-color: #e6f2ff;
      border-color: #007acc;
    }
    
    .pagination-link.is-current {
      background-color: #007acc;
      color: white;
      pointer-events: none;
      font-weight: bold;
    }
    

    Step 4: Adding the Pagination to Your Blog

    Include the custom pagination snippet in your blog’s post listing template (usually index.html or archive.html):

    {% raw %}
    {% include custom-pagination.html %}
    {% endraw %}

    Step 5: Testing and Mobile Responsiveness

    Test your pagination on various screen sizes. Add media queries as needed to ensure usability on mobile devices:

    
    @media (max-width: 600px) {
      .pagination {
        flex-direction: column;
        gap: 0.5rem;
      }
    
      .pagination-list {
        flex-wrap: wrap;
        justify-content: center;
      }
    }
    

    SEO and Accessibility Tips

    • Use aria-label and aria-current attributes for screen readers
    • Ensure focus styles for keyboard navigation
    • Use semantic HTML elements like <nav> and <ul> for better SEO

    Conclusion

    Custom pagination enhances navigation and user experience on your Jekyll blog hosted on GitHub Pages. With simple Liquid templating and CSS, you can provide a professional and accessible browsing interface that encourages visitors to explore more of your content.

    customizing github pages search for jekyll mediumish

    Introduction to Client-Side Search Customization

    Adding a customized search feature on your Jekyll-powered GitHub Pages site improves user experience by allowing visitors to quickly find relevant content without page reloads. This guide walks you through tailoring the client-side search to suit the Mediumish template design.

    Key Objectives

    • Integrate a lightweight JavaScript search engine
    • Match search UI style with Mediumish aesthetics
    • Ensure fast indexing of posts for real-time results

    Step 1: Setting Up the Search Index

    Use Jekyll's build process to create a JSON index of posts. Add the following snippet in your _layouts/default.html or a dedicated partial:

    {% raw %}
    
    {% endraw %}

    Step 2: Implementing the Search Input and Results Container

    Add a search input box and results container in your sidebar or header:

    
    

      Step 3: JavaScript for Real-Time Filtering

      Use a simple JavaScript function to filter posts from the JSON index as users type:

      
      document.addEventListener('DOMContentLoaded', function () {
        const input = document.getElementById('search-input');
        const results = document.getElementById('search-results');
        const indexScript = document.getElementById('search-index');
        const posts = JSON.parse(indexScript.textContent);
      
        input.addEventListener('input', function () {
          const query = this.value.toLowerCase();
          results.innerHTML = '';
          if (query.length < 3) return;
      
          const filtered = posts.filter(post => 
            post.title.toLowerCase().includes(query) ||
            post.content.toLowerCase().includes(query)
          );
      
          filtered.slice(0, 5).forEach(post => {
            const li = document.createElement('li');
            const a = document.createElement('a');
            a.href = post.url;
            a.textContent = post.title;
            li.appendChild(a);
            results.appendChild(li);
          });
        });
      });
      

      Step 4: Styling the Search Interface

      Apply styles to fit the Mediumish look and improve usability:

      
      #search-wrapper {
        position: relative;
        margin-bottom: 2rem;
      }
      
      #search-input {
        width: 100%;
        padding: 0.5rem 1rem;
        border: 1px solid #ddd;
        border-radius: 4px;
        font-size: 1rem;
      }
      
      #search-results {
        margin-top: 0.5rem;
        list-style: none;
        padding: 0;
      }
      
      #search-results li {
        padding: 0.5rem 1rem;
        border-bottom: 1px solid #eee;
      }
      
      #search-results li a {
        text-decoration: none;
        color: #333;
      }
      
      #search-results li:hover,
      #search-results li a:focus {
        background-color: #f0f0f0;
        outline: none;
      }
      

      Accessibility Tips

      • Use aria-label on inputs for screen readers
      • Ensure results container uses aria-live="polite" for dynamic updates
      • Make sure keyboard navigation works smoothly

      Conclusion

      Customizing client-side search for your Jekyll GitHub Pages site using the Mediumish template enhances content discoverability and user engagement. By combining JSON indexing, lightweight JavaScript, and thoughtful styling, your blog visitors will find information faster and stay longer.

      Building a Personal Reading Dashboard in Jekyll

      What Is a Reading Dashboard?

      A reading dashboard offers users a personalized overview of their reading history, progress through post series, and suggestions on what to read next. This feature is common in large content platforms but rarely seen on static blogs — especially those built with Jekyll. In this guide, you'll learn how to build one from scratch using only client-side tools.

      Why It Matters for Static Blogs

      Since Jekyll is a static site generator, implementing user-specific features like dashboards can seem out of scope. But with creative use of localStorage, JavaScript, and well-structured front matter, we can simulate dynamic, personalized behavior entirely in the browser.

      Design Goals for the Dashboard

      • No backend or server-side database
      • Lightweight and privacy-respecting
      • Client-only logic using localStorage
      • Data pulled from existing Jekyll collections or posts
      • Easy to integrate into any Jekyll theme (especially Mediumish)

      Step 1: Recording Read Posts in localStorage

      We begin by modifying your post layout to register each time a user views a post. Add this to your post.html layout:

      <script>
      (function() {
        const key = 'jekyll_read_posts';
        const slug = '{{ page.url | replace:'/','-' | escape }}';
      
        let data = JSON.parse(localStorage.getItem(key) || '[]');
      
        if (!data.includes(slug)) {
          data.push(slug);
          localStorage.setItem(key, JSON.stringify(data));
        }
      })();
      </script>
      

      This stores an array of post URLs (or slugs) that the user has visited. It requires no backend or cookies.

      Step 2: Setting Up the Dashboard Page

      Create a new page called dashboard.md or dashboard.html in your root folder:

      ---
      layout: page
      title: "Your Reading Dashboard"
      permalink: /dashboard/
      ---
      
      <h2>Welcome Back</h2>
      <div id="dashboard-container"></div>
      
      <script>
      (async function() {
        const readPosts = JSON.parse(localStorage.getItem('jekyll_read_posts') || '[]');
        const postMap = {
          {% for post in site.posts %}
            '{{ post.url | replace:'/','-' }}': {
              title: '{{ post.title | escape }}',
              url: '{{ post.url }}',
              date: '{{ post.date | date: "%Y-%m-%d" }}'
            },
          {% endfor %}
        };
      
        const container = document.getElementById('dashboard-container');
      
        if (readPosts.length === 0) {
          container.innerHTML = '<p>No posts read yet. Start exploring the blog!</p>';
          return;
        }
      
        container.innerHTML = '<h3>Posts You’ve Read:</h3><ul>' + 
          readPosts.map(slug => {
            const post = postMap[slug];
            return post ? `<li><a href="${post.url}">${post.title}</a> <small>(${post.date})</small></li>` : '';
          }).join('') + 
          '</ul>';
      })();
      </script>
      

      This creates a client-side dashboard that lists the posts the user has already read, pulled dynamically from site.posts.

      Step 3: Suggesting Next Posts

      You can extend the dashboard with suggestions using the related_posts logic, your series data, or simply the most recent unread posts.

      <script>
      const suggestions = Object.keys(postMap)
        .filter(slug => !readPosts.includes(slug))
        .slice(0, 5)
        .map(slug => {
          const post = postMap[slug];
          return `<li><a href="${post.url}">${post.title}</a></li>`;
        });
      
      container.innerHTML += '<h3>Suggested Reads:</h3><ul>' + suggestions.join('') + '</ul>';
      </script>
      

      Step 4: Enhancing UX

      • Show progress bars or completion indicators for post series
      • Add filters like “Read this week” or “Unread only”
      • Style with theme-consistent cards or badges

      Benefits of This Approach

      • No personal data stored or sent
      • Fully offline-capable
      • No third-party trackers or cookies
      • Data persists across sessions (unless browser is cleared)

      Limitations

      • localStorage is per-device and per-browser
      • Cannot sync across multiple devices
      • No analytics unless explicitly logged via opt-in (e.g., GitHub Issues)

      Conclusion

      Even in a purely static Jekyll site, you can offer personalized reading experiences with zero backend. A personal reading dashboard is a low-effort, high-impact enhancement for your blog, especially if you publish content in series or categories.

      What’s Next?

      In the next part of this series, we’ll explore how to combine this dashboard with client-side search to help users find their most relevant next article — boosting engagement without leaving the JAMstack philosophy.

      Tracking Post Series Engagement in Jekyll Without a Backend

      Why Track Post Series Navigation?

      After building contextual navigation between posts in a series, the next step is understanding how readers engage with that flow:

      • Are they completing the entire series?
      • Do they drop off at a particular part?
      • Which parts are revisited?

      Traditional analytics tools like Google Analytics may feel like overkill or may not offer fine-grained, privacy-friendly insights. Let's explore a lightweight, backend-free approach tailored for Jekyll sites.

      Step 1: Use localStorage to Track Progress

      The simplest method is to record which parts a user has read using localStorage. Here’s a JavaScript snippet you can include in your layout:

      <script>
      (function() {
        const key = 'jekyll_series_progress';
        const series = "{{ page.series.name | escape }}";
        const part = "{{ page.series.part }}";
      
        if (series && part) {
          let progress = JSON.parse(localStorage.getItem(key) || '{}');
      
          if (!progress[series]) {
            progress[series] = [];
          }
      
          if (!progress[series].includes(part)) {
            progress[series].push(part);
          }
      
          localStorage.setItem(key, JSON.stringify(progress));
        }
      })();
      </script>
      

      This script stores the user's read parts for each series as an object. You can use this later to display a dynamic "Your Progress" UI.

      Step 2: Show Reading Progress Visually

      To make this data useful, render a visual progress bar. Add this near your series navigation box:

      <script>
      (function() {
        const key = 'jekyll_series_progress';
        const series = "{{ page.series.name | escape }}";
        const totalParts = {{ site.data.series[page.series.name].size }};
        const progress = JSON.parse(localStorage.getItem(key) || '{}');
      
        if (progress[series]) {
          const readParts = progress[series].length;
          const percent = Math.round((readParts / totalParts) * 100);
      
          const progressEl = document.getElementById('series-progress');
          if (progressEl) {
            progressEl.innerHTML = `You’ve read ${readParts} of ${totalParts} parts (${percent}%)`;
          }
        }
      })();
      </script>
      
      <div id="series-progress"></div>
      

      Step 3: Optional – Log Data to GitHub Issues

      If you'd like to log anonymous progress data publicly without a backend, you can use GitHub's Issues API. You’ll need:

      • A GitHub personal access token (with public_repo scope)
      • A dedicated repository for tracking logs

      Example of sending an anonymous log via GitHub's API:

      
      fetch('https://api.github.com/repos/YOUR_USERNAME/YOUR_REPO/issues', {
        method: 'POST',
        headers: {
          'Authorization': 'token YOUR_PERSONAL_ACCESS_TOKEN',
          'Content-Type': 'application/json'
        },
        body: JSON.stringify({
          title: `Series Read: ${series}, Part ${part}`,
          body: `Anonymous user read part ${part} of series ${series} at ${new Date().toISOString()}.`,
          labels: ['engagement', 'series-tracking']
        })
      });
      

      Note: This should be used sparingly and not on every page load. Consider adding a “submit feedback” button instead that triggers the logging manually.

      Step 4: Privacy Considerations

      This approach uses:

      • localStorage for client-side persistence (no network transmission)
      • GitHub Issues as an optional, user-controlled method to share engagement (fully transparent)

      No personal data is captured unless you explicitly ask for it or link GitHub accounts to readers (which is not recommended for anonymous tracking).

      Advanced Enhancements

      • Trigger a confetti or badge once a series is completed
      • Show estimated time remaining based on unread parts
      • Store and retrieve series bookmarks or annotations (via cookies or IndexedDB)

      Conclusion

      Tracking series engagement in Jekyll doesn't require Google Analytics or third-party scripts. By leveraging localStorage, you can offer meaningful user feedback, enhance UX, and maintain privacy—all while staying fully static and GitHub Pages–friendly.

      Next Steps

      In the next article, we’ll dive into building a personal reading dashboard for returning visitors using localStorage and JavaScript—think of it as a "Continue Where You Left Off" for static blogs.