diff --git a/_11ty/blurry-placeholder.js b/_11ty/blurry-placeholder.js index f6aa321..57b183e 100644 --- a/_11ty/blurry-placeholder.js +++ b/_11ty/blurry-placeholder.js @@ -92,7 +92,10 @@ function getBitmapDimensions_(imgWidth, imgHeight) { } module.exports = async function (src) { - const filename = "_site/" + src; + let filename = "_site/" + src; + if (!await exists(filename) && src.startsWith("/assets/")) { + filename = "src" + src; + } const cachedName = filename + ".blurred"; if (await exists(cachedName)) { return readFile(cachedName, { diff --git a/_11ty/img-dim.js b/_11ty/img-dim.js index 7013669..65423c8 100644 --- a/_11ty/img-dim.js +++ b/_11ty/img-dim.js @@ -21,6 +21,7 @@ const { JSDOM } = require("jsdom"); const { promisify } = require("util"); +const fs = require("fs"); const sizeOf = promisify(require("image-size")); const blurryPlaceholder = require("./blurry-placeholder"); const srcset = require("./srcset"); @@ -54,7 +55,11 @@ const processImage = async (img, outputPath) => { } let dimensions; try { - dimensions = await sizeOf("_site/" + src); + let inputPath = "_site/" + src; + if (!fs.existsSync(inputPath) && src.startsWith("/assets/")) { + inputPath = "src" + src; + } + dimensions = await sizeOf(inputPath); } catch (e) { console.warn(e.message, src); return; @@ -93,39 +98,52 @@ const processImage = async (img, outputPath) => { if (img.tagName == "IMG") { img.setAttribute("decoding", "async"); img.setAttribute("loading", "lazy"); - img.setAttribute( - "style", - `background-size:cover;` + - `background-image:url("${await blurryPlaceholder(src)}")` - ); + try { + img.setAttribute( + "style", + `background-size:cover;` + + `background-image:url("${await blurryPlaceholder(src)}")` + ); + } catch (e) { + console.warn(`[img-dim] blurryPlaceholder failed for ${src}: ${e.message}`); + } const doc = img.ownerDocument; const picture = doc.createElement("picture"); const avif = doc.createElement("source"); const webp = doc.createElement("source"); const jpeg = doc.createElement("source"); - const fallback = await setSrcset(jpeg, src, fallbackType); - if (!fallback) { + try { + const fallback = await setSrcset(jpeg, src, fallbackType); + if (!fallback) { + return; + } + const avifFallback = await setSrcset(avif, src, "avif"); + if (avifFallback) { + avif.setAttribute("type", "image/avif"); + picture.appendChild(avif); + } + const webpFallback = await setSrcset(webp, src, "webp"); + if (webpFallback) { + webp.setAttribute("type", "image/webp"); + picture.appendChild(webp); + } + jpeg.setAttribute("type", `image/${fallbackType}`); + picture.appendChild(jpeg); + img.parentElement.replaceChild(picture, img); + picture.appendChild(img); + img.setAttribute("src", fallback); + } catch (e) { + console.warn(`[img-dim] setSrcset failed for ${src}: ${e.message}`); return; } - const avifFallback = await setSrcset(avif, src, "avif"); - if (avifFallback) { - avif.setAttribute("type", "image/avif"); - picture.appendChild(avif); - } - const webpFallback = await setSrcset(webp, src, "webp"); - if (webpFallback) { - webp.setAttribute("type", "image/webp"); - picture.appendChild(webp); - } - jpeg.setAttribute("type", `image/${fallbackType}`); - picture.appendChild(jpeg); - img.parentElement.replaceChild(picture, img); - picture.appendChild(img); - img.setAttribute("src", fallback); } else if (!img.getAttribute("srcset")) { - const fallback = await setSrcset(img, src, fallbackType); - if (fallback) { - img.setAttribute("src", fallback); + try { + const fallback = await setSrcset(img, src, fallbackType); + if (fallback) { + img.setAttribute("src", fallback); + } + } catch (e) { + console.warn(`[img-dim] setSrcset (fallback) failed for ${src}: ${e.message}`); } } }; diff --git a/_11ty/srcset.js b/_11ty/srcset.js index 639e87b..2e34fd7 100644 --- a/_11ty/srcset.js +++ b/_11ty/srcset.js @@ -79,7 +79,13 @@ async function resize(filename, width, format) { if (await exists("_site" + out)) { return out; } - await sharp("_site" + filename) + + let inputPath = "_site" + filename; + if (!await exists(inputPath) && filename.startsWith("/assets/")) { + inputPath = "src" + filename; + } + + await sharp(inputPath) .rotate() // Manifest rotation from metadata .resize(width) [format]({ diff --git a/src/pages/content-playing-field.md b/src/pages/content-playing-field.md index 79b1795..eb799bf 100644 --- a/src/pages/content-playing-field.md +++ b/src/pages/content-playing-field.md @@ -37,7 +37,7 @@ Driver of Change (DoC) model for Si ### James -![James's avatar](images/FDBD38E0-FAA0-4A10-96DF-7B547D168F73-300x300.jpeg) +![James's avatar](/assets/images/FDBD38E0-FAA0-4A10-96DF-7B547D168F73-300x300.jpeg) 30something male from UK, 2 kids (boy 7, girl 2), married, web developer for a small company, mostly remote. More of a night owl than a morning person.  @@ -53,7 +53,7 @@ James values giving back to society, special time with his family and learning n ### Antonia -![Antonia's avatar](images/4F1CD6A6-5576-4FEF-BBB5-EEDBB8DEC280-300x300.jpeg) +![Antonia's avatar](/assets/images/4F1CD6A6-5576-4FEF-BBB5-EEDBB8DEC280-300x300.jpeg) 20something female from US, single with no kids, freelance designer, works from home or co-working spaces. Generally up early except weekends.  @@ -88,8 +88,23 @@ ADDE Examples for Si ### Assets -![Mini Coops in house style duotone](images/3B7A5BB9-EB74-4B3C-8B57-579A39F06FC5-1024x1024.jpeg) +![Mini Coops in house style duotone](/assets/images/3B7A5BB9-EB74-4B3C-8B57-579A39F06FC5-1024x1024.jpeg) ## Content Tilt 👓 -\[**TODO**: _Competitor analysis to identify any unique angles from the side project community_\] +The side project community is bustling, but often polarized: + +* **The Hustlers 💸**: "Ship fast," "MRR," and "Sleep when you're dead." Inspiring, but often leads to burnout and feelings of inadequacy for those with limited time. +* **The Techies 💻**: Deep dives into code, frameworks, and "How to build X." Essential for *building*, but lacks the structural advice to *finish* or stay motivated. +* **The Makers 🎨**: Focused on the craft and design. Often missing the pragmatic project management needed to ship. + +**My unique angle:** +**Sustainable Side Projects for the Rest of Us.** + +This publication sits at the intersection of **Agile Project Management** and **Personal Well-being**. It treats a side project not as a lottery ticket, but as a fulfilling creative outlet that must coexist with a career and family. + +I apply professional leadership and delivery methodologies (usually reserved for enterprise teams) to the chaotic, emotional world of the solo creator. + +* **Less** "How to make $10k/month" → **More** "How to find 2 hours a week without guilt." +* **Less** "Best React Framework" → **More** "Best framework for decision making." +* **Less** "Success Stories" → **More** "Honest Retrospectives on Failure."