Jon Brookes
2024-12-18
the code for this missive is here in which a very simple Astro app is used to demonstrate how to quickly create and experiment with an example codepen that shows off what can be done with CSS Grid layouts and picture galleries
lets try and create a project with minimum effort that uses Astro as a framework to give us rapid template development as this will require for a lot of copy / pasting otherwise as grids, like many other HTML components are formulaic and repeated
npm install astro@latest
accepting all defaults and creating a random name project, why not ?
this creates a project directory called in my instance bustling-binary
so I cd into that and code .
to open this in vscode
and then, to get a page / dev environment running npm run dev
and we have a page in our browser running on http://localhost:4321
and at time of writing an exciting message saying whats new in version 5
https://astro.build/blog/astro-5/
no time just now to read that but I’m sure even with all the new stuff I can create a simple page with a page layout and components
so, whilst the origonal default page that comes with the new Astro project is fun and all, we want something ultra simpler even that it gives us out the gate so I can just blow away what is there and start over, referring to astro docs on layouts initially which sets out two files
src/layouts/MySiteLayout.astro
src/pages/index.astro
the layout file has a head matter that Astro uses to import any components and props it needs to use site wide that can look like
---
import BaseHead from '../components/BaseHead.astro';
import Footer from '../components/Footer.astro';
const { title } = Astro.props;
---
this is followed by any HTML and Astro templates you want but the most important for a layout to work in this context is
<!-- head of page goes here -->
<BaseHead title={title}/>
<!-- main body of page follows ... -->
<slot /> <!-- your content is injected here -->
where the page title is rendered using {title}
and the page content is yielded into <slot />
the index page is even simpler
---
import MySiteLayout from '../layouts/MySiteLayout.astro';
---
<MySiteLayout title="Home Page">
<p>My page content, wrapped in a layout!</p>
</MySiteLayout>
it crucially imports the layout file to then use it in the style of a component with <MySiteLayout title="home Page"> ...
and passing both a prop and content for the slot
cheeking on the default content already created this is what we already have, so I dont see a problem with just re-using what is already there but stripping out any styles / assets it currently uses as these would likely break a grid / full page layout that I am about to try out
so all I needed to do for this was to remove the <style>
entry from the layout and in the index file I now have
---
// import Welcome from '../components/Welcome.astro';
import Layout from '../layouts/Layout.astro';
---
<Layout>
<h1>I am a mole and I live in a hole</h1>
</Layout>
the <Welcome>
component is no longer in use and I have simple, ugly unformatted HTML, just like in the 90s
now, on with the excercise to scaffold out a CSS grid as taken from this wonderful YouTube video by Optimistic Web
see also this codepen for a demo of this and its HTML and CSS
first up I don’t like in-lining CSS for things like this example that start out as traditional CSS page layouts, so into my layouts file I add
<link rel="stylesheet" href="/style.css" />
to the <head>
section and pasted the codepen CSS into public/style.css
which has an immediate effect to my ugly HTML
still very ugly but it proves that the styles are being applied so we have this bit working so far
on the the interesting bit, I hope, certainly for me that is, which is the motivator to writing this missive !
the repeated HTML piece looks like this
<figure>
<img src="https://images.unsplash.com/photo-1494976388531-d1058494cdd8"
alt="Lorem ipsum dolor sit amet">
<figcaption>
<h3>Automobile</h3>
<p>Lorem ipsum dolor sit amet consectetur adipisicing elit.</p>
</figcaption>
</figure>
and there are 20 of these in the above codepen example so for starters, I want a JSON file that looks like this
[
{
"url": "https://images.unsplash.com/photo-1458668383970-8ddd3927deed",
"caption": "Photo by John Doe",
"text": "Et harum est temporibus sint quisquam. Quam qui labore qui quas numquam. Mollitia a nihil odio quia aliquam aut."
},
........
]
the url field I can copy / paste from the codepen code but the text I’d like to randomise so the following one liner in bash creates 20 text entries with random lorem text :
❯ for i in {1..20}; do echo "\"text\": \"$(lorem)\""; done > /tmp/tmp.txt
and I read the tmp.txt file into my json file and hacked each one into its record placement like in the above
so that in the index.astro
page we can add
import siteData from '../data/site.json';
which brings this data into our app such that it can be iterated over in our Astro templates
and this we can find in Astro documentation for Dynamic HTML
my code to replace the copy paste of 20 items in HTML now looks like this
<main class="main">
<h1>CSS Grid - Photo Gallery</h1>
<div class="gallery">
{
siteData.map((rec) => (
<figure>
<img src={rec.url} />
<figcaption>
<h3>{rec.caption}</h3>
<p>{rec.text}</p>
</figcaption>
</figure>
))
}
</div>
</main>
much much shorter and easy to read, oh and it works !
the data we can edit separately in site.json
Astro will re-render for us in npm run dev
mode and create a static site rendering also with npm run build
that we can production preview with npx serve dist/
, after re-building or if we prefer, npm run preview
when scaffolding this out earlier, I stripped out the <Welcome>
component that was added for me by the Astro setup process but I can easiliy put my own back in to further simplify the above index.astro
file which may seem contrite but it shows how this can be further developed and a simple quick project can turn into a proper, engineered and well organized one
first, we can create a component to host the <figure>
block in new file named src/components/GalleryFigure.astro
:
---
const { rec } = Astro.props;
---
<figure>
<img src={rec.url} />
<figcaption>
<h3>{rec.caption}</h3>
<p>{rec.text}</p>
</figcaption>
</figure>
where the ‘props’ Astro gives to us is ‘de-structued’ to provide rec
being the record we used from earlier
now, this can be imported and used in index.astro
---
import Layout from "../layouts/Layout.astro";
import siteData from "../data/site.json";
import GalleryFigure from "../components/GalleryFigure.astro";
---
<Layout>
<aside class="sidebar">
<div class="logo">∞</div>
</aside>
<main class="main">
<h1>CSS Grid - Photo Gallery</h1>
<div class="gallery">
{siteData.map((rec) => <GalleryFigure rec={rec} />)}
</div>
</main>
</Layout>
and we now import GalleryFigure
component to then pass as a property rec
containing each record parsed out for us from site.json
as site.json
is entirely separate to our actual Astro, HTML and template code, it can be handed off to another process capable of creating or otherwise handling structured JSON data, that is a pipeline or script need know nothing about Astro to retrieve data and feed it into this tiny web app
this I believe gives us a simple, scalable and extendable approach to rapidly develop web content using a component based framework, eficient static html but with the dynamic nature that Astro brings to the static web site space
Other static site generators are available, of course, but Astro is a good starting point for anyone with a knowledge of JavaScript and that want a HTML first, low or no JavaScript static site output
If you need JavaScript Single Page Application like functionality, Astro works with React, Vue, Svelte, Preact, Alpine, so you can pick your preference and it uses what are called ‘islands’ in which an SPA can be embedded into a page of a multi page static Astro site which sounds a bit weird but am also interested in version 5 now just out that can create ‘server islands’ which create server side rendered parts within a static site that are rendered on demand, at the server and which serialize HTML to the client I’m guessing in a similar manner to that of Laravel’s Livewire, perhaps Hotwire with Rails. Very interesting and I would like to explore this aspect of Astro at a later date, perhaps even to integrate it with Laravel, who knows it could be similar to NextJS integrations but I’m certainly not there yet so will wait and see.