feat: a whole bunch of fixes ahead of launch
This commit is contained in:
parent
01647bf930
commit
8e54b46b6b
48 changed files with 184 additions and 140 deletions
|
@ -15,7 +15,7 @@ I’ve had a Figma file ready for months now that outlined my basic design - all
|
|||
|
||||
{% image "https://cdn.wonderfulfrog.com/figma-v3.png", "A screenshot from Figma showing my blog prototype. There are various elements visible like buttons and widgets. A colour scheme using teal.", "Figma screenshot" %}
|
||||
|
||||
{% image "https://cdn.wonderfulfrog.com/figma-v3-lightmode.png", "Another screenshot from Figma showing my blog prototype. This is showing the 'light theme' with a serif title font, monospaced body font, and teal accent colours.", "Astute observers will notice the current version doesn't look a whole lot like the site now!" %}
|
||||
{% image "https://cdn.wonderfulfrog.com/figma-v3-lightmode.png", "Another screenshot from Figma showing my blog prototype. This is showing the 'light theme' with a serif title font, monospaced body font, and teal accent colours.", "Astute observers will notice the site you're looking at doesn't look a whole lot like the design!" %}
|
||||
|
||||
Something that always frustrated me was having my content along the code. I sought out a solution for this for months. I tried a whole bunch of headless CMS options like [Sanity](https://www.sanity.io/), [Hygraph](https://hygraph.com/), [Ghost](https://ghost.org/), and even hosting my own [WordPress](https://wordpress.com) (that was an exciting prospect until I discovered their mobile app does not support plugins).
|
||||
|
||||
|
@ -23,7 +23,7 @@ I was always paranoid that bad actors would clone my git repo and steal my conte
|
|||
|
||||
One day I thought to myself “who cares?” and it was like a switch flipped. Bad actors can still copy and paste everything, so having the content off the repo makes little difference. LLMs are exploding and would make it trivial to scrape my site (if they wanted to).
|
||||
|
||||
At last, that let me narrow down my options. I’ll just keep the text content in the repo. But I still wanted a way to store images and other large media outside of the repo (more on that later. )
|
||||
At last, that let me narrow down my options. I’ll just keep the text content in the repo. But I still wanted a way to store images and other large media outside of the repo (more on that later).
|
||||
|
||||
If the text content can be in the repo, then I can use anything! So let’s stick with [Next.js](https://nextjs.org/)! … actually, let’s not.
|
||||
|
||||
|
@ -33,9 +33,9 @@ Anyone working in the web dev sphere knows things move fast. Too fast these days
|
|||
|
||||
I tried to migrate to the new [app router structure](https://nextjs.org/docs/app) and found myself getting stuck learning about breaking changes in Next.js and breaking changes in React[^1]. I spent way too much time trying to get things working with my tools that I didn’t stop and think that my tools should be working for me.
|
||||
|
||||
I was originally thinking of [Astro](https://astro.build/), but it’s still using React. I’m happy to use React for work but I don’t want to have to deal with its newest idiosyncrasies in my own stuff.
|
||||
As an alternative, I was thinking of using [Astro](https://astro.build/), but it’s still React under the hood. I’m happy to use React for work but I don’t want to have to deal with its newest idiosyncrasies in my own stuff.
|
||||
|
||||
Simply put modern React confuses the hell out of me. I’m not all in for [“server vs client components”](https://nextjs.org/learn/react-foundations/server-and-client-components). I’m certainly not the only one[^4]. I don't want this post to digress into my thoughts on modern React so I'm going to sidestep that for now. I’m spending time wrapping my head around modern React - because I have to for my job - but for my personal site I'd rather things should be _easy._ I'm tired of having to deal with breaking changes all the time. I longed for the days of PHP where stuff just worked forever. It was time to embrace an old foe: boring technology.
|
||||
Simply put modern React confuses the hell out of me. I’m not all in for [“server vs client components”](https://nextjs.org/learn/react-foundations/server-and-client-components). I’m certainly not the only one[^4]. I don't want this post to digress into my thoughts on modern React so I'm going to sidestep that for now. I am forced to learn its newest architecture for work, but for my personal site I'd rather things be <mark>easy</mark>. I'm tired of having to deal with breaking changes all the time. I longed for the days of PHP where stuff just worked forever. It was time to embrace an old foe: boring technology.
|
||||
|
||||
Boring technology - to me, anyway - is something that’s been around for a while, probably a few years. It’s stable. It’s been around long enough that someone has asked the basic questions on how to do XYZ on StackOverflow. And something important to me is that it’s also *averse to breaking changes*. It tries its best to avoid them, and if they must then there is a clear migration path and plenty of warning.
|
||||
|
||||
|
@ -90,13 +90,24 @@ A methodology that resonated with me was [CUBE CSS](https://cube.fyi/). CUBE sta
|
|||
|
||||
I organized everything following this methodolgy, and added a `global.css` file that ties everything together with `@import`and `@import-glob` statements. I used PostCSS to process the CSS into a single file.
|
||||
|
||||
In order to include my CSS generation as part of 11ty's build process, I used a JavaScript [Template Data File](https://www.11ty.dev/docs/data-template-dir/) that processes the CSS using PostCSS, and the data file renames the file using the `permalink` property in the frontmatter. It leads to a single `style.css` that has everything I need - nice and clean, and no extra `npm` task required.
|
||||
For my design tokens, I use a handful of JavaScript files to assemble my JSON design tokens into CSS strings. That leaves me with two chunks of CSS - my design tokens, and site CSS that uses those design tokens.
|
||||
|
||||
As part of my CSS build process, I inject my design tokens into the generated CSS from `global.css`. It's not the most elegant way I'm sure, but it works fine. Speaking of design tokens...
|
||||
I used a JavaScript [Template Data File](https://www.11ty.dev/docs/data-template-dir/) (`css/styles.11tydata.js`) that assembles all of my CSS into one giant string, run it through PostCSS (which includes some extra plugins), and then data file renames the file using the `permalink` property in the frontmatter. It leads to a single `style.css` that has everything I need - nice and clean, and no extra `npm` task required.
|
||||
|
||||
# Reinventing the wheel
|
||||
What PostCSS plugins am I using? The following:
|
||||
|
||||
I like Tailwind - especially for work - but I think it comes in heavy-handed. It does a lot, and I don't need the majority of it. I've seen some developers strip away everything but the design system that Tailwind provides out of the box, and I thought about doing that... but what if I did it myself instead?
|
||||
- [`postcss-import`](https://github.com/postcss/postcss-import) - Any `@import` statements are replaced with the file being imported.
|
||||
- [`postcss-import-ext-glob`](https://github.com/dimitrinicolas/postcss-import-ext-glob) - Like using `@import` but with glob file paths. More of a developer convenience.
|
||||
- [`autoprefixer`](https://github.com/postcss/autoprefixer) - Not as essential these days, but there are still some vendor-prefixed CSS rules.
|
||||
- [`cssnano`](https://github.com/cssnano/cssnano) - Minifies my CSS, and fast.
|
||||
|
||||
Combining two CSS strings is perhaps not the most efficient way of doing things, but it makes sense to me and seems fast enough.
|
||||
|
||||
Let's take a walk through how my design tokens work!
|
||||
|
||||
## Reinventing the wheel
|
||||
|
||||
I like Tailwind - especially for work - but I think it comes in heavy-handed. It does a lot, and I don't need the majority of it. I've seen some developers strip away everything but the design system that Tailwind provides out of the box, and I thought about doing that... but what if I built my own solution instead?
|
||||
|
||||
Tailwind is yet another dependency. If I only need a small subset of its feature-set, why should I have to spend developer-hours stripping away everything I don't need and instead solve the problem myself with less code?
|
||||
|
||||
|
@ -155,55 +166,47 @@ const helperClasses = [
|
|||
];
|
||||
```
|
||||
|
||||
I run this array through a `helperClassesToCss` function I wrote, which takes the helper class prefix, appends the colour name (e.g. `primary`) to the prefix, and sets the array of CSS properties to the desired value. If you're curious, here is the full source code with comments:
|
||||
I run this array through a `helperClassesToCss` function I wrote, which takes the helper class prefix, appends the colour name (e.g. `primary`) to the prefix, and sets the array of CSS properties to the desired value.
|
||||
|
||||
### A walkthrough
|
||||
I wanted to run through an example process from JSON to CSS and how it works - hopefully I can refer to this in the future if I ever forget how the process works.
|
||||
|
||||
To start, `cssPropertiesToCss` takes an array of CSS rules and sets them to the same `value`. For example:
|
||||
|
||||
```js
|
||||
/**
|
||||
* Given an array of CSS properties, output css properties
|
||||
* with each property equal to `value`
|
||||
*/
|
||||
const cssPropertiesToCss = (cssProperties, value) => {
|
||||
return cssProperties.reduce((css, cssProp) => {
|
||||
return css + `${cssProp}:${value};`;
|
||||
}, ``);
|
||||
};
|
||||
|
||||
/**
|
||||
* Given a helperClass (string) and array of cssProperties,
|
||||
* will generate a css class named helperClass that has
|
||||
* all cssProperties mapped to value.
|
||||
*/
|
||||
const helperClassToCss = (helperClass, cssProperties, value) => {
|
||||
const cssProps = cssPropertiesToCss(cssProperties, value);
|
||||
return `.${helperClass}{${cssProps}}`;
|
||||
};
|
||||
|
||||
/**
|
||||
* Given an array of helperClasses that map to cssProperties,
|
||||
* output a string of CSS that maps the helperClass (with variant modifier)
|
||||
* to the array of css properties with each css property equal to
|
||||
* value
|
||||
*
|
||||
* e.g.
|
||||
* helperClasses = [["text", ["color"]]],
|
||||
* variant = "primary",
|
||||
* value = "#000"
|
||||
*
|
||||
* Will output the following:
|
||||
* .text-primary {
|
||||
* color: #000;
|
||||
* }
|
||||
*/
|
||||
const helperClassesToCss = (helperClasses, variant, value) => {
|
||||
return helperClasses.reduce((css, [helperClass, cssProperties]) => {
|
||||
return (
|
||||
css + helperClassToCss(`${helperClass}-${variant}`, cssProperties, value)
|
||||
);
|
||||
}, ``);
|
||||
};
|
||||
const cssProperties = ["margin-left", "margin-right"];
|
||||
const value = "16px";
|
||||
|
||||
const css = cssPropertiesToCss(cssProperties, value);
|
||||
// css = "margin-left:16px;margin-right:16px;";
|
||||
```
|
||||
|
||||
`cssPropertiesToCss` uses the `.reduce()` function to transform the array of properties into a string by concatenating each item in the array together.
|
||||
|
||||
`helperClassToCss` takes the generated CSS string and wraps it with a CSS selector `helperClass`.
|
||||
|
||||
```js
|
||||
const cssProperties = ["margin-left", "margin-right"];
|
||||
const value = "16px";
|
||||
const helperClass = "mx-1";
|
||||
|
||||
const helperClassCss = helperClassToCss(helperClass, cssProperties, value);
|
||||
// helperClassCss = ".mx-1{margin-left:16px;margin-right:16px;}"
|
||||
```
|
||||
|
||||
Finally, `helperClassesToCss` ties everything together to make adding new classes and variants easy.
|
||||
|
||||
```js
|
||||
const helperClasses = [["mx", ["margin-left", "margin-right"]], ["my", ["margin-top", "margin-bottom"]]];
|
||||
const variant = "1";
|
||||
const value = "16px";
|
||||
|
||||
const helperClassesToCss = (helperClasses, variant, value);
|
||||
// helperClassesToCss = ".mx-1{margin-left:16px;margin-right:16px}.my-1{margin-top:16px;margin-bottom:16px}"
|
||||
```
|
||||
|
||||
It might not be the best, but it makes sense to me!
|
||||
|
||||
All of my code related to generating CSS from design tokens uses nothing but plain JavaScript and built-in modules (I think the only one being `path`). I'll never have to worry about my build process breaking. All of my design tokens are JSON. If I ever need to tweak them, the only thing I need to change is a couple of JSON files!
|
||||
|
||||
It took me a fair while to write, and certainly some trial and error, but I'm quite pleased with the final outcome. I can shunt this output into any CSS or future project if I want. If I want more helper classes, it's very easy to add more. There are quite a few spacing helpers:
|
||||
|
@ -251,11 +254,13 @@ Yep, that's it. It uses a fancy wildcard selector (`* + *`) to achieve the effec
|
|||
|
||||
What ends up happening is if you have a bunch of headers and paragraph tags on the page, this one single class styles it up perfectly so that it looks readable and beautiful. No extra work required, because the spacing value is `1em` it will use whatever the current child's `font-size` is. How freakin' cool is that?! I love it.
|
||||
|
||||
# Speaking of Cascade... The Data Cascade
|
||||
Utilities like this are a big part of the principles behind [Build Excellent Websites](https://buildexcellentwebsit.es/). "Be the browser's mentor, not its micromanager." Highly recommend watching [Andy Bell's keynote](https://www.youtube.com/watch?v=5uhIiI9Ld5M) on the subject.
|
||||
|
||||
[The Data Cascade](https://www.11ty.dev/docs/data-cascade/) is what I would consider to be 11ty's killer feature (like a [killer app](https://en.wikipedia.org/wiki/Killer_application)). In short, it allows for injecting data (or assembling data) practically anywhere, and rely on context in order to drive where that data goes.
|
||||
## Speaking of Cascade... The Data Cascade
|
||||
|
||||
The simplest data source would be front matter - the data that lives at the top of Markdown files. The neat part is that front matter can be added to non-Markdown files too, which allows for some potential fun stuff.
|
||||
[The Data Cascade](https://www.11ty.dev/docs/data-cascade/) is what I would consider to be 11ty's killer feature (like a [killer app](https://en.wikipedia.org/wiki/Killer_application)). In short, it allows for injecting (or assembling) data practically anywhere, and rely on context in order to drive where that data goes.
|
||||
|
||||
The simplest data source would be frontmatter - the data that lives at the top of Markdown files. The neat part is that frontmatter can be added to non-Markdown files too, which allows for some potential fun stuff.
|
||||
|
||||
For example, my catalogue content is inside `/catalogue`. Inside that folder are more subfolders, and a [template data file](https://www.11ty.dev/docs/data-template-dir/) for the folder called `catalogue.json`. It looks like this:
|
||||
|
||||
|
@ -288,10 +293,12 @@ Lets go through this one:
|
|||
- Every file inside uses the `layouts/catalogue-item` layout
|
||||
- Applies the `book` tag (and therefore generates a new collection, automatically)
|
||||
- Creates a permalink using the `fileSlug` variable per page
|
||||
- Changes the `linkTitle` front matter
|
||||
- Uses `eleventyComputed` (which is a special field) to inject data from the Markdown file
|
||||
- Changes the `linkTitle` frontmatter
|
||||
- Uses `eleventyComputed` (which is a special field) to inject frontmatter into the template's frontmatter
|
||||
|
||||
That's a lot! I'd like to turn your attention to the `permalink` variable. By using just this alone, 11ty will automatically generate HTML pages for each of my Markdown files inside this directory and pass in the front matter I set here. With just one line of code! What?! That's awesome!!
|
||||
Highly suggest reading the docs on [Computed Data](https://www.11ty.dev/docs/data-computed/).
|
||||
|
||||
That's a lot! I'd like to turn your attention to the `permalink` variable. By using just this alone, 11ty will automatically generate HTML pages for each of my Markdown files inside this directory and pass in the frontmatter I set here. With just one line of code! What?! That's awesome!!
|
||||
|
||||
At this point all I have to do is make sure I have a layout file defined and built the way I like... and 11ty takes care of the rest. Love this!
|
||||
|
||||
|
@ -341,7 +348,7 @@ For now I'm content to trigger a build every so often in order for the data to b
|
|||
|
||||
## Alert the media
|
||||
|
||||
This was a thorny issue for me. I couldn't decide on how I wanted to manage media storage for a while. All I knew for certain was that I was tired of keeping it in my repo - it needed to be elsewhere.
|
||||
Media storage was still a thorny issue. I couldn't decide on how I wanted to manage it for a while. All I knew for certain was that I was tired of keeping it in my repo - it needed to be elsewhere.
|
||||
|
||||
As I dug into 11ty, I discovered a truly magical plugin called [`eleventy-img`](https://www.11ty.dev/docs/plugins/image/). It takes an image (either locally or remote), optimizes it, and stores it in the _output_ directory. The image can therefore be put anywhere you like, and with a small bit of shortcode it works like magic. For example:
|
||||
|
||||
|
@ -353,7 +360,7 @@ It's a little bit of extra syntax compared to a Markdown image (and the newest v
|
|||
|
||||
That left the final question - where do I keep my media? I eventually settled on [Bunny.net](https://bunny.net/) - simple, no-nonsense storage with clear pricing. Has a REST API for uploading images, and even works with SFTP.
|
||||
|
||||
I decided to do the painstaking process of manually updating all of my content by myself rather than automating it. I figured the time investment of an automated solution would be roughly equal to the time it would take to do manually. Regardless, all my media is now behind a robust CDN (and with a custom domain too). When my site is built, 11ty will fetch all those images and generate local copies that are resized and output using `srcset`. It's all so seamless and easy, and `eleventy-image` even caches the results so subsequent re-builds are super fast!
|
||||
I decided to do the painstaking process of manually updating all of my content by myself rather than automating it. I figured the time investment of an automated solution would be roughly equal to the time it would take to do manually. Regardless, all my media is now behind a robust CDN (and with a custom domain too). When my site is built, 11ty will fetch all those images and generate local copies that are resized and output using `srcset`. It's all so seamless and easy, and `eleventy-img` even caches the results so subsequent re-builds are super fast!
|
||||
|
||||
## Other organizational details
|
||||
|
||||
|
@ -394,6 +401,12 @@ I'm also using the [`@11ty/eleventy-plugin-rss`](https://www.11ty.dev/docs/plugi
|
|||
|
||||
[Shortcodes](https://www.11ty.dev/docs/shortcodes/) are cool. They're like custom Markdown but supercharged. I have my aforementioned `image` shortcode, and another for embedding YouTube videos using [`lite-youtube`](https://github.com/justinribeiro/lite-youtube).
|
||||
|
||||
Here is an example of the YouTube shortcode:
|
||||
|
||||
```
|
||||
{% raw %}{% youtube "Video ID", "Video Title" %}{% endraw %}
|
||||
```
|
||||
|
||||
### Transforms
|
||||
|
||||
[Transforms](https://www.11ty.dev/docs/config/#transforms) are great for modifying the template output. I only have one which minifies the HTML output using [`html-minifier-terser`](https://github.com/terser/html-minifier-terser).
|
||||
|
@ -423,6 +436,10 @@ I use the [`netlify-plugin-cache`](https://github.com/jakejarvis/netlify-plugin-
|
|||
|
||||
Whenever a change is detected on the `main` branch, a new version is deployed automatically.
|
||||
|
||||
## Accessibility
|
||||
|
||||
I tried hard to ensure that my site is fully accessible. I'm still learning how to properly test this - automated tools can only go so far - so if I missed something obvious [please tell me]({{ meta.social.mastodon }})!
|
||||
|
||||
## Conclusion
|
||||
|
||||
That was a lot, but I covered everything! I hope.
|
||||
|
@ -437,6 +454,8 @@ This has been a long (but fun) project, and I'm glad it's done. Now time to prod
|
|||
|
||||
If you'd like to view the source for my website, it's [available here](https://github.com/wonderfulfrog/wonderfulfrog.com)!
|
||||
|
||||
If you have any questions, please [reach out to me on Mastodon]({{ meta.social.mastodon }})!
|
||||
|
||||
Here are some resources I used (likely heavily) while building the site.
|
||||
|
||||
### Inspiration
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue