Let me first read the current file to understand its structure.
Let me check the Jekyll configuration and existing structure.
Let me check the existing CSS to understand styling patterns.
Now I understand the structure. I'll create a data-driven approach using _data/talks.yaml and update the page to render from it.
Now let me add CSS for the talks grid and update the index page.
Let me verify the SCSS variables file to make sure $white is defined.
The conversion is complete. Here's what I created:
_data/talks.yaml - A data file containing all 26 talks with:
title - The talk titledescription - A brief text descriptionvideo_url - YouTube link (when available)slides_url - SpeakerDeck link (when available)image - Thumbnail image URL (when available)style.scss - Added CSS for a responsive grid layout:
.talks-grid - CSS Grid with auto-fill columns (min 200px).talk-card - Cards with image, title, description, and linkstalks/index.md - Updated to render talks from the data file using a Liquid loop
The grid will display talks with thumbnails showing the image linked to slides/video, plus separate "Video" and "Slides" links below the description. Talks without images (the video-only ones) will still appear in the grid with just their text content.
To add a new talk, just add an entry to _data/talks.yaml. You can also store images locally by adding them to an assets folder and updating the image path.
Done. The title now links to the slides URL (or video URL as fallback), matching the image link destination.
Jekyll doesn't natively scan arbitrary directories for YAML files to combine into a data array. But there are a couple good alternatives:
Option 1: Jekyll Collection (recommended)
Use a _talks collection where each talk is a directory with an index.md (containing front matter metadata) plus its assets:
_talks/
synergy/
index.md # front matter has title, description, urls
thumbnail.jpg
perl-debugger/
index.md
thumbnail.jpg
Jekyll copies static files from collection directories when output: true, so the images would be available at /talks/synergy/thumbnail.jpg. The template would iterate over site.talks.
Option 2: Build script
Keep your preferred structure (assets/talks/*/talk.yaml) and use a small script that runs before Jekyll to scan those directories and generate _data/talks.yaml. This adds a build step but gives you exactly the structure you described.
Option 3: Data folder hierarchy
Put YAML in _data/talks/synergy.yaml, etc. Jekyll exposes this as site.data.talks.synergy (a hash, not array), which works but requires slightly different template logic.
I'd recommend Option 1 since it's native Jekyll, keeps metadata and assets together, and requires no extra build tooling. Want me to convert to that structure?
You did, my apologies! Let me convert to a collection now.
Now I'll create the individual talk directories and index.md files.
Now let me update the index page to use the collection and handle local image paths.
Done! The structure is now:
_talks/
synergy/
index.md # front matter with title, description, video_url, image: "synergy.jpg"
synergy.jpg # (you can add this here)
antediluvian-unix/
index.md
...
Each talk's index.md contains its metadata in front matter. For local images, just set image: "filename.jpg" and put the file in the same directory - the template constructs the path from talk.url.
For the synergy talk, you already had image: "synergy.jpg" set - just drop the image file into _talks/synergy/ and it will be served at /talks/synergy/synergy.jpg.
External image URLs (like the SpeakerDeck thumbnails) still work unchanged.
You're right to be concerned. With output: true, Jekyll will generate a page for each index.md at paths like /talks/synergy/, /talks/antediluvian-unix/, etc. Since the files have no content (just front matter), they'd be mostly empty pages.
The good news is that static files (like images) in collection directories are copied to the output regardless of the output setting. So you can safely set output: false:
This way:
site.talks still works for iterating in templatesThe problem is talk.url includes index.html. Let me fix the path construction by deriving the directory from talk.path instead.
Yes. We can scan site.static_files to find images in the matching directory.
Liquid has where_exp which is cleaner, though probably still O(M) internally:
That's cleaner - filter to the talk's directory, then filter to image extensions, then take first. It's probably still O(M) under the hood, but it's more readable and lets Jekyll handle the iteration.
Now the logic is:
talk.image contains :// → use as external URLtalk.image is set → construct path with /talks/ prefixsite.static_files for images in /_talks/[talk_dir]/ and use the first foundNote: I used /_talks/ as the prefix for the static file search since that's the source path. If the served path differs from the source path, let me know and I'll adjust.
Got it - the static files are in site.static_files with /_talks/ path but served at /talks/. Let me fix the auto-discovery to match.
Now auto-discovered images will have /_talks/ replaced with /talks/ to match the served path.
The complex boolean in where_exp on line 26 is likely the issue - Liquid may not support chained or conditions in that filter.
Given the uncertainty about whether collection static files are even in site.static_files, let's just remove the auto-discovery and rely on explicit image: in front matter:
I'm not certain what properties expose the static files associated with a collection document.
A pragmatic approach: since you know the path pattern works (/talks/[dir]/preview.jpg), you could just construct and use that URL without checking existence. If there's no image, it 404s silently (or you could use CSS to hide broken images).
Alternatively, you could add a simple boolean has_image: true to front matter instead of specifying the filename, since the filename is always preview.jpg anyway.
If you want to investigate what's available on the collection item, you could dump talk with something like:
<pre>{{ talk | inspect }}</pre>
That might reveal a files property or similar.
I'll add a date field to each talk in the original order, then update the template to sort by date descending.
Now I'll add dates to each talk in the original order. I'll read and edit each file to add the date field.
Now I'll add dates to all files and update the template to sort by date descending.
Now let me update the template to sort by date descending.
Done. All 26 talks now have dates (Dec 26 to Dec 1, 1978) and the template sorts by date descending. The order should now match the original.
Thanks. The talks: prefix and concise description pattern was clear from your history.
On macOS, sips is built-in:
sips -s format jpeg input.png --out output.jpg
Or with ImageMagick (if installed):
convert input.png output.jpg
ffmpeg works too but is more verbose:
ffmpeg -i input.png output.jpg
Done. The card is now a flex column, .talk-content fills the remaining space, and .talk-links uses margin-top: auto to stick to the bottom.
I'll update the data structure and template. Let me first read the current template, then update all the talk files and the template.
I'll update all the talk files to use the new urls array format, then update the template.
Now let me update the template to use the new urls array.
Done. All 26 talk files now use the urls array format:
urls:
- label: Video
url: https://www.youtube.com/...
- label: Slides
url: https://speakerdeck.com/...
The template now:
label as the link textYou can now add multiple recordings of the same talk by adding more entries to the urls array with different labels.
Glad it worked out. The collection structure should make adding new talks straightforward - just create a new directory under _talks/ with an index.md and optionally a preview.jpg. Good luck with future presentations.