the developers nemesis CSS : grids

Jon Brookes

2024-12-18

TL;DR

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">&#8734;</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.