a laptop and a phone on an orange background with the same text on the screen and the same text on the other side of the screen

astro portfolio redux

a grounds-up rebuild of a portfolio site with astro's latest and greatest

EJ /

This rebuild was something of a labor of love. I’ve been working on it on and off for a couple of weeks now, and continuously updating to the latest releases of Astro. I also wanted to extend the previous iteration and make this more flexible and adaptable to difference uses cases. With a few modification, it can be a personal portfolio or even a small agency’s site. Without further ado, let’s get into it.

The Scaffold

The rebuild started with the following command: yarn create astro portfolio. I elected to go to include sample files and install dependencies, TypeScript (Strict), and initialize a git repo. (And here, take a moment to appreciate the fact that Astro’s generator is a work of art.)

astro scaffolding

A few more dependencies were added to the project:

  • yarn astro add mdx for markdown support. Yes to add the dependencies, and Yes to update astro.config.mjs - neat!

  • yarn astro add tailwind. Yes to add the dependencies and but No to generate a minimal ./tailwind.config.mjs file (I’ll generate a .ts file manually.) Yes to update astro.config.mjs.

  • npx tailwindcss init --ts to generates a tailwind.config.ts file (type safety for the win.)

…and a few more dependencies

  • yarn add -D sass @tailwindcss/aspect-ratio @tailwindcss/typography probe-image-size @types/probe-image-size

  • I updated tailwind.config.ts to include the aspect ratio and typography plugins and a couple of files to the content property.

Once the basics were in place, I proceeded to rest of the site: configuring content and defining schemas, site config file, components, styles, etc.

Breaking it down

Here’s a quick overview of the project structure, with a few notes on the more interesting bits of the code, along with reason behind some of the decisions. Word of warning: this is a work in progress and the code will change as I continue to work on it and Astro continues to update their framework.

src/components/

The components folder holds all the site’s components. Most are pretty straight forward, but the card and hero components include logic to make them more reusable. For instance, the card component can be short (without a thumbnail), or long (with image and additional info). The hero component can be used on a card to render a thumbnail, or on a page to render all media including images, videos, and 3d renderings. For styling, checkout the styling section.

src/content/

All the site’s content including articles, projects, clients and people. The content is organized into folders by content type and defined in config.ts.

src/layouts/

Nothing special here. Three simple layouts to cover most of the site’s needs: Layout.astro which is the base layout upon which others are build on, Landing.astro for the landing and non-article pages, and Article.astro for projects and blog content.

src/pages/

Nothing special here.

src/schemas/

Nothing special here.

src/styles/

Any styles that are shared across more than component or files, is found here. This simplifies the developer experience and maintains consistency across the site. Styles that are component specific, are contained therein.

src/types/

The hero and metadata types are define here. The metadata type has ShortMetadataType for content types like landing pages and person that do not require attributes like author, dates, etc. LongMetadataType extends ShortMetadataType and adds those attributes for articles content types.

The hero type includes:

  • alt for accessibility,
  • caption and credit for captioning attribution,
  • height and width for sizing which can be calculated on the fly,
  • src, obviously,
  • type enum for image, video, or 3d rendering, and,
  • thumbnail to be used on cards and hero background where the type is video or 3d rendering,

src/utils/

The two files here, getContent.ts and getHashtags.ts, are used to prepare the site’s content for use in the site - from parsing to filtering to sorting. In getContent.ts, I added url to the content object in order to customize the URL structure of different sections of the site. Consequently, I had to extend the collection entry type to squash some TypeScript errors. Here’s how I extended the project collection entry type:

type ExtendedProjectCollectionEntry = CollectionEntry<"project"> & { url: string; }

In getHashtags.ts, I added a count property and function to the hashtag object in order to sort the hashtags by occurrences/popularity. This can be extended in the future to render them in a tag cloud. Most if not all of the files and functions here are well-documented.

src/config.ts

The config.ts file holds the site’s metadata and a few other pieces of info that can be referenced throughout the site. I think the benefit of centralizing things like site name, url, email, landing page metadata, etc, is that they can be referenced from multiple places but only needs to be updated in one place.