Howdy Jekyll Theme

A clean, modern, and minimalist Jekyll theme for personal websites and portfolios.

Gem Version Gem Downloads CI Jekyll Ruby License: MIT Lighthouse

Live Demo · Installation · Configuration · Contributing


Requirements

Dependency Version Required
Ruby >= 3.0 Yes
Jekyll >= 3.9 Yes
Bundler >= 2.4 Yes
Node.js >= 18 For Lighthouse CI only

Screenshots

Light Mode Dark Mode
Light mode Dark mode

Features

Feature Description
Gem-based Easy installation via RubyGems
Light & Dark Toggle with persistent preference, respects system preference
Responsive Desktop, tablet, and mobile optimized
Hero Carousel Touch/swipe-enabled, Swiper.js powered, fade transitions, autoplay
Projects Portfolio collection sorted by year with dedicated layouts
Blog jekyll-paginate-v2 powered with category color accents
SEO jekyll-seo-tag integration
RSS Automatic feed via jekyll-feed
TOC Auto-generated table of contents with configurable heading levels
Share Twitter, LinkedIn, Facebook, Reddit, Hacker News, WhatsApp, Telegram, Email, and copy-link buttons
Newsletter Mailchimp, Buttondown, or custom form support
Comments Giscus, Disqus, or Utterances
Analytics Google Analytics or Plausible
Company Logos Configurable marquee section
Fonts Local DM Sans variable font + Chaumont Script logo font. Configurable via fonts.primary and fonts.mono

Browser Compatibility

Browser Version Supported
Chrome 120+
Firefox 115+
Safari 16+
Edge 120+

Known Quirks

  • Firefox scrollbars: Uses scrollbar-color and scrollbar-width (native thin scrollbars). ::-webkit-scrollbar rules are ignored.
  • Safari scroll-margin-top: Fully supported in Safari 15+. Older versions may require padding-top workaround on headings.
  • Safari SVG loading="lazy": SVG images do not support lazy loading in Safari; they load eagerly regardless of attribute.

Automated Testing

This theme includes Playwright end-to-end tests covering:

Test Suite Coverage
nav.spec.js Hamburger toggle, ESC/overlay close, desktop hide
theme.spec.js Light/dark toggle, localStorage persistence, reload restore
carousel.spec.js Dot navigation, swipe gestures, slide dimensions
toc.spec.js Anchor scroll, scroll-margin offset, TOC presence
transitions.spec.js Page fade-out, external/hash link exclusion
npm test          # Run all tests
npm run test:ui   # Interactive UI mode

Installation

New Jekyll Site

jekyll new my-site --skip-bundle
cd my-site

Add the Theme

Add to your Gemfile:

gem "howdy-jekyll-theme"

Add to your _config.yml:

theme: howdy-jekyll-theme

Then install:

bundle config --local path .bundle
bundle install

Troubleshooting: If you run into permission issues or conflicts with system gems, run bundle config --local path .bundle before bundle install. This vendors all gems into the project's .bundle/ directory, keeping your environment isolated.

Existing Site

Remove any theme: and remote_theme: lines from _config.yml, delete _layouts, _includes, and _sass directories, then follow the steps above.

Configuration

Minimal Setup

# _config.yml
title: "Your Name"
description: "Your site description"
author: "Your Name"
email: "hi@yoursite.com"

url: "https://yoursite.com"
baseurl: ""

theme: howdy-jekyll-theme

plugins:
  - jekyll-seo-tag
  - jekyll-feed
  - jekyll-sitemap
  - jekyll-paginate-v2

collections:
  projects:
    output: true
    permalink: /projects/:slug/
    sort_by: year

pagination:
  enabled: true
  per_page: 5
  permalink: "/blog/page:num/"
  sort_reverse: true

Single image or multi-image carousel:

# Single image
hero_image: /assets/images/hero.png

# Carousel (2+ images enables touch/swipe)
hero_images:
  - /assets/images/hero-1.jpg
  - /assets/images/hero-2.jpg
  - /assets/images/hero-3.jpg
navigation:
  - title: "Home"
    url: "/"
  - title: "Projects"
    url: "/projects/"
  - title: "Blog"
    url: "/blog/"
  - title: "About"
    url: "/about"
social:
  github: "https://github.com/yourusername"
  twitter: "https://x.com/yourusername"
  linkedin: "https://linkedin.com/in/yourusername"
  instagram: "https://instagram.com/yourusername"
  dribbble: "https://dribbble.com/yourusername"

Homepage Hero

hero:
  headline: "I&nbsp;shape product&nbsp;strategy<br>&amp;&nbsp;design experiences<br><span class=\"text-muted\">at scale</span>"
  subtitle: "Your tagline or short description."
  cta_text: "View case studies"
  cta_url: "/projects/"

About Page

about_bio: "Your intro paragraph. <strong>HTML</strong> supported."
about_headline: "Building products that<br>sit at the intersection<br><span class=\"text-muted\">of design and intelligence</span>"
about_paragraph_2: "Your second paragraph."
about_paragraph_3: "Your third paragraph."
about_hero_image: "/assets/illustrations/designer.svg"
about_resume:
  enabled: true
  text: "Download Resume"
  url: "/assets/resume.pdf"

# Testimonials (shown on about page)
testimonials:
  - quote: "A great testimonial quote."
    name: "Jane Doe"
    role: "Title, Company"

Pagination

pagination:
  enabled: true
  per_page: 5
  permalink: "page:num/"
  sort_reverse: true
  prev_text: "Prev"
  next_text: "Next"

404 Page

page_404:
  title: "Page Not Found"
  message: "The page you're looking for doesn't exist or has been moved."
  button_text: "Go Home"
  button_url: "/"

Fonts

fonts:
  primary: "DM Sans"      # DM Sans, Inter, Plus Jakarta Sans, Instrument Sans, Space Grotesk, Work Sans, Outfit, Manrope, Sora
  mono: "Fragment Mono"   # Fragment Mono, JetBrains Mono, Fira Code

Company Logos Marquee

company_logos:
  enabled: true
  items:
    - name: "Company 1"
      width: 120
      url: "https://company1.com"
    - name: "Company 2"
      width: 100
      url: "https://company2.com"

Newsletter, Comments, Analytics

# Newsletter
newsletter:
  enabled: true
  provider: buttondown      # mailchimp, buttondown, custom
  action_url: "https://..." # form action URL
  title: "Stay updated"

# Comments
comments:
  enabled: false
  provider: giscus          # giscus, disqus, utterances
  giscus:
    repo: "user/repo"
    repo_id: "R_XXXXXXXX"
    category: "Announcements"
    category_id: "DIC_XXXXXXXX"

# Analytics
analytics:
  provider: plausible       # google, plausible
  plausible_domain: "yoursite.com"

Content Types

Projects

Create files in _projects/:

---
layout: project
title: "Project Name"
subtitle: "Brief description"
category: "Web Design"
year: "2025"
image: /assets/images/project.jpg
---
Project content with markdown...

Blog Posts

Create files in _posts/ with naming format YYYY-MM-DD-slug.md:

---
layout: post
title: "Post Title"
category: "Tutorials"      # Tutorials, Inspiration, Freebies, Interviews
image: /assets/images/post.jpg
---
Post content...

Tip: Use <!--more--> to define the excerpt shown on the blog index.

Customization

Override Styles

Create assets/css/main.scss in your site:

---
---
@use "variables";
@use "base";
@use "typography";
@use "layout";
@use "components";
@use "dark-mode";

// Your styles here

Override Partials

Copy any _includes/ or _layouts/ file into your site's directory with the same path. Jekyll will use your version instead of the theme's.

User Color Palette

Override the accent color in your _config.yml:

colors:
  accent: "#e63946"

Development

git clone https://github.com/howdyitskyle/howdy-jekyll-theme.git
cd howdy-jekyll-theme
bundle config --local path .bundle
bundle install
bundle exec jekyll serve

Visit http://localhost:4000.

Testing

bundle exec rake test             # HTML validation (links, images, scripts)
bundle exec rake validate_config  # Config field checks
bundle exec rake lighthouse       # Performance & accessibility audits
bundle exec rake all              # All of the above

Lighthouse CI asserts accessibility >= 0.9 across 5 pages.

Build the Gem

gem build howdy-jekyll-theme.gemspec
gem install ./howdy-jekyll-theme-1.0.0.gem

File Structure

├── _includes/          # 12 partials (nav, hero-carousel, toc, share, etc.)
├── _layouts/           # 7 layouts (default, home, page, post, project, blog, collection)
├── _sass/              # 7 stylesheets (variables, base, typography, layout, components, dark-mode, user-colors)
├── assets/
│   ├── css/            # main.scss, swiper-bundle.min.css
│   ├── js/             # hero-carousel.js, mobile-nav.js, theme-toggle.js, swiper-bundle.min.js
│   ├── fonts/          # ChaumontScript-Regular.otf, DMSans-Variable.ttf, DMSans-Italic-Variable.ttf
│   ├── images/
│   └── illustrations/
├── _projects/          # Demo portfolio items
├── _posts/             # Demo blog posts
├── blog/               # Blog index with pagination
├── lib/                # Gem entry point
├── _config.yml         # Fully documented configuration
├── Rakefile            # Test runner
└── lighthouserc.json   # Lighthouse CI assertions

Contributing

  1. Fork the repo
  2. Create a feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

Bug reports and feature requests are welcome at Issues.

License

This theme is available under the MIT License.

Acknowledgments