Skip to content

Repository files navigation

Kenneth Lamar's website

My website, based on Hylia, a lightweight Eleventy starter kit.


Terminal commands

Ensure packages are installed and up to date

npm install

Serve the site locally (with hot reload)

npm start

Builds the CSS, then runs the CSS watcher, the deletion watcher, and the Eleventy dev server (eleventy --serve) concurrently. Edit a post or the SCSS and the browser reloads automatically.

Note: dev builds skip the pre-compression pass, and the dev server removes any .br/.gz siblings left over from a previous production build, so after npm start the dist/ lacks the pre-compressed variants. Run npm run production to restore the deployable dist before deploying or running performance audits.

Build a production version of the site

npm run production

Builds the CSS, then runs a full Eleventy build into dist/. After the build, every text asset in dist/ (HTML, CSS, JS, XML, JSON, SVG, TXT) is pre-compressed with Brotli and Gzip (.br/.gz siblings), so the site is fast on any static host — no platform compression needed.

Compile the CSS (one-shot)

npm run build:css

Compiles src/scss/global.scss (regenerating the design tokens first) to dist/assets/css/global.<hash>.css — the filename is a content hash, so the file is cacheable immutably (see src/_headers) and changes whenever the CSS does. The href + content are published to .cache/css-build.json (gitignored build output, regenerated before every build), which the layout reads and inlines. In watch mode a change to that file cannot trigger a page rebuild — Eleventy's watcher honors .gitignore — so the CSS watcher additionally touches src/_data/site.json (identical bytes) to make the serve rebuild pick up the new stylesheet.

External link previews

External links in posts render as rich link previews (favicon, title, description). Metadata is fetched with metascraper and cached in .cache/link-previews.json (gitignored, like the images); preview images and logos go into .cache/previews/ (content-addressed) and are copied into the build as /assets/images/previews/, so built pages never hot-link third-party image hosts and every preview <img> carries explicit width/height (no layout shift).

The build runs the fetcher in incremental mode before every build: it fetches URLs added since the last refresh and refills cache files missing on a cold checkout, while a warm cache stays fully offline and the JSON is left untouched. Links without an entry fall back to plain links.

npm run fetch:previews

Run this (full refresh) after adding new external links or to update stale titles/descriptions: it re-fetches every referenced page, re-downloads every preview image, and rewrites the JSON.

Image pipeline

Originals live in src/assets/images/ and are copied to the output verbatim. Markdown images (![alt](/assets/images/foo.jpg)) are additionally wrapped in a click-to-full-size link with responsive <picture> sources, generated by @11ty/eleventy-img at 6 widths × webp/jpeg/svg/avif (plus the source format).

Generated variants go into a persistent, content-addressed cache (.cache/images/, gitignored) instead of being regenerated every build. After each build the cache is synced into dist/assets/images/ and variants no longer referenced by any page are pruned. Because the filenames embed a hash of the source bytes, editing an image only regenerates that image's variants; everything else is a no-op. Delete .cache/ to force a full re-render of all images.

Math

Markdown posts support LaTeX math via [KaTeX]: $...$ for inline, $$...$$ for display. All markdown is rendered by the shared engine in src/utils/markdown.js (post bodies, feed, link previews); the KaTeX rules live there, so that's where to adjust them.

Watch mode

On some filesystems the kernel's file-event API silently delivers no events (WSL2's virtual v9fs/9p mounts, macOS iCloud/OneDrive/external volumes), which leaves hot reload dead. The Eleventy watcher therefore polls for file changes by default on non-macOS platforms (.eleventy.jssetChokidarConfig) and the deletion watcher (scripts/watch-deletions.cjs) does the same. The CSS watcher (scripts/build-css.cjs) always polls source mtimes.

macOS uses native FSEvents and never polls by default. If edits are not picked up there (project on a synced/external volume), force polling:

ELEVENTY_POLL=1 npm start

(ELEVENTY_POLL=0 forces native events the other way, on any platform.)

Build performance

The first build on a machine (no .cache/images/) is slow — around a minute on a desktop — because @11ty/eleventy-img renders every post image at 6 widths x webp/jpeg/avif/svg (~400 variants, some sources over 8000px wide). Everything after that is fast:

Build Typical time
First (no image cache) ~60-120 s
Full build (warm cache) ~8 s
The image cache is content-addressed (hash of the source bytes in the
filename), so editing one image only re-renders that image.

Deployment (Cloudflare Pages)

  • Build command: npm run production — output directory: dist/.
  • Use the V3 build image (Settings → Build & deployments). V2 projects are auto-migrated to V3 on 2027-02-23; migrating earlier is a dashboard change with no repo changes needed. V2's default runtime (Node 18) is end-of-life.
  • Node is pinned to 22 via .node-version (the V3 default), so builds are identical on V2 and V3 and immune to future default bumps.
  • Enable Build cache (Settings → Build → Build cache; V2 or V3). Pages auto-detects Eleventy and caches .cache/ between deploys, so CI pays the image-render cost once and reuses the variants. The cache is purged 7 days after its last read, so the first build after enabling (or after a long idle) is cold: variants re-render (~60-120 s) and preview images re-fetch from their recorded sources, but the build is complete either way.
  • The site is self-contained: minified + hashed assets, pre-compressed Brotli/Gzip, responsive image variants. Cloudflare's edge compression is a bonus on top, not a dependency.

Design Tokens and Styleguide

Design Tokens

Although Hylia has a pretty simple design, you can configure the core design tokens that control the colours, size ratio and fonts.


Note: Credit must be given to the hard work Jina Anne did in order for the concept of design tokens to even exist. You should watch this video, then read this article and then sign up for this course to expand your knowledge.


To change the design tokens in the CMS, find the “Globals” in the sidebar then in the presented options, select “Theme Settings”.

To change the design tokens directly, edit _src/data/tokens.json.

The tokens are converted into maps that the Sass uses to compile the front-end CSS, so make sure that you maintain the correct structure of tokens.json.

Styleguide

Your version of Hylia ships with a Styleguide by default. You can see a demo of the Styleguide at https://kennethmlamar.com/styleguide/.

You can edit the Styleguide by opening src/styleguide.njk. If you don’t want the Styleguide, delete that file and the page will vanish.

Sass

Hylia is based on the WIP v2 version of Stalfos, which currently has no documentation (I know, I’m bad). Here is some very basic documentation for elements of the new framework that you will encounter on this project.

Configuration

The whole Sass system is powered by central config file, which lives here: _src/scss/_config.scss.

Before Sass is compiled, a _tokens.scss file is generated from the design tokens config which is required.

Key elements:

  • $stalfos-size-scale: A token driven size scale which by default, is a “Major Third” scale
  • $stalfos-colors: A token driven map of colours
  • $stalfos-util-prefix: All pre-built, framework utilities will have this prefix. Example: the wrapper utility is '.sf-wrapper' because the default prefix is 'sf-'
  • $metrics: Various misc metrics to use around the site
  • $stalfos-config: This powers everything from utility class generation to breakpoints to enabling/disabling pre-built components/utilities

How to create a new utility class with the generator

The utility class generator lets you generate whatever you want, with no opinions on class name or properties affected.

To add a new class, add another item to the exists $stalfos-config map. This example adds a utility for floating elements.

'float':('items':('left':'left','right': 'right'
  ),
  'output': 'responsive',
  'property': 'float'
);

The output is set to responsive which means every breakpoint will generate a prefixed class for itself. If you only wanted elements to float left in the md breakpoint, you’d now be able to add a class of md:float-left to your HTML elements.

If you only want standard utility classes generating, set the output to standard.

Functions

get-color($key)

Function tries to match the passed $key with the $stalfos-colors map. Returns null if it can’t find a match.

get-config-value($key, $group)

Returns back a 1 dimensional (key value pair) config value if available.

get-size($ratio-key)

Function tries to match the passed $ratio-key with the $stalfos-size-scale. Returns null if it can’t find a match.

Mixins

apply-utility($key, $value-key)

Grabs the property and value of one of the $stalfos-config utilities that the generator will generate a class for.

media-query($key)

Pass in the key of one of your breakpoints set in $stalfos-config['breakpoints'] and this mixin will generate the @media query with your configured value.

About

This website provides coverage of my projects, both personal and professional.

Resources

Stars

0 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages