Subscribe

Get authentic content to your mailbox

Initialise Implementation

0:00
Log

With renewed enthusiasm I head back to Sime’s kitchen again in Kaikoura, New Zealand for an inspired atmosphere.

Sime's cafe, Kaikoura, NZ, wooden floors, bifold doors, curving timber barista station & serving counter.
Sime's cafe, Kaikoura, NZ

The goal today is to implement the blog’s home page as per the design after migrating to figma components.

I was going to need to get the CSS + content out of Figma and place it in my blank Astro project. I immediately discovered that Figma’s ā€œdev modeā€ is a paid only feature, but this really doesn’t matter as you can right-click Copy as->Copy as code->CSS which gives you the most useful aspect of dev mode anyway.

This is my first time working with Astro. As a stubborn JSX lover I arrived looking to utilise React components. However, taking a look at Astro components there really is little between them: Astro just has frontmatter delimiters to separate out data preparation from the return JSX. I’ve previously spent much time battling to get TSX support with Eleventy so the out-of-the box astro component experience is pretty slick.

I’m a tailwind convert. Prior to that I loved styled components: a key feature I liked was the semantic naming of the elements in the JSX which makes the code very descriptive, but tailwind is a seamless experience with almost zero faff. Less typing combined with its inline nature are the fundamental winning features. Astro makes it super simple to add, just an npx astro add tailwind and you’re away. I’m liking Astro so far.

For adding fonts I popped over to google-web-font helper. This site allows you to easily download Google web fonts for self hosting. Self hosting is generally more performant because even if visitors have a cached version of Google hosted fonts you still have to do double step of hitting Google’s servers and then asking for the (cached) fonts. I’ve seen situations where this is significantly slower. It also increases user privacy.

I was additionally going to need a new favicon. It feels like it has to be some variation of the hamburger. I tried out a few options:

Various favicon options
Favicon options

The one on the right feels the most logo-like… šŸš€.

Next up is the date display. I’m on the passionate side when it comes to internationalisation (i18n) and with that in mind I look to use the Intl api for date display. react-intl from FormatJS is my go to wrapper library for internationalisation concerns, however, this will require an IntlProvider react context setup, and of course Astro’s rendering model doesn’t really cater to this paradigm. It will take me a more deeper look to understand the HTML output rendering context, but suppose it would be more of a template renderer than straight JS execution. Astro can mix and match components from different libraries in the same template, so the rendering is probably done in fragments. Regardless, it’s different enough that context doesn’t work. I decide to lean into Astro components vs React ones here and just build a FormattedDate Astro component for now:

---
const {
  date: dateString,
  lang = Astro.currentLocale
} = Astro.props;

const date = new Date(dateString);
---

<time datetime={date.toISOString()}>
  {
    date.toLocaleDateString(lang, {
      year: '2-digit',
      month: 'short',
      day: 'numeric',
    })
  }
</time>

Note that this required setting up i18n in astro to enable the Astro.currentLocale setting.

astro.config.mjs:

i18n: {
  defaultLocale: 'en',
  locales: ['en', 'fr'],
  routing: {
    prefixDefaultLocale: true,
  },
},

I know this won’t be the end of it though, I will want a fully-fledged i18n approach in Astro land. For example, I’m going to additionally need to internationalise my post tags, and that won’t be content driven. More on this in my next post.

We now have the basic home page design implemented, but…

Browser window showcasing the website
Basic home page design implemented

…but not content driven. We still have to make the page source from mdx files (aka markdown jsx). It can’t just be plain markdown because we want to import e.g. the FormattedDate component we just created. Astro again makes this out of the box easy: yarn astro add mdx.

Astro has the concept of ā€œcollectionsā€ which are simply any content files placed in a subfolder of the content folder. Rather than importing posts directly which can be done, you declare a schema (pertaining to keys in the mdx files frontmatter) for your collections in content/config.ts like so:

import { defineCollection, z } from "astro:content";

export const collections = {
  type: "content",
  posts: defineCollection({
    schema: z.object({
      date: z.date(),
      title: z.string(),
      tags: z.array(z.string()),
      excerpt: z.string(),
      pinned: z.optional(z.boolean()),
    }),
  }),
};

and then we need to create a template that has a wildcard parameter in it’s file name like [slug].astro and declare which slugs it exports. Of course the slugs it exports will just be the contents that are available, so all in all:

import { getCollection } from "astro:content";

export async function getStaticPaths() {
  return (await getCollection("posts")).map(({ id }) => ({
    params: { slug: id },
  }));
}

From there it’s just a matter of wiring up the frontmatter data and the goal for today is achieved!