This is a Jekyll-based personal portfolio website for Simon van Dyk. The site showcases writing, speaking engagements, and photography. It’s a clean, minimalist design with a focus on performance, accessibility, and responsive design patterns.
Repository: simonvandyk.info
Build Tool: Jekyll 4.x (GitHub Pages)
Language: Ruby 3.2.3
Hosting: GitHub Pages
Domain: simonvandyk.info
simonvandyk.info/
├── _layouts/ # Jekyll layout templates (4 files)
│ ├── post.html # Post/article pages with header/footer
│ ├── album.html # Photo album pages with gallery
│ ├── basic.html # General page layout
│ └── error.html # 404/500 error pages
│
├── _includes/ # Reusable template components
│ ├── head.html # Common <head> content
│ ├── critical-css.html # Inlined CSS for above-the-fold
│ ├── image-viewer.html # Lightbox modal with JS (282 lines)
│ ├── gallery-controls.html # Image size slider component
│ ├── post-sign-off.html # Post footer signature
│ ├── anonymous-analytics.html # Analytics script
│ └── components/ # Reusable UI components
│ ├── nav-links.html
│ ├── footer.html
│ ├── intro-blurb.html
│ ├── feed-item.html
│ ├── figure.html
│ ├── button.html
│ ├── icon.html
│ ├── profile-mark.html
│ ├── decorative-mark.html
│ ├── title-mark.html
│ └── design-system/ # Design documentation components
│ ├── color-swatch.html
│ ├── contrast-example.html
│ ├── size-example.html
│ └── text-style-example.html
│
├── assets/ # Static assets
│ ├── css/
│ │ ├── main.css # Main stylesheet (imports all)
│ │ ├── tokens.css # Design tokens (colors, typography, spacing)
│ │ ├── typography.css # Text style classes
│ │ ├── reset.css # CSS reset (based on Josh Comeau's reset)
│ │ ├── fonts.css # Font face declarations
│ │ ├── layout.css # Body/layout spacing
│ │ ├── form.css # Form element styles
│ │ ├── feed.css # Feed/list styles
│ │ ├── syntax-highlighting.css # Code blocks
│ │ ├── components/ # Component-specific CSS
│ │ │ ├── nav-links.css
│ │ │ ├── footer.css
│ │ │ ├── feed-item.css
│ │ │ ├── figure.css
│ │ │ ├── button.css
│ │ │ ├── icon.css
│ │ │ ├── intro-blurb.css
│ │ │ ├── profile-mark.css
│ │ │ ├── decorative-mark.css
│ │ │ └── title-mark.css
│ │ └── pages/ # Page-specific CSS
│ │ ├── post.css
│ │ └── album.css
│ │
│ ├── fonts/ # Self-hosted fonts
│ │ ├── Karla-Regular.ttf # Sans-serif body font (preloaded)
│ │ ├── Karla-Bold.ttf
│ │ ├── Karla-Medium.ttf
│ │ ├── Karla-Italic.ttf
│ │ ├── Karla-MediumItalic.ttf
│ │ └── YoungSerif-Regular.otf # Serif display font (preloaded)
│ │
│ ├── img/ # Image assets
│ │ ├── albums/ # Photo albums (date-based directories)
│ │ │ ├── 2023-03-19-belgravia-photo-walk/
│ │ │ ├── 2023-11-04-weekend-in-brighton/
│ │ │ ├── 2024-08-31-the-barbican-conservatory/
│ │ │ └── ... (10 albums total)
│ │ │
│ │ ├── posts/ # Post-related images
│ │ │ └── (various post images)
│ │ │
│ │ ├── components/ # Component graphics
│ │ │ └── (icon/decorator assets)
│ │ │
│ │ └── seo/ # SEO-related images
│ │ └── site--profile.jpeg
│ │
├── collections/ # Jekyll collections directory
│ ├── _posts/ # Published articles (30+ posts)
│ ├── _drafts/ # Draft posts (not published)
│ └── _ui-library/ # UI component showcase pages
│ ├── colours.html
│ ├── typography.html
│ ├── button.html
│ ├── figure.html
│ └── ... (14 pages total)
│
├── bin/ # Utility scripts
│ ├── build # Production build script
│ ├── dev # Development server (livereload)
│ ├── setup # Initial setup (installs dependencies)
│ ├── compose # Create new posts/drafts
│ └── dimensions # Python script to get image dimensions
│
├── _config.yml # Jekyll configuration
├── Gemfile # Ruby dependencies
├── Gemfile.lock # Locked dependency versions
├── .tool-versions # asdf version manager config (Ruby 3.2.3)
├── .gitignore # Excludes _site, .jekyll-cache, vendor, etc.
│
├── Root-level pages # HTML/Markdown pages
│ ├── index.html # Home page (uses feed layout)
│ ├── writing.html # Writing posts page
│ ├── photos.html # Photo albums page
│ ├── speaking.html # Speaking engagements page
│ ├── ui-library.html # UI component showcase
│ ├── 404.html # Not found error page
│ ├── 500.html # Server error page
│ ├── cover.html # Cover/splash page
│ └── coming-soon.html # Coming soon placeholder
│
└── README.md # Project setup and documentation
The main Jekyll configuration file (~150 lines) defines:
Site Identity:
title: “Simon van Dyk”domain: “simonvandyk.info”url: “https://simonvandyk.info”description: Professional biolocale: en_GB (London timezone)Email & Contact:
Navigation & Routes:
SEO:
/assets/img/seo/site--profile.jpegCollections:
ui-library (output: true)collections/basicPlugins:
Jekyll Compose Config:
Build Exclusions:
.sass-cache/, .jekyll-cache/, bin/, tmp/, Gemfile, README.mdMinimal Ruby dependencies focused on GitHub Pages:
github-pages gem (manages Jekyll and all compatible plugins)jekyll-feed (~0.12)jekyll-seo-tagjekyll-sitemapjekyll-composewebrick (required for Jekyll 4+)- Includes critical CSS and syntax highlighting
- Header with nav links
- Article with date, title, subtitle
- Post body content
- Footer with intro blurb included
- Used for: blog posts, photo albums
- Extends post.html structure
- Additional CSS for album layouts
- Conditionally renders photo gallery if `page.photos` exists
- Gallery items use figure component with dimensions
- Lazy-loaded images with data-width/height attributes
- Includes image-viewer modal for lightbox
- Minimal template for generic content
- Just head + content (no nav/footer)
- Used for: home, writing, photos, speaking, ui-library pages
- Allows pages to include their own nav/footer as needed
- Special error page layout
- Different background color (--color-bg-error)
- Centered article with max-width: 85ch
- Used for 404.html and 500.html
head.html - Standard <head> content
critical-css.html - Inlined CSS
image-viewer.html - Lightbox Modal (282 lines)
gallery-controls.html - Gallery Size Controller
post-sign-off.html - Simple footer signature marker
anonymous-analytics.html - Privacy-focused analytics snippet
A modular component library with 11 base components + design-system variants:
Structural Components:
nav-links.html - Navigation list (loops through site.nav_links)footer.html - Site footer with flexbox layout
Content Components:
figure.html - Responsive image figure wrapper
feed-item.html - Post/article list item
intro-blurb.html - Personal introduction sectionUI Components:
button.html - Accessible button elementicon.html - SVG icon component (3469 bytes, 1+ icon support)profile-mark.html - Avatar/profile image componentdecorative-mark.html - Decorative border/elementtitle-mark.html - Title decorationDesign System Components (_includes/components/design-system/)
color-swatch.html - Displays CSS color variablescontrast-example.html - WCAG contrast demonstrationssize-example.html - Spacing/sizing scale displaytext-style-example.html - Typography examplesDesign Tokens (tokens.css - ~200 lines):
--property-modifier (e.g., –color-fg-weak)Responsive Breakpoints:
Component-Based CSS:
View Transitions:
@view-transition { navigation: auto; } for fade transitionsSelf-hosted fonts in assets/fonts/:
rel="preload" and crossoriginNo Google Fonts or external dependencies - entirely self-hosted.
Album Structure (assets/img/albums/):
Post Images (assets/img/posts/):
Component Images (assets/img/components/):
SEO Images (assets/img/seo/):
From README:
All content lives in collections/ directory (not root _posts, _drafts):
_posts/ (30+ published articles)
type fieldpost: Technical/personal writing (most common)album: Photo galleriespresentation: Speaking engagements_drafts/ (unpublished)
--drafts flag during development_ui-library/ (component showcase)
Blog Posts (type: post)
---
layout: post
title: "Article Title"
subtitle: "Brief description"
type: post
---
Photo Albums (type: album)
---
layout: album
title: "Album Title"
subtitle: "Description"
type: album
image: /assets/img/albums/[date]-[slug]/photo-1.jpeg
photos:
- src: /assets/img/albums/.../photo-1.jpeg
alt: "Descriptive alt text"
width: 1365
height: 2048
caption: "Optional caption"
- ... (more photos)
---
Speaking (type: presentation)
---
layout: post
title: "Talk Title"
subtitle: "Description"
type: presentation
---
Pages filter posts by type:
where: "type", "post"where: "type", "album"where: "type", "presentation"#!/bin/sh
set -e
JEKYLL_ENV=production bundle exec jekyll build
#!/bin/sh
set -e
bundle exec jekyll serve --livereload -H 0.0.0.0 "$@"
#!/bin/sh
set -e
asdf install
bundle install
#!/bin/sh
set -e
bundle exec jekyll "$@"
bin/compose draft "Post Title", bin/compose publish, etc.bin/dimensions assets/img/albums/2024-08-31-the-barbican-conservatoryDesign Token System:
var(--color-fg))Color Palette:
Typography System:
Spacing System:
Performance Optimizations:
Wavy Links on hover/focus:
a:hover, a:focus {
text-decoration-style: wavy;
}
Dark Mode Image Inversion:
.invert-image-dark-mode {
filter: invert(1);
}
Applied conditionally to images in dark mode.
CSS Reset: Based on Josh Comeau’s modern CSS reset:
The site uses minimal, vanilla JavaScript - no frameworks or jQuery.
image-viewer.html (~280 lines, embedded script):
gallery-controls.html (~50 lines, embedded script):
Footer Component:
<noscript>2023 → present</noscript> for JavaScript disabled<dialog> element for modalsFrom README - optimized for Fujifilm X-T1 JPEG output:
Export Phase (Lightroom):
Optimization Phase 1 - ImageOptim:
Optimization Phase 2 - Preview App:
Total Savings: 45% file size reduction from original export
Responsive Images:
<img
src="/path/to/image.jpeg"
alt="Descriptive text"
width="1365"
height="2048"
data-width="1365"
data-height="2048"
loading="lazy"
/>
Lazy Loading:
Dynamic Scaling (JavaScript):
Lightbox Pattern:
Responsive Image Markup:
<figure class="figure" >
<img alt="" loading="lazy" src="" />
</figure>
Album Structure:
2024-08-31-the-barbican-conservatory//assets/img/seo/site--profile.jpegbin/setup # Install Ruby 3.2.3 + gem dependencies
bin/dev # Start livereload server on localhost:4000
bin/dev --drafts # Include draft posts
bin/compose draft "Post Title" # Create draft
bin/compose post "Post Title" # Create published post
bin/compose publish _drafts/NAME # Move draft to _posts
bin/dimensions path/to/album # Get image dimensions
bin/build # Production build to _site/
# Push to main branch → GitHub Actions → Deploy to GitHub Pages
| Component | Technology |
|---|---|
| Static Site Generator | Jekyll 4.x |
| Language | Ruby 3.2.3 |
| Package Manager | Bundler |
| Version Manager | asdf |
| Hosting | GitHub Pages |
| CI/CD | GitHub Actions |
| Fonts | Self-hosted (Karla, YoungSerif) |
| JavaScript | Vanilla ES6+ |
| CSS | Modern CSS (Grid, Flexbox, Custom Properties) |
| Plugins | jekyll-feed, jekyll-seo-tag, jekyll-sitemap, jekyll-compose |
| Analytics | Privacy-focused (server-side only) |
collections/ directory, not roottype field (post, album, presentation)