ObjectGraph Redesign Using Astro
My ideal workflow for a website would involve writing content in Markdown to be able to publish not just blog posts, but also product pages and other page types. I also take a lot of notes in Obsidian, and I appreciate their philosophy that your data should be future-proof, and what better way to ensure this than to store everything in plain text files (Markdown).
We have been around since 2004, so you can see the evolution of the website in the Wayback Machine. Below are screenshots if you prefer to scroll through them.
After 2015, we haven’t made any changes to the design as it was entirely based on an HTML template. My requirements are:
- All content should be in Markdown.
- Be able to embed dynamic elements within the Markdown, like slideshows, videos, random JavaScript components, etc.
I wanted to try Astro to see if it fits the bill.
npm create astro@latest
At this point, I needed to see if Cloudflare supported Astro, so I created a repository and configured Cloudflare Pages to automatically publish whenever it detects a commit on the main
branch - it worked like a charm.
Then I needed Tailwind CSS:
npx astro add tailwind
The dev toolbar was distracting me:
npx astro preferences disable devToolbar
I needed typography:
npx astro add tailwindcss/typography
Let’s get interactive with MDX:
npx astro add mdx
I also needed a Svelte component for Slideshow:
npx astro add svelte
You can see from my Astro file structure below.
I have two components so far, one for the Slideshow, which is written in Svelte.
<script>
let currentIndex = 0;
export let images = [];
// Array to hold CSS classes to control visibility
$: carouselItems = images.map((_, index) =>
index === currentIndex ? 'block duration-700 ease-in-out' : 'hidden duration-700 ease-in-out');
const nextImage = () => {
currentIndex = (currentIndex + 1) % images.length;
};
const prevImage = () => {
currentIndex = (currentIndex - 1 + images.length) % images.length;
};
</script>
<div id="default-carousel" class="relative w-full bg-slate-100 rounded-lg" data-carousel="slide">
<div class="relative h-56 md:h-[70lvh] overflow-hidden rounded-lg">
{#each images as image, index (image)}
<div class={carouselItems[index]} data-carousel-item>
<img src={image} class="absolute w-full object-cover top-0 left-0" alt={`Slide ${index + 1}`}>
</div>
{/each}
</div>
<div class= "absolute z-30 flex -translate-x-1/2 bottom-5 left-1/2 space-x-3 rounded-md border p-1 bg-gray-400 border-b-slate-400">
{#each images as _, index}
<button
type="button"
class="w-3 h-3 rounded-full {currentIndex === index ? 'bg-orange-200' : 'bg-slate-200'}"
aria-current={currentIndex === index ? 'true' : 'false'}
aria-label={`Slide ${index + 1}`}
on:click={() => { currentIndex = index; }}
></button>
{/each}
</div>
<button
type="button"
class="absolute top-0 start-0 z-30 flex items-center justify-center h-full px-4 cursor-pointer group focus:outline-none bg-slate-100 bg-opacity-15"
data-carousel-prev
on:click={prevImage}
>
<svg class="w-4 h-4 text-white dark:text-gray-800 rtl:rotate-180" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 6 10">
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 1 1 5l4 4"/>
</svg> <span class="sr-only">Previous</span>
</button>
<button
type="button"
class="absolute top-0 end-0 z-30 flex items-center justify-center h-full px-4 cursor-pointer group focus:outline-none bg-slate-100 bg-opacity-15"
data-carousel-next
on:click={nextImage}
>
<svg class="w-4 h-4 text-white dark:text-gray-800 rtl:rotate-180" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 6 10">
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m1 9 4-4-4-4"/>
</svg> <span class="sr-only">Next</span>
</button>
</div>
And you use it by passing a list of images:
<Slideshow client:only="svelte" images={[
'/img/geomeasuremac/sc1_2560x1600.jpg',
'/img/geomeasuremac/sc2_2560x1600.jpg',
'/img/geomeasuremac/sc3_2560x1600.jpg',
'/img/geomeasuremac/sc4_2560x1600.jpg',
'/img/geomeasuremac/jp/sc1_2560x1600.jpg',
'/img/geomeasuremac/jp/sc2_2560x1600.jpg',
'/img/geomeasuremac/jp/sc3_2560x1600.jpg',
'/img/geomeasuremac/jp/sc4_2560x1600.jpg'
]}/>
The contact form Svelte component is also super simple. This advantage allows me to add more interactive elements in the future. My contact.mdx
is very simple, as you can see below:
---
layout: ../layouts/Product.astro
title: Contact Us
description: Find out how to connect with ObjectGraph
---
### Contact
GET IN TOUCH
We welcome your comments and any business inquiries.
32 Union Square East
#1113
New York
NY 10003
E: [email protected]
### Contact Form
import ContactForm from '../components/ContactForm.svelte'
<ContactForm client:only="svelte"></ContactForm>
It’s important to note that if you do not specify client:only="svelte"
, the interactive elements will not function properly.
Overall, I am very happy with Astro, and now I have no excuses not to write blog entries as they are as simple as creating markdown files.