diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/components/BaseHead.astro | 44 | ||||
| -rw-r--r-- | src/components/BlogFeed.astro | 76 | ||||
| -rw-r--r-- | src/components/FormattedDate.astro | 21 | ||||
| -rw-r--r-- | src/components/Me.astro | 43 | ||||
| -rw-r--r-- | src/consts.ts | 2 | ||||
| -rw-r--r-- | src/content/blog/2024/01/14/sapiens_on_religion.md | 77 | ||||
| -rw-r--r-- | src/content/blog/2024/01/29/euhwc_toast_to_the_lasses_2024.md | 261 | ||||
| -rw-r--r-- | src/content/blog/2024/03/30/easter.md | 108 | ||||
| -rw-r--r-- | src/content/config.ts | 19 | ||||
| -rw-r--r-- | src/env.d.ts | 2 | ||||
| -rw-r--r-- | src/layouts/BlogPost.astro | 47 | ||||
| -rw-r--r-- | src/layouts/Page.astro | 17 | ||||
| -rw-r--r-- | src/pages/blog/[...slug].astro | 20 | ||||
| -rw-r--r-- | src/pages/blog/index.astro | 17 | ||||
| -rw-r--r-- | src/pages/error.astro | 14 | ||||
| -rw-r--r-- | src/pages/index.astro | 11 | ||||
| -rw-r--r-- | src/pages/rss.xml.js | 17 |
17 files changed, 796 insertions, 0 deletions
diff --git a/src/components/BaseHead.astro b/src/components/BaseHead.astro new file mode 100644 index 0000000..e8e44ab --- /dev/null +++ b/src/components/BaseHead.astro @@ -0,0 +1,44 @@ +--- +interface Props { + title: string; + description: string; + image?: string; +} + +const canonicalURL = new URL(Astro.url.pathname, Astro.site); + +const { title, description, image = '/images/headshot.jpg' } = Astro.props; +--- + +<!-- Stylesheets --> +<link rel="stylesheet" href="/css/reset.css" /> +<link rel="stylesheet" href="/css/base.css" /> +<link rel="stylesheet" href="/css/hcard.css" /> + +<!-- Global Metadata --> +<meta charset="utf-8" /> +<meta name="viewport" content="width=device-width,initial-scale=1" /> +<link rel="icon" type="image/svg+xml" href="/favicon.svg" /> +<meta name="generator" content={Astro.generator} /> + +<!-- Canonical URL --> +<link rel="canonical" href={canonicalURL} /> + +<!-- Primary Meta Tags --> +<title>{title}</title> +<meta name="title" content={title} /> +<meta name="description" content={description} /> + +<!-- Open Graph / Facebook --> +<meta property="og:type" content="website" /> +<meta property="og:url" content={Astro.url} /> +<meta property="og:title" content={title} /> +<meta property="og:description" content={description} /> +<meta property="og:image" content={new URL(image, Astro.url)} /> + +<!-- Twitter --> +<meta property="twitter:card" content="summary_large_image" /> +<meta property="twitter:url" content={Astro.url} /> +<meta property="twitter:title" content={title} /> +<meta property="twitter:description" content={description} /> +<meta property="twitter:image" content={new URL(image, Astro.url)} /> diff --git a/src/components/BlogFeed.astro b/src/components/BlogFeed.astro new file mode 100644 index 0000000..d785524 --- /dev/null +++ b/src/components/BlogFeed.astro @@ -0,0 +1,76 @@ +--- +import type { CollectionEntry } from 'astro:content'; +import { getCollection } from 'astro:content'; + +export interface Props { + headingLevel?: 1 | 2 | 3 | 4 | 5 | 6, + hideAuthor?: boolean, +}; + +const { headingLevel = 2, hideAuthor = false } = Astro.props; + +const posts = (await getCollection('blog')); + +const distinctYears: number[] = posts + .map(post => post.data.pubDate.year) + .reduce<number[]>((acc, curr) => acc.includes(curr) ? acc : [...acc, curr], []) + .sort((a, b) => b - a); + +function matchesYear(year: number) { + return (post: CollectionEntry<'blog'>) => post.data.pubDate.year === year; +} + +function sortByPubDateDescending(post1: CollectionEntry<'blog'>, post2: CollectionEntry<'blog'>) { + const year1 = post1.data.pubDate.year; + const year2 = post2.data.pubDate.year; + const month1 = post1.data.pubDate.month; + const month2 = post2.data.pubDate.month; + const day1 = post1.data.pubDate.day; + const day2 = post2.data.pubDate.day; + + if (year1 !== year2) { + return year2 - year1; + } else if (month1 !== month2) { + return month2 - month1; + } else { + return day2 - day1; + } +} + +const headingElem = `h${headingLevel}`; + +const canonicalUrl = new URL(Astro.url.pathname, Astro.site) +--- + +<section class="h-feed"> + <Fragment set:html={` + <${headingElem} class="p-name"> + My blog + </${headingElem}> + `} /> + + <aside> + <p class={hideAuthor ? 'hidden' : ''}> + This blog is written by <a class="p-author h-card" href="/">Joe Carstairs</a> + </p> + + <p> + <a class="u-url" href={canonicalUrl}>Permalink</a> + </p> + </aside> + + <ul> + { distinctYears.map(year => ( + <li> + {year} + <ul> + { posts.filter(matchesYear(year)).sort(sortByPubDateDescending).map(post => ( + <li class="h-entry"> + <a class="u-url p-name" href={`/blog/${post.slug}`}>{post.data.title}</a> + </li> + )) } + </ul> + </li> + )) } + </ul> +</section>
\ No newline at end of file diff --git a/src/components/FormattedDate.astro b/src/components/FormattedDate.astro new file mode 100644 index 0000000..bb7a2c0 --- /dev/null +++ b/src/components/FormattedDate.astro @@ -0,0 +1,21 @@ +--- +interface Props { + className?: string; + date: Date | string; +} + +let { className, date } = Astro.props; +if (typeof(date) === 'string') { + date = new Date(date); +} +--- + +<time datetime={date.toISOString()} class={className ?? ''}> + { + date.toLocaleDateString('en-GB', { + year: 'numeric', + month: 'long', + day: 'numeric', + }) + } +</time> diff --git a/src/components/Me.astro b/src/components/Me.astro new file mode 100644 index 0000000..c9af587 --- /dev/null +++ b/src/components/Me.astro @@ -0,0 +1,43 @@ +--- +--- + +<section class="h-card"> + <img class="u-photo" src="/images/headshot.jpg" height="256" width="256" /> + + <div> + <h1> + <a class="p-name u-url u-uid" href="https://joeac.net" rel="me"> + Joe Carstairs + </a> + </h1> + + <p> + Hi! 👋 My name is <span class="p-given-name">Joe</span> + <span class="p-family-name">Carstairs</span>. I’m a + <span class="p-job-title">software developer</span> at + <a class="p-org" href="https://www.scottlogic.com">Scott Logic</a>, a + graduate of Philosophy and Mathematics at the University of Edinburgh, + a committed Christian and a pretty rubbish poet. + </p> + + <p> + I’m also the <span class="p-job-title">secretary</span> of the + <a class="p-org" href="https://scotsleidassocie.org">Scots Language Society</a>. + <a href="https://github.com/joeacarstairs/lallans-wabsteid-astro">Help me maintain our website!</a> + </p> + + <p> + Email me at + <a class="u-email" href="mailto:joeacarstairs@gmail.com">joeacarstairs@gmail.com</a> + with your thoughts on metaethics, Scots verse and eschatology. + </p> + + <p> + Or get me on + <a href="https://www.facebook.com/joe.carstairs.5" rel="me">Facebook</a>, + <a href="https://mastodon.social/@joe_carstairs" rel="me">Mastodon</a>, + <a href="https://www.linkedin.com/in/joe-carstairs-0aa936277" rel="me">LinkedIn</a>, + or <a href="https://github.com/joeacarstairs" rel="me">GitHub</a>. + </p> + </div> +</section>
\ No newline at end of file diff --git a/src/consts.ts b/src/consts.ts new file mode 100644 index 0000000..461043d --- /dev/null +++ b/src/consts.ts @@ -0,0 +1,2 @@ +export const SITE_TITLE = 'Joe Carstairs'; +export const SITE_DESCRIPTION = 'Joe’s personal website.';
\ No newline at end of file diff --git a/src/content/blog/2024/01/14/sapiens_on_religion.md b/src/content/blog/2024/01/14/sapiens_on_religion.md new file mode 100644 index 0000000..b484557 --- /dev/null +++ b/src/content/blog/2024/01/14/sapiens_on_religion.md @@ -0,0 +1,77 @@ +--- +title: Harari’s Sapiens on Religion +description: >- + In which I discuss why I think Harari’s characterisation of religion + is inadequate because it’s too materialistic. +pubDate: + year: 2024 + month: 01 + day: 14 +--- + +I’ve been slowly re-reading Yuval Noah Harari’s 2014 classic, +<a href="https://www.ynharari.com/book/sapiens-2">Sapiens</a>, +which apart from being ridiculously over-scoped and hilariously +under-evidenced, is proving delightfully entertaining. + +I’ve just finished chapter 12, covering the world history of all +religion in thirty pages. Of course, at that level of brevity, +there will be many deficiencies. But here’s some thoughts - not +terribly well organised - which stand out to me. + +Hurari generally assumes a materialist metaphysic (a problem which +blights the book more generally). Nothing exists except physical stuff. +This gives him severe tunnel vision. As a consequence of this +restricting metaphysic, he is forced to adopt limiting accounts of what +the role of religion is in world history, and therefore what religion is. + +> The crucial historical role of religion has been to give superhuman +> legitimacy to [all social orders and hierarchies]. +> Religion can thus be defined as <em>a system of human norms and +> values that is founded on a belief in a superhuman order</em>. +> <footer>p. 234</footer> + +It might seem a little unfair to criticise Harari for giving a +materialist account of religion. <i>Sapiens</i> is, after all, a +materialist world history. + +But this account is just one extreme example of how that project, to +give a materialist account of world history, will inevitably lack the +metaphysical resources to really understand the human story. + +On Harari’s view, any human enterprise which attempts to understand +that which transcends direct human experience is at best an effort in +imaginative story-telling. All scientific theory, theology, ethics and +metaphysics either contorted out of all recognition into a pragmatic +fiction or is cast to the flames. + +In particular, it’s a view which is incapable of taking seriously some +of the most important questions human beings have grappled with in the +course of their history. Those who know me won’t be surprised at which +ones I’m going to pick out: who was the being which made their covenant +with Abraham? How is that promise being fulfilled? And who the heck was +Jesus of Nazareth? + +If Harari’s characterisation of religion is adequate - and the Abrahamic +faiths come under that banner - then those questions are reduced to +nothing more profound than Doctor Who fans arguing over ‘canon’. The +question of who God is becomes a mere tool for the organisation of +society, rather than a substantial and important question on a matter +of fact. + +This is a shortcoming for its own sake: a materialist account of +religion cannot adequately account for the phenomenon of religion +itself. + +But it is also a shortcoming even by its own lights. Without giving +serious consideration to the substantial matter of what Harari calls +‘religion’ (which, to his mind, includes the Abrahamic faiths, +Hinduism, paganism, animism, Buddhism, Shintoism, Confucianism, +capitalism, communism and Nazism), even the material facts are +inexplicable. Why would, as Harari is keen to point out, out, people +fight and die over and over again for a fiction? + +The material facts themselves prove that ‘religion’ as he construes it +is not window dressing to the real story of history. It cannot merely +serve as a mechanism in the churning of material history. It is itself +the centre of the story. diff --git a/src/content/blog/2024/01/29/euhwc_toast_to_the_lasses_2024.md b/src/content/blog/2024/01/29/euhwc_toast_to_the_lasses_2024.md new file mode 100644 index 0000000..8641a5d --- /dev/null +++ b/src/content/blog/2024/01/29/euhwc_toast_to_the_lasses_2024.md @@ -0,0 +1,261 @@ +--- +title: EUHWC Toast to the Lassies 2024 +description: >- + At the <a href="https://www.euhwc.co.uk">EUHWC</a> Burns meet in + Ullapool last weekend, I had the last privilege of giving the Toast to + the Lassies. Particularly for the benefit of those who weren’t there, + here it is in full! +pubDate: + year: 2024 + month: 01 + day: 29 +--- + +Had Burns, instead of his sweet bonnie Jean,<br> +his skills poetical for to mature<br> +had any one of our club’s lassies seen<br> +he would forever have remained obscure.<br> +If he had nothing but this box of worms<br> +Scotia would have been poorer, that I’m sure.<br> +Now none of us can claim to be a Burns,<br> +I’m no poetic master, still, I’ll have a punt,<br> +though let’s be clear, I’ll do it on my terms.<br> +I’ve everywhere avoided being blunt -<br> +politeness matters more than any schema -<br> +but it is hard when Isla’s such a cunt.<br> +It was a challenge to produce a terza rima<br> +I could recite withouten snoring;<br> +you’ve been so stiff I thought youse had oedema.<br> +The bother is this year is you’ll all been boring:<br> +no drugs, no sex, no gossiping or lies,<br> +no rock and roll, and hardly any whoring.<br> +But hey well, rules is rules, I’ve had to try!<br> +At least it can’t be worse than the reply.<br> + +I’ll start with Audrey, the club’s senior member,<br> +for if there’s something that I say which disconcerts her,<br> +it’s fine: the poor old girl, she won’t remember.<br> +She likes to let us think she’s a hard worker<br> +but we’re electing a third social sec…<br> +it’s pretty clear she’s just another shirker.<br> +This lady, half American, half Czech,<br> +for study, moved to Scotland for to do<br> +American history – really, what the heck?<br> +The club is so much louder thanks to you:<br> +impressive vocals for just five foot two.<br> + +That woman, Willow, reggles is bespeckled<br> +with her sickle and her fishing tackle<br> +shackled by the shins while she is heckled;<br> +the way that Willow waddles maks me cackle<br> +like a speckled jackal getting tickles,<br> +worth a shekel in the tabernacle;<br> +I chuckle muckle at her love of pickles<br> +which she wiggles when she has the heart<br> +while work for the Committee’s fickle trickles.<br> +Her modus operandi: <em>you can’t rush art.</em><br> +Her reimbursements programme’s going great;<br> +any day now, she’ll maybe even start.<br> +She cannot walk without Audrey, her mate:<br> +I wonder when they’re going to consummate.<br> + +Although they make them pretty tough in Peebles,<br> +the thought of actually going up a peak<br> +fills Shona Lewis with the heeble-jeebles.<br> +New car? We miss your beautiful antique!<br> +How long before this one’s also up a creek?<br> + +Once there was a lass called Hannah Collier<br> +whom even hell below regarded nasty,<br> +deeply despised by all that dwells there.<br> +Dating’s proceeding slowly for our lassie;<br> +not far from giving up til she beguiles<br> +a hot Italian in Southsider: classy!<br> +At first, Michaelo seems to be all smiles<br> +till it transpires he’s one of Dante’s demons…<br> +I guess it’s back to posters of Harry Styles.<br> +One day you’ll get a decent boy, keep dreamin;<br> +somewhere there waits a handsome Mr Collier.<br> +Hopefully when she meets him she’ll no be steamin.<br> +Hannah, I’m not sure why you chose to maul your<br> +poor skeleton at Subway (she’s still tetchy)<br> +and then abandon what remains of all your<br> +dignity at Ryvoan with a Frenchie!<br> +I think he wishes that he never met ye.<br> + +And has a quiet Felicia e’er been seen?<br> +The energy she has is frankly wild.<br> +I’ve never seen a hillwalker so keen!<br> +<i lang="de">Ssie ischt raschtlos und nie gelangweilt</i>.<br> +She eats raw oats with soggy protein powder:<br> +a camping pot has ne’er been worse defiled.<br> +She uses what her Maker has endowed her<br> +with: her recorder skills are off the charts;<br> +youse think I’m joking, but I wouldn’t doubt her!<br> +This lass of the land of the Rot-Gold-Schwarz<br> +will soon depart, though long we might beseech ya<br> +to stay. Of course, you’ll break all of our hearts,<br> +but mine most of all. Any time, Felicia,<br> +Creag Meagaidh calls, I know routes up the rear<br> +dark and under-explored that I can teach you!<br> +I won’t deny I think it’s rather queer<br> +the things you do with chickpeas, but no matter.<br> +You’re keen, you’re quick, you’re cool, that much is clear.<br> +In fact, I think you’d make a damn good faffer:<br> +swoop down on distilleries like the Luftwaffe.<br> + +And now we come to our girl Emily Topness!<br> +You’re keen for social sec. You’d suit the role<br> +because… I’m not quite sure, it’s embdy’s guess.<br> +We met your sister, and she was just as dull.<br> +No, please drone on about Icelandic soil!<br> +Poor Joe here down the front’s bored out his skull.<br> +And since I mentioned Joe – I hate to spoil<br> +it for you – but you’ve got the inferior Joe,<br> +by Jove, no joke, it’s Jock here’s got the style!<br> +Nah, write the boy a sonnet, get in the flow,<br> +Whatever you produce’ll beat by thrice<br> +your Masters thesis. What’d you got to show<br> +for months of hunting for the butterflies?<br> +‘There weren’t any.’ Oh, and have some sense,<br> +cos I’ve heard rumours – I assume they’re lies –<br> +you’ve called yourself the ‘poet in residence.’<br> +You know you can’t compete, drop the pretence.<br> + +Tereza was our gear sec for last year.<br> +She helped herself to stuff: that’s factual.<br> +Now when she asks to loan a bit club gear<br> +we have to ask her to provide collateral.<br> +She picked up tin whistle pretty sharp!<br> +Which is to say, she’s not a natural.<br> +She’s nowhere happier than under tarp<br> +gazing up at the moon and stars alone<br> +somewhere distant and remote like Glen Tarff.<br> +Now what to say about Lucy Ma-the-soooon....<br> +she likes… to faff… mm hmmm… well, moving on!<br> + +And now we come to Emilie the French.<br> +She seems to be nice on the trips we see her<br> +but my distrust of frogs will ne’er be quenched.<br> +Claims she’s a ‘pharmacist’? So she’s a dealer.<br> +Need some pills in a pinch? You call, she’s there<br> +at your door in her rally-approved four-wheeler.<br> +One question we have is, why are you here?<br> +Most folk are in uni, you’ve no refutin<br> +you were kicked out after second year!<br> +Now the Engineering grad, Sophia Newton.<br> +Your namesake, Isaac, was a man convicted,<br> +constructed calculus; but no computin,<br> +not even Isaac’s, could’ve e’er predicted<br> +you’d drop the Eng for creative writing!<br> +now that’s what I would call a self-inflicted<br> +inflection point! It must be quite enlightening,<br> +but that doesn’t excuse when you give us an earful.<br> +The blood boils in our veins, the rage heightening,<br> +and you’re an American, that makes me fearful.<br> +What’s your secret? You have us knackered!<br> +What are you on to always be so cheerful?<br> +Now we approach the topic of Merzbacher.<br> +Wait, she’s not here? Abandoned ship?<br> +She says she’s informatics: so she’s a hacker?<br> +She has strong views, she lets her anger rip.<br> +Poor George got an earful, full of future advice,<br> +but why hasn’t she been on another club trip?<br> +We’re cruel to focus on this list of vice;<br> +the fact remains: she’s headstrong and nice.<br> + +On Skye, a lady gave her poles to Sasha,<br> +which was really nice - I mean just the best -<br> +but Sasha really didn’t have to flash her.<br> +Quick history lesson: way back, RBS<br> +led the banking system to self-destruct<br> +and left taxpayers to pick up the mess.<br> +Since then, the name’s so irredeemably fucked<br> +they’ve had to ditch the brand once and for all.<br> +There’s one lassie who I need not instruct<br> +What, these days, the Royal Bank is called<br> +cos NatWest’s nasty history of scandal<br> +didn’t stop Booth from working there at all.<br> +Nothing motivates her more than to trample<br> +upon the working class. They set her free.<br> +She sank the pound quicker than the Belgrano,<br> +because ‘there is no such thing as society,’<br> +that’s how it is, is it? All right, I see.<br> + +Now, coming all the way from Glenmore Lodge,<br> +it’s Ellie’s turn! We have done what we can,<br> +although I’m scared what she’ll put in my squash.<br> +She wasn’t into Benji, but listen man,<br> +you’re lucky that you dodged her drunken benders.<br> +You’ll wake up in a tent in Kyrgystan,<br> +as for how you got there, no-one remembers,<br> +and if you’d known you’d be sleeping next to Ellie,<br> +you would’ve brought some fucking ear defenders.<br> +She’ll wrap you in bubblewrap, from your ears to your belly,<br> +cotton clothes for none, and no complaining,<br> +applying safety to the max, spare socks in your wellies.<br> +She’s always at her Mountain Leader training,<br> +practicing her night nav in the locale,<br> +pursuing QMDs - unless it’s raining.<br> +But some water should not scare our gal!<br> +She’s had much experience with the wet as of late:<br> +after all, she got on well with our navy pal.<br> +What was the age of that particular first mate?<br> +Older than your ex - always part of the plan?<br> +Ah, of course! He was a spry twenty-eight!<br> +Youth’s for the losers, let’s get you a real man,<br> +mature and rugged, but kind and astute?<br> +Just make sure he’s not as old as your gran.<br> +One request we all have is you ditch the uke:<br> +never have strings been pluckèd quite so shitely;<br> +we would all much rather be hit by a nuke.<br> +And please shut up about your nice society.<br> +We are all glad you had a fun summer,<br> +but bringing it up throws us right back to sobriety.<br> +To lose you of course would be a bummer:<br> +that is, for your carefully groomed newcomers.<br> + +Now time for the main woman, El Presidente!<br> +To here, it’s been like getting stones to bleed,<br> +but in Isla Burslem’s case we’ve material aplenty!<br> +As Holy Scripture says, ‘let those who lead<br> +well be worthy of double honour,’ so<br> +your bit is double length – it’s quite the screed!<br> +I’ll start off with her brilliant boyfriend – oh!<br> +Not boyfriend! Friend? To me this rather smacks<br> +of low commitment, but what do I know?<br> +So far, he’s disappointing, but on track.<br> +What’s he up to Isla: seven minutes? neat!<br> +Despite that, he is never holding back<br> +your blossoming romance with Dr Peat.<br> +Don’t deny it, that launch was pretty hard!<br> +It’s fifth base next: that’s photos of his feet.<br> +It’s fair to say her reputation’s marred.<br> +We all regret that we did once anoint<br> +her President: her premiership’s ill-starred.<br> +Hey - you’re meant to be in charge of this joint!<br> +You’re seldom seen cos of the mountaineering<br> +meets that you’re always on. You’d made your point<br> +before you chose to go off disappearing<br> +to <em>New Zealand</em>… we get the message! Plus<br> +we’ve had enough of all your domineering:<br> +maybe it’s time we put you on a bus!<br> +Nah, I’m just joking. All I’ve said’s refutable.<br> +But the boys, we mean this next bit, all of us,<br> +so stop me Isla if this isn’t suitable<br> +but honestly we think your mum is beautiful.<br> + +Alas, I have to bring an end to this rhyme.<br> +I know it wasn’t much, in our defence,<br> +the fact you used ChatGPT’s a crime.<br> +I hope I’ve not caused over much offence<br> +don’t worry, that is it, I’ve said my bit,<br> +so I’ll turn from the ladies to the gents.<br> +Yeah, don’t look away now, we wrote this shit!<br> +I see you looking at your laces, Chris!<br> +Wit without real goodwill is not legit,<br> +so boys, don’t send sincerity to piss!<br> +Why did God say he’d take our hearts of stone<br> +and give us hearts of flesh? For this, for this!<br> +Here is flesh of our flesh, bone of our bone;<br> +love, and love nothing more but God alone.<br> diff --git a/src/content/blog/2024/03/30/easter.md b/src/content/blog/2024/03/30/easter.md new file mode 100644 index 0000000..01d4c5b --- /dev/null +++ b/src/content/blog/2024/03/30/easter.md @@ -0,0 +1,108 @@ +--- +title: Why Easter is the best week of the year +description: >- + Based on a talk given to my colleagues at + <a href="https://www.scottlogic.co.uk">Scott Logic</a> for Maundy + Thursday, 2024. +pubDate: + year: 2024 + month: 03 + day: 30 +--- + +As you might have noticed, it is Easter this week! So I'd like to take five or +five minutes of your time to share why I – and about two billion other humans +going about the place just now – think Easter is the best week of the year. And +it's got something to do with a special Christian ritual called Communion. + +Communion, at its heart, is about as simple a ritual as you can get. You get +together with a bunch of other people. You share some bread, and you share some +wine. + +And it’s because of this ritual that so many people regard Easter as the best +week of the year. I want to explain to you why that is, and more than that, I +want to convince you that Easter is the best week of the year for you, too! + +If you’ve passed by _The Hub_ at the top of Johnstone Terrace here in +Edinburgh recently, you might have notice the banner which is draped over +the railings just now – reading, ‘RITUALS THAT UNITE US.’ + +Now, that might seem like an odd idea. But wouldn’t that be great, if we +actually had a ritual which could unite us? Because the world could surely do +with a bit more unity right now. The world seems so divided, and sometimes it +seems like there’s no hope for real unity. + +We can see that in our politics. We’re divided about foreign policy, about +taxation policy, about trade policy, about environmental policy. + +And the conflicts that we have in this country seem pretty trivial when we +remember the conflicts that are playing out in other parts of the world right +now. In Israel and Gaza. In Sudan. In Russia and Ukraine. + +And there’s plenty of conflict happening on the small scale, too. Often it’s the +smallest-scale conflicts which hurt us the most deeply. Your landlord pushes you +around. That friend you trusted like no-one else in the world lets you down. The +partner or spouse you loved like no-one else in the world – you end up fighting. + +It’s possible that you’re going to be reading this right now with a heavy heart +because of a broken relationship in their life. And doesn’t that hurt more than +anything else we know? + +When the world is groaning so heavily under the weight of conflict, and some +banner on _The Hub_ tells us a ritual can unite us, that seems so out of +proportion to the scale of the problem, doesn’t it? What can a ritual do? A bit +of old superstition? An excuse to divide people, maybe – what can a ritual do +to unite us? + +Well, two thousand years ago, a man had a meal with his friends. Together, they +shared a meal of bread and wine – which, in that time and place, was the most +ordinary meal imaginable. + +And yet, in that most ordinary event imaginable, something was happening which +was totally unimaginable. As this man, Jesus, shared the elements of this meal, +he made some extraordinary statements about what he was doing: ‘take, eat, this +is my body’ – ‘drink this, all of you; this is my blood of the new covenant.’ +(The word ‘covenant’ means a promise.) + +He told them he wasn’t just giving them bread and wine, he was giving his body +and his blood, and a promise. + +Before Jesus ate another meal, he was flogged and nailed to a cross. His blood +was spilt and his body broken, even to death. + +And yet, that wasn’t the end of the Easter story. Because three days later, +mourners turned up at Jesus’ tomb to pay their respects, and found the tomb +empty, the stone rolled away. Then they became the first of crowds of +incredulous eyewitnesses to see Jesus, the same Jesus who was killed on a cross, +alive. + +Some magic trick, right? But this matters a hell of a lot more than just some +magic trick. Because Jesus became the first person in history to prove that you +really can both have your cake and eat it. He gave his life, and lived! As a +result, we can have his life and our own. We can join with Jesus through the +ritual of Communion which he established, and thereby, through Jesus’ body, join +together with everyone else who takes part in that ritual, as one body. Then we +can start living our brand-new, full-fat, original-recipe life overflowing with +generosity where we too can both give our life to others and enjoy it ourselves. +Indeed, Jesus taught us and showed us that it’s precisely by giving our lives to +others that we get to truly live ourselves. + +This is why, in spite of all the division which persists in the world today, two +billion people regard this week as the best week of the year. Two billion +people, from every nation on Earth, speaking thousands of languages, of every +age and culture and gender and race, who defy the divisions of this world to +insist on joining together as one body in Jesus. + +That includes Edinburgh’s thriving and diverse Christian community, many of whom +will be taking part in the ritual of Communion at some point this week. And if +you want to hear more about how Jesus gave his life for us and why that matters +for all of us, I’m sure every church in Edinburgh will have their doors open at +some point this week and would be delighted to have you. My own church, +Bruntsfield Evangelical, will be having a service tomorrow, Good Friday at +twelve noon, and also at eleven o’ clock on Sunday – I’d especially recommend this +one if you’re new to church or haven’t been in a while. You’d be very welcome to +join me there! + +Because Jesus’ new covenant, his promise to all of us, is that in an apparently +hopelessly divided world, there exists real hope for unity. And that’s why +Easter is the best week of the year. diff --git a/src/content/config.ts b/src/content/config.ts new file mode 100644 index 0000000..58dbc00 --- /dev/null +++ b/src/content/config.ts @@ -0,0 +1,19 @@ +import { defineCollection, z } from 'astro:content'; + +const dateSchema = z.object({ + year: z.number(), + month: z.number(), + day: z.number(), +}); + +const blog = defineCollection({ + type: 'content', + schema: z.object({ + title: z.string(), + description: z.string(), + pubDate: dateSchema, + updatedDate: z.optional(dateSchema), + }), +}); + +export const collections = { blog }; diff --git a/src/env.d.ts b/src/env.d.ts new file mode 100644 index 0000000..acef35f --- /dev/null +++ b/src/env.d.ts @@ -0,0 +1,2 @@ +/// <reference path="../.astro/types.d.ts" /> +/// <reference types="astro/client" /> diff --git a/src/layouts/BlogPost.astro b/src/layouts/BlogPost.astro new file mode 100644 index 0000000..25f76e6 --- /dev/null +++ b/src/layouts/BlogPost.astro @@ -0,0 +1,47 @@ +--- +import type { CollectionEntry } from 'astro:content'; +import BaseHead from '../components/BaseHead.astro'; +import FormattedDate from '../components/FormattedDate.astro'; + +type Props = CollectionEntry<'blog'>['data']; + +const { title, description, pubDate, updatedDate } = Astro.props; + +const canonicalUrl = new URL(Astro.url.pathname, Astro.site); + +const pubDateStr = `${pubDate.year}-${pubDate.month}-${pubDate.day}`; +const updatedDateStr = updatedDate ? + `${updatedDate.year}-${updatedDate.month}-${updatedDate.day}` + : pubDateStr; +--- + +<html lang="en"> + <head> + <BaseHead title={`${title} | joeac’s blog`} description={description} /> + <link rel="stylesheet" href="/css/blog.css"> + </head> + + <body> + <article class="h-entry"> + <aside> + <p>This is a blog post by <a class="p-author h-card" href="/">Joe Carstairs</a></p> + { updatedDate + ? ( + <p>Updated: <FormattedDate date={updatedDateStr} className="dt-updated"/></p> + <p>Originally published: <FormattedDate date={pubDateStr} className="dt-published"/></p> + ) : <p>Published: <FormattedDate date={pubDateStr} className="dt-published"/></p> + } + <p>Go back to his <a href="/blog">blog</a> if you like.</p> + <p><a class="u-url uid" href={canonicalUrl}>Permalink</a></p> + </aside> + + <h1 class="h-name">{title}</h1> + + <p class="p-summary" set:html={description} /> + + <div class="e-content"> + <slot /> + </div> + </article> + </body> +</html> diff --git a/src/layouts/Page.astro b/src/layouts/Page.astro new file mode 100644 index 0000000..4e5f3bb --- /dev/null +++ b/src/layouts/Page.astro @@ -0,0 +1,17 @@ +--- +import BaseHead from "../components/BaseHead.astro"; + +const { title, description } = Astro.props; +--- + +<!DOCTYPE html> +<html lang="en-GB"> + <head> + <BaseHead title={title} description={description} /> + </head> + + <body> + <slot /> + </body> +</html> + diff --git a/src/pages/blog/[...slug].astro b/src/pages/blog/[...slug].astro new file mode 100644 index 0000000..800c534 --- /dev/null +++ b/src/pages/blog/[...slug].astro @@ -0,0 +1,20 @@ +--- +import { type CollectionEntry, getCollection } from 'astro:content'; +import BlogPost from '../../layouts/BlogPost.astro'; + +export async function getStaticPaths() { + const posts = await getCollection('blog'); + return posts.map((post) => ({ + params: { slug: post.slug }, + props: post, + })); +} +type Props = CollectionEntry<'blog'>; + +const post = Astro.props; +const { Content } = await post.render(); +--- + +<BlogPost {...post.data}> + <Content /> +</BlogPost>
\ No newline at end of file diff --git a/src/pages/blog/index.astro b/src/pages/blog/index.astro new file mode 100644 index 0000000..0c617bd --- /dev/null +++ b/src/pages/blog/index.astro @@ -0,0 +1,17 @@ +--- +import BaseHead from '../../components/BaseHead.astro'; +import { SITE_TITLE, SITE_DESCRIPTION } from '../../consts'; +import BlogFeed from '../../components/BlogFeed.astro'; +--- + +<!doctype html> +<html lang="en"> + <head> + <BaseHead title={SITE_TITLE} description={SITE_DESCRIPTION} /> + </head> + <body> + <main> + <BlogFeed headingLevel={1} /> + </main> + </body> +</html> diff --git a/src/pages/error.astro b/src/pages/error.astro new file mode 100644 index 0000000..fc84122 --- /dev/null +++ b/src/pages/error.astro @@ -0,0 +1,14 @@ +--- +import Page from '../layouts/Page.astro'; +--- + +<Page title="Got lost?" description="Error page for Joe's personal website"> + <main> + <h1>Got lost?</h1> + + <p> + If you’re on this page, something’s probably gone wrong. Try going to + my <a href="/">homepage</a> instead. + </p> + </main> +</Page> diff --git a/src/pages/index.astro b/src/pages/index.astro new file mode 100644 index 0000000..74cf934 --- /dev/null +++ b/src/pages/index.astro @@ -0,0 +1,11 @@ +--- +import BlogFeed from '../components/BlogFeed.astro'; +import Me from '../components/Me.astro'; +import { SITE_TITLE, SITE_DESCRIPTION } from '../consts'; +import Page from '../layouts/Page.astro'; +--- + +<Page title={SITE_TITLE} description={SITE_DESCRIPTION}> + <Me /> + <BlogFeed hideAuthor /> +</Page>
\ No newline at end of file diff --git a/src/pages/rss.xml.js b/src/pages/rss.xml.js new file mode 100644 index 0000000..79da596 --- /dev/null +++ b/src/pages/rss.xml.js @@ -0,0 +1,17 @@ +import rss from '@astrojs/rss'; +import { getCollection } from 'astro:content'; +import { SITE_TITLE, SITE_DESCRIPTION } from '../consts'; + +export async function GET(context) { + const posts = await getCollection('blog'); + return rss({ + title: SITE_TITLE, + description: SITE_DESCRIPTION, + site: context.site, + items: posts.map((post) => ({ + ...post.data, + link: `/blog/${post.slug}/`, + pubDate: new Date(post.data.pubDate.year, post.data.pubDate.month - 1, post.data.pubDate.day), + })), + }); +} |
