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!