Not so very long ago, I wrote about using remark and a script called remark-modified-time.mjs to update a page’s front matter Date value for Auto-Generated Last Modified Dates in Astro. This approach worked pretty well until I moved the content for my /Uses page out of a markdown file and into a json file. I didn’t want to have to keep modifying essentially an associated empty markdown file to get the last modified date to change.
What I wanted was a way to see if the json file itself had changed, and then enter THAT date into the /Uses markdown file front matter. In essence, change one file, and another one gets its updated timestamp. This would give me a self-updating last modified date value for everything and I wouldn’t have to remember to manually change anything to make it happen.
Git hooks let you run a script when some git action occurs. One git related action that occurs all the time is the git commit. Git commit turns out to be a very good time to look at when files were last modified, since git’s entire job is paying attention to when and how files are modified. The git hook to use if you want it to happen when you perform a git commit is called pre-commit.
Although it’s called pre-commit, it happens during a commit but before you enter the commit message. It can be used to verify commits before allowing them to happen, but instead I use it to modify files and commit those new changes along with the existing ones.
Implementing a pre-commit git hook is simple. Go into your project’s .git/hooks directory and create a file called pre-commit, with no file extension. There should already be a file in there called pre-commit.sample – you can either rename it without the extension and edit it, or just create a new one with the correct name.
Here are the contents of my .git/hooks/pre-commit file:
Two points if you noticed that it’s just a bash script and that it clearly is running a git command to see what files have changed since the last commit!
The output of that command is piped to grep to look to see if any of the modified files are src/content/links/links.md, src/content/now/now.md, src/content/pins/pins.md, src/data/uses.json, or src/data/spotlight.json.1
Once it finds any modified files that match my list, it uses awk and sed to find the front matter and change the date value to git’s modified timestamp. I leave learning all about awk and sed as an exercise for the reader – see you in about 10 years.
For the most part, the files that are modified are the files that get their front matter date value updated, except in the case of the two json files. If src/data/spotlight.json is modified, src/content/links/links.md gets its timestamp instead. And if src/data/uses.json is changed, src/content/uses/uses.md gets that timestamp in its front matter date field.
Another way to look at it is that if any of the markdown files I’m looking for are updated, they get their timestamp set accordingly. In addition, links.md also gets an updated timestamp if src/data/spotlight.json is modified. This is because both links.md and spotlight.json contain data that shows up on Links.
But in the case of my /Uses page, I never look to see if src/content/uses/uses.md gets updated. That’s because I’m really only using it for its front matter at this point. All of the data displayed on the page itself comes from src/data/uses.json. So I only look to see if uses.json was modified, and if so, I update the timestamp in uses.md. Then I can use that value to display on the compiled page as the last modified date and time.
That’s it. It really is that simple to implement git hooks. They’re sitting inside .git/hooks in your repo just waiting to be used for exciting things like telling people when you added your mouse to the list of computer hardware that you gaze at lovingly on a daily basis.
Footnotes
Spotlight.json is used for the Cool Site Spotlight on my Links page. ↩
Web frameworks like Astro often allow the use of Markdown for blog post “content”, that is, the actual blog post, and then the page design and programming is in a page template which takes the Markdown and renders its contents in the appropriate place for a fully rendered HTML page. This is convenient and allows excellent separation of written words (“content”) and site rendering mechanisms.
Sometimes, though, what you want to put on the page is more complicated than you can easily do in Markdown. Such is the case with my /uses page on this website.
When I first created my /uses page, I thought I was going to do the same thing I do for my /links and /now pages… namely, use Markdown for the “data” (please don’t make me say content again) which would go into the Astro <Content /> component to render the Markdown into HTML.
But…
As Jason from Hemispheric Views stated rather enthusiastically during a recent episode, Markdown isn’t always the right tool for the job. And it’s NOT the right tool for the /uses job, because I want a format and flexibility for this page that Markdown makes difficult, if not impossible.
The result is that I created the /now page as an Astro page template with all the “data” (no, I will NOT) thrown in amongst the html tags, just like 1990 calling for its beautiful, manually handwritten pages back. But that’s gross. It’s gross because it’s harder to work with from an adding and editing perspective, and it just seems dumb.
The solution is very simple, and it’s what I do on Friends with Beer for the drinks. I use a JSON data file and render it in different Astro page templates.
The Starting Point
My original uses.astro file looks like this:
The text that is the whole point of the page, namely the items that I use that I wish to present, are just more stuff in a sea of rendering implementation.
You can see for yourself how un-fun it would be to add new items to or edit existing ones. It requires carefully trawling through everything and make sure I’m not messing up nested divs and all kinds of junk. Even with VSCode’s code folding and syntax highlighting, it’s still ugly and nightmarish.
Let’s fix this mess by breaking out the data from the drawing, so to speak.
The Modifications
A JSON Is Born
Step 1 is creating a data file for the stuff I use. I’m calling it uses.json, and I’m putting it in my data folder at src/data/uses.json.
Step 2 is figuring out the data structure. I break my stuff up into categories or sections, like “Computer” and “Podcast Recording”.1
You can see that in each category, there are multiple items. Each item consists of:
The name of the item,
Some specs,
An optional link to a website for the item,
My thoughts about it, aka a description,
An image of the item.
Based on that, here’s how I’ll structure my JSON data file.
This lets me have multiple categories, each with multiple items, each with the item data listed above.
Then I just have to modify my uses.astro page template to read it in and map it out to the template I’m using for the “data” section.
An Astro Page Template Gets Smarter
I lied just a little bit. I am going to modify uses.astro to parse the JSON data from uses.json, but I’m also going to create an Astro component called UsesItem.astro that will be used to render the individual items. So uses.astro is going to load the JSON file, map through the categories, and every time it hits a list of items, it’s going to hand the rendering for them over to the UsesItem.astro component.
This will break it things up, make re-use easier if I ever want to render any of these items elsewhere, and generally make me happier. And I like being happier.
Here’s what the /uses page template, uses.astro, looks like now with this approach:
All the information, or data, or content, about the items is GONE from the html template. It’s all in the JSON file, and uses.astro is just a set of display instructions.
Now the only part that’s hard-coded into the page template is the page introduction area indicated by the rectangle marker below, which is right at the beginning of the <article> section.
Everything following it comes from the JSON file and is mapped out in this section:
Very little of the work of rendering the items is done in uses.astro. Instead, it relies on the UsesItem component which it imports from UsesItem.astro. The current item in the map loop is passed as a prop to the component, which then renders the item detail view. Here’s my UsesItem.astro component in its entirety, and half of it is CSS:
This uses the astro:assets Image component to optimize the item’s image as specified in uses.json. It puts this next to the item name and a list of item features of specs. Under that is my description of what it is or why I like it, followed by an optional link to a website for the item.
You can see that item.Specs is an array and so I use item.Specs.map to create the list of specs. Also notice where the item URL link is rendered starts with item.Link && which means the following href rendering only happens if item.Link indeed exists.
The end result that is rendered is something like the following:
Summarium
You can definitely use Markdown for site content when appropriate, such as for a standard blog post. For anything more complicated, though, you’ll want to use a data source, like a JSON file, and a page template to parse it and map it. This way your data, or “content”, is separate from the rendering mechanics, but can be laid out on the page more intricately than is possible with a straight Markdown to HTML rendering.
The good news is that Astro page templates and components are super flexible in terms of easily mixing HTML, CSS, and JavaScript, and they’re the right tool to use for a one-off page with a custom layout.
Footnotes
That’s all I have right now because instead of adding things I use, I’m busy rewriting how the page is created. ↩
I don’t know if you’ve heard, but the Mac turned 40. And what a glorious 40 it is – with Apple Silicon and the return to some amount of function over form in the laptops and (most of the) desktops, the Mac is better than ever.
Personally, I started early on the Mac. My dad bought us matching 128K Macs the first year they came out. My Mac was upgraded to Fat Mac and then Mac Plus, and it served me far longer than it had any right to. By the time I got a current at the time PC at a discount from my employer, my Mac was way past its useful lifespan.
People have been telling a lot of fun stories about the Mac for its 40th anniversary. I highly encourage you listen to Upgrade #496: 40th Anniversary of the Mac Draft even if you’re not an Upgrade listener. Yes, it’s a draft, yes, John Siracusa actually drafts the current Mac Pro as the worst Mac ever, and yes, it is glorious. There’s also this peak Siracusa moment…
Here’s a list of some articles, podcast episodes, and videos I’ve enjoyed this week.
Not long ago, I created a Links page which I used to hold links I wanted to keep temporarily, like a little pinboard. But then I realized “HEY!! THIS IS BAD!!” People expect a links page to be things like blogrolls and stuff they might like, not the stupid stuff Scott Willsey wants to remember for a day or month or year or three.
So as of today, I now have Links, which is what people expect of a links page, and Pins, which is what I apparently expect of a links page.
Links is now, just to clarify, a permanent-ish list of links to things that YOU, the reader, might also find interesting.
Pins is, just to also clarify, where I’ll stick ephemeral stuff that I want to get back to but doesn’t make sense to stick in a read later app.
If you started using Nate More’s Astro Icon with Astro early on and are using a version lower than 1.0, you will be in for a surprise when you upgrade to the latest version of Astro Icon and suddenly find a lot of breaking errors related to it.
Migrating from 0.x to 1.x is simple but can be a little tedious depending on how many icons you’re using. A couple key things to understand are that you have to import packages for each icon set that you use now, and that this is now an Astro integration. Don’t worry if you’re not sure how that affects you. You’ll handle adjusting to those changes in the upgrade steps below.
By the way, Nate has a great upgrade guide already at Upgrade to Astro Icon v1 | Astro Icon. I just thought I’d write about it too, since I’ve done this on a couple different sites now and I still have a couple to go.
Upgrade to the latest version packages of astro and astro-icon (astro-icon >= 1.0)
Import and integrate astro-icon in your astro.config.mjs
Install @iconify-json/icon-family for every icon package you use
On every page you import astro-icons, change import { Icon } from "astro-icon"; to import { Icon } from "astro-icon/components";
Find and replace [astro-icon] tags with [data-icon] in your css
Find and replace any CSS width or height properties on [data-icon] entries and replace with font-size
Now go through every single instance of [data-icon] in your CSS and tweak the font-size that you just added.
1. Upgrade to the latest Astro and Astro Icon versions
I use npm-check-updates to update npm packages, but you can do it the standard way. The key thing to note is that in this instance I’m upgrading from astro-icon 0.8.2 to 1.0.2.
2. Import and integrate astro-icon in your astro.config.mjs
No explanation needed here – import astro-icon and add to the list of integrations.
3. Install @iconify-json/icon-family for every icon package you use
This one requires you to scour through every place you specify an icon package name. Search for [astro-icon in your project and look for instances of things like <Icon name="simple-icons:mastodon" /> or CSS instances like [astro-icon="bi:hand-thumbs-up-fill"] and take note of the package names (in these examples, simple-icons and bi).
Now install these by installing @iconify-json/package-name for each. E.g,
That will install the two packages in my example above.
4. Change your astro-icon import statements on Astro pages
The old way to import astro-icons in your .astro files is
Change all instances of this in your astro files to
5. Change all references to [astro-icon] in your CSS to [data-icon]
Another search and replace activity – [astro-icon] references have been replaced with [data-icon] references.
Old:
New:
6. Use font-size instead of width or height to set the icon sizes
You may have noticed in the previous section that I not only changed [astro-icon] to [data-icon] in my CSS reference to it, but I also removed the width property and replaced it with a font-size property instead.
After performing the first 5 steps, you’ll see wildly sized icons all over the place until you do this.
7. Tweak font-size on all CSS for your icons
Step 6 will get you in the ballpark, but you’ll now find that some of your sizes, while closer, are still off. Just play with font-size values until you get what you like. CSS specificity is key here – be specific about which icons you’re setting your CSS for and use scoped CSS by putting them in <style> tags in specific astro pages.
The first example below affects all icons, the second one affects the bi:hand-thumbs-up-fill icon specifically.
Summarium
That’s pretty much it. Once change you will notice when running the dev server is that the first time you try to load the site in the browser, Astro Icon has to load all the icon sets and it takes a little bit. Don’t worry. It will work. 🙂
Back when the iPad Pro was my main mobile “work” device,1Drafts played a huge role in my writing and blogging. For one thing, it has extensive automation and scripting capabilities, and those came in super handy on iPadOS. I created automations to let me choose photos from my photos library and add them to blog posts, including creating proper links and adding them to my site repo.
But now I have an Apple Silicon MacBook Pro and I’ve long since given up trying to fight my way to success through all the limitations of iPadOS. It’s hard to overstate how much faster and easier so many things are on the Mac, and given the current state of Apple’s Shortcuts, having tons of other reliable methods of automating things, as you do on the Mac, is incredibly nice.
All that to say, I realized today I haven’t really done anything in Drafts except type out blog posts and then paste them into VSCode when it was time to publish them. I’m no longer making use of all the great Drafts automations and integrations that I used to. And given THAT fact, I may as well do my writing in something that looks nicer and feels more modern as a writing app.
I own iA Writer but it bugs me in certain ways. I don’t like the non-standard footnote default that makes you manually type the footnote reference at the bottom yourself if you don’t want an inline footnote, and it’s also very limited on font choices.
Ulysses is also weird. It shows you some of the Markdown but tries to hide other Markdown, like URLs, in very inconvenient ways. It’s been a long time since I thought I was a fan of Ulysses.
Before Ulysses, it was Byword for me. To say it looks a little basic now is a bit of an understatement.
That leaves Bear as the only realistic Markdown writing option for me, and I have to say, I like how the editor looks. That’s important to me. If it wasn’t, I’d still be using Drafts because it’s a great app and I do really like Greg Pierce and the work he does on it. It’s just not as important to me on the Mac now as it was on iPadOS.
I’m a person who has a lot of idiosyncrasies, and one of them is that I need a nice looking editor to be able to enjoy the writing process. Bear definitely looks nice. The defaults are nice, customizing it is simple, and things like images and links look really nice in it.
Below is an image showing both the light and dark themes I’m currently using in Bear.
There’s not a lot I need to do in order to incorporate Bear into my writing workflow. I already have a script that names my images and puts copies of them in the right locations to both be optimized for the blog and to be able to link to the original. I’ll need to put an automation somewhere in the publish chain that updates the post’s Markdown file with those image paths and creates the links to the larger, original images. But that was also something I needed to do for my Drafts workflow, and hadn’t yet.
Also I think playing with Bear has given me some ideas for improving my site CSS a bit… 🤔
Footnotes
Not my WORK work device, but my personal project work device ↩
I’m trying to figure out how to use remark plugins in Astro to modify a couple things in posts for me automatically, and along the way I’ve used remark to add a couple quality of life improvements. The first is an auto-generated table of contents for longer posts that I feel need one, and the second is an auto-generated last modified date for pages based on git commit timestamps.
The benefit of an automatically generated last modified date is that I don’t have to remember to update it when I make changes to a page that displays it, like my now page or my links page. I can just commit my changes and the last modified date will update automatically. It’s simple, but it’s kind of beautiful.
I basically did exactly what Astro’s documentation says to do in a helpful recipe called Add last modified time. I created a file called remark-modified-time.mjs and put it in my src/components/utilities folder.1
Then I updated my astro.config.mjs file to reference this as a remark plugin, and to use it in my markdown processing.
Once those are set up, it’s just a matter of referencing remarkPluginFrontMatter.lastModified in the Astro template.
That’s it. Now I have auto-generated last modified dates for any page I feel needs one, thanks to Astro using remark to render markdown and therefore making adding remark plugins super simple.
Footnotes
You can put your remark plugin anywhere you want, as long as you reference the path correctly in astro.config.mjs. ↩