apply formatting rules
This commit is contained in:
+14
-15
@@ -1,31 +1,30 @@
|
||||
---
|
||||
|
||||
import { Image } from "astro:assets";
|
||||
|
||||
import portrait from "../assets/portrait.jpg";
|
||||
|
||||
interface Props {
|
||||
size: number;
|
||||
size: number;
|
||||
}
|
||||
|
||||
const { size } = Astro.props;
|
||||
---
|
||||
|
||||
<Image
|
||||
src={portrait}
|
||||
alt="Illustrated portrait of a person with glasses and a beard, wearing a red and black plaid shirt. The background is a solid green color."
|
||||
class="portrait"
|
||||
width={`${size}`}
|
||||
height={`${size}`}
|
||||
src={portrait}
|
||||
alt="Illustrated portrait of a person with glasses and a beard, wearing a red and black plaid shirt. The background is a solid green color."
|
||||
class="portrait"
|
||||
width={`${size}`}
|
||||
height={`${size}`}
|
||||
/>
|
||||
|
||||
<style>
|
||||
.portrait {
|
||||
display: block;
|
||||
margin: auto;
|
||||
border-radius: 200px;
|
||||
}
|
||||
a > svg {
|
||||
transform: translateY(0.18rem);
|
||||
}
|
||||
.portrait {
|
||||
display: block;
|
||||
margin: auto;
|
||||
border-radius: 200px;
|
||||
}
|
||||
a > svg {
|
||||
transform: translateY(0.18rem);
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,44 +1,43 @@
|
||||
---
|
||||
|
||||
import { add, format } from "date-fns";
|
||||
|
||||
import CalendarIcon from "../assets/svg/calendar.svg";
|
||||
|
||||
interface Props {
|
||||
title: string;
|
||||
date: Date;
|
||||
slug?: string;
|
||||
title: string;
|
||||
date: Date;
|
||||
slug?: string;
|
||||
}
|
||||
|
||||
const { title, date, slug } = Astro.props;
|
||||
---
|
||||
|
||||
<div class="blog-header">
|
||||
{slug ? <a href={`/blog/${slug}`}>{title}</a> : <h1>{title}</h1>}
|
||||
<div>
|
||||
<CalendarIcon class="calendar-icon" width={24} height={24} />
|
||||
<strong>{format(add(new Date(date), { hours: 6 }), 'MMM do, y')}</strong>
|
||||
</div>
|
||||
{slug ? <a href={`/blog/${slug}`}>{title}</a> : <h1>{title}</h1>}
|
||||
<div>
|
||||
<CalendarIcon class="calendar-icon" width={24} height={24} />
|
||||
<strong>{format(add(new Date(date), { hours: 6 }), "MMM do, y")}</strong>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
a {
|
||||
font-size: 1.3rem;
|
||||
}
|
||||
a {
|
||||
font-size: 1.3rem;
|
||||
}
|
||||
|
||||
strong {
|
||||
font-weight: bolder;
|
||||
}
|
||||
strong {
|
||||
font-weight: bolder;
|
||||
}
|
||||
|
||||
.blog-header {
|
||||
margin: 1rem 0;
|
||||
}
|
||||
.blog-header {
|
||||
margin: 1rem 0;
|
||||
}
|
||||
|
||||
.blog-header > h2 {
|
||||
margin: 0;
|
||||
}
|
||||
.blog-header > h2 {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.calendar-icon {
|
||||
transform: translateY(0.3rem);
|
||||
}
|
||||
.calendar-icon {
|
||||
transform: translateY(0.3rem);
|
||||
}
|
||||
</style>
|
||||
|
||||
+39
-40
@@ -1,55 +1,54 @@
|
||||
---
|
||||
|
||||
interface Props {
|
||||
title?: string;
|
||||
image?: ImageMetadata;
|
||||
links?: {
|
||||
label: string;
|
||||
href: string;
|
||||
newWindow?: boolean;
|
||||
}[];
|
||||
title?: string;
|
||||
image?: ImageMetadata;
|
||||
links?: {
|
||||
label: string;
|
||||
href: string;
|
||||
newWindow?: boolean;
|
||||
}[];
|
||||
}
|
||||
|
||||
const { title, image, links } = Astro.props;
|
||||
---
|
||||
|
||||
<div class="card">
|
||||
{image && <img src={image.src} alt="" />}
|
||||
<div>
|
||||
{title && <h2>{title}</h2>}
|
||||
<slot />
|
||||
{
|
||||
links &&
|
||||
links.map((link) => (
|
||||
<a
|
||||
href={link.href}
|
||||
target={link.newWindow ? '_blank' : '_self'}
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
{link.label}
|
||||
</a>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
{image && <img src={image.src} alt="" />}
|
||||
<div>
|
||||
{title && <h2>{title}</h2>}
|
||||
<slot />
|
||||
{
|
||||
links &&
|
||||
links.map((link) => (
|
||||
<a
|
||||
href={link.href}
|
||||
target={link.newWindow ? "_blank" : "_self"}
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
{link.label}
|
||||
</a>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.card {
|
||||
border: var(--border);
|
||||
border-radius: var(--radius);
|
||||
overflow: hidden;
|
||||
}
|
||||
.card {
|
||||
border: var(--border);
|
||||
border-radius: var(--radius);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.card > div {
|
||||
padding: 1rem;
|
||||
}
|
||||
.card > div {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
h2 {
|
||||
margin: 0;
|
||||
}
|
||||
h2 {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
img {
|
||||
object-fit: cover;
|
||||
max-width: 100%;
|
||||
}
|
||||
img {
|
||||
object-fit: cover;
|
||||
max-width: 100%;
|
||||
}
|
||||
</style>
|
||||
|
||||
+46
-47
@@ -1,5 +1,4 @@
|
||||
---
|
||||
|
||||
import MastodonIcon from "../assets/svg/mastodon.svg";
|
||||
import RssIcon from "../assets/svg/rss.svg";
|
||||
|
||||
@@ -7,61 +6,61 @@ const year = new Date().getFullYear();
|
||||
---
|
||||
|
||||
<footer>
|
||||
<div class="socials">
|
||||
<a
|
||||
href="https://mastodon.social/@ghalldev"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
><MastodonIcon class="mastodon-icon" width={20} height={20} /> Follow me on
|
||||
Mastodon</a
|
||||
>
|
||||
<div class="socials">
|
||||
<a
|
||||
href="https://mastodon.social/@ghalldev"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
><MastodonIcon class="mastodon-icon" width={20} height={20} /> Follow me on
|
||||
Mastodon</a
|
||||
>
|
||||
|
||||
<a href="/rss.xml"
|
||||
><RssIcon class="rss-icon" width={20} height={20} /> Subscribe with RSS</a
|
||||
>
|
||||
</div>
|
||||
<p>
|
||||
Copyright 2022 - {year}, Graham Hall
|
||||
<a href="/rss.xml"
|
||||
><RssIcon class="rss-icon" width={20} height={20} /> Subscribe with RSS</a
|
||||
>
|
||||
</div>
|
||||
<p>
|
||||
Copyright 2022 - {year}, Graham Hall
|
||||
|
||||
<br />
|
||||
Built with <a href="https://astro.build">Astro</a>
|
||||
</p>
|
||||
<br />
|
||||
Built with <a href="https://astro.build">Astro</a>
|
||||
</p>
|
||||
</footer>
|
||||
|
||||
<style>
|
||||
footer {
|
||||
margin: 2rem 0;
|
||||
text-align: center;
|
||||
border-top: var(--border);
|
||||
padding-top: 24px;
|
||||
}
|
||||
footer {
|
||||
margin: 2rem 0;
|
||||
text-align: center;
|
||||
border-top: var(--border);
|
||||
padding-top: 24px;
|
||||
}
|
||||
|
||||
.rss-icon {
|
||||
transform: translateY(0.18rem);
|
||||
}
|
||||
.rss-icon {
|
||||
transform: translateY(0.18rem);
|
||||
}
|
||||
|
||||
.socials {
|
||||
display: flex;
|
||||
gap: 24px;
|
||||
justify-content: center;
|
||||
}
|
||||
.socials {
|
||||
display: flex;
|
||||
gap: 24px;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.socials a {
|
||||
color: var(--text);
|
||||
}
|
||||
.socials a {
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
.rss-icon {
|
||||
color: var(--orange);
|
||||
}
|
||||
.rss-icon {
|
||||
color: var(--orange);
|
||||
}
|
||||
|
||||
.mastodon-icon {
|
||||
color: var(--blue);
|
||||
transform: translateY(4px);
|
||||
}
|
||||
.mastodon-icon {
|
||||
color: var(--blue);
|
||||
transform: translateY(4px);
|
||||
}
|
||||
|
||||
@media (max-width: 516px) {
|
||||
.socials {
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
@media (max-width: 516px) {
|
||||
.socials {
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
---
|
||||
|
||||
import BarsIcon from "../../assets/svg/bars.svg";
|
||||
import CloseIcon from "../../assets/svg/xmark.svg";
|
||||
|
||||
@@ -8,94 +7,94 @@ import { navLinks } from "../../data/nav-links";
|
||||
|
||||
<!-- drawerOpened is defined in /src/layouts/Layout.astro -->
|
||||
<div class="drawer-container" @keydown.escape="drawerOpen = false">
|
||||
<button class="icon-button" @click="drawerOpen = !drawerOpen">
|
||||
<BarsIcon width={24} height={24} />
|
||||
</button>
|
||||
<div
|
||||
class="overlay"
|
||||
@click="drawerOpen = !drawerOpen"
|
||||
x-show="drawerOpen"
|
||||
x-transition:enter-start="hidden-overlay"
|
||||
x-transition:enter-end="visible-overlay"
|
||||
x-transition:leave-start="visible-overlay"
|
||||
x-transition:leave-end="hidden-overlay"
|
||||
>
|
||||
</div>
|
||||
<div
|
||||
class="drawer"
|
||||
x-show="drawerOpen"
|
||||
x-transition:enter-start="hidden-drawer"
|
||||
x-transition:enter-end="visible-drawer"
|
||||
x-transition:leave-start="visible-drawer"
|
||||
x-transition:leave-end="hidden-drawer"
|
||||
>
|
||||
<div>
|
||||
<button class="icon-button" @click="drawerOpen = !drawerOpen">
|
||||
<CloseIcon width={24} height={24} /></button
|
||||
>
|
||||
</div>
|
||||
<nav>
|
||||
{navLinks.map((link) => <a href={`/${link.path}`}>{link.label}</a>)}
|
||||
</nav>
|
||||
</div>
|
||||
<button class="icon-button" @click="drawerOpen = !drawerOpen">
|
||||
<BarsIcon width={24} height={24} />
|
||||
</button>
|
||||
<div
|
||||
class="overlay"
|
||||
@click="drawerOpen = !drawerOpen"
|
||||
x-show="drawerOpen"
|
||||
x-transition:enter-start="hidden-overlay"
|
||||
x-transition:enter-end="visible-overlay"
|
||||
x-transition:leave-start="visible-overlay"
|
||||
x-transition:leave-end="hidden-overlay"
|
||||
>
|
||||
</div>
|
||||
<div
|
||||
class="drawer"
|
||||
x-show="drawerOpen"
|
||||
x-transition:enter-start="hidden-drawer"
|
||||
x-transition:enter-end="visible-drawer"
|
||||
x-transition:leave-start="visible-drawer"
|
||||
x-transition:leave-end="hidden-drawer"
|
||||
>
|
||||
<div>
|
||||
<button class="icon-button" @click="drawerOpen = !drawerOpen">
|
||||
<CloseIcon width={24} height={24} /></button
|
||||
>
|
||||
</div>
|
||||
<nav>
|
||||
{navLinks.map((link) => <a href={`/${link.path}`}>{link.label}</a>)}
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
:root {
|
||||
--transition: all 0.3s ease-in-out;
|
||||
}
|
||||
:root {
|
||||
--transition: all 0.3s ease-in-out;
|
||||
}
|
||||
|
||||
.drawer-container {
|
||||
display: none;
|
||||
}
|
||||
.drawer-container {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.overlay {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
background-color: rgba(0, 0, 0, 0.6);
|
||||
z-index: 2;
|
||||
transition: var(--transition);
|
||||
}
|
||||
.overlay {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
background-color: rgba(0, 0, 0, 0.6);
|
||||
z-index: 2;
|
||||
transition: var(--transition);
|
||||
}
|
||||
|
||||
.hidden-overlay {
|
||||
opacity: 0;
|
||||
}
|
||||
.hidden-overlay {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.drawer {
|
||||
right: 0;
|
||||
top: 0;
|
||||
background-color: var(--background);
|
||||
position: absolute;
|
||||
width: 75vw;
|
||||
height: 100vh;
|
||||
z-index: 3;
|
||||
transition: var(--transition);
|
||||
}
|
||||
.drawer {
|
||||
right: 0;
|
||||
top: 0;
|
||||
background-color: var(--background);
|
||||
position: absolute;
|
||||
width: 75vw;
|
||||
height: 100vh;
|
||||
z-index: 3;
|
||||
transition: var(--transition);
|
||||
}
|
||||
|
||||
.drawer > div {
|
||||
padding: 8px 6px 4px;
|
||||
border-bottom: var(--border);
|
||||
}
|
||||
.drawer > div {
|
||||
padding: 8px 6px 4px;
|
||||
border-bottom: var(--border);
|
||||
}
|
||||
|
||||
nav {
|
||||
text-align: center;
|
||||
padding: 12px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
nav {
|
||||
text-align: center;
|
||||
padding: 12px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
.hidden-drawer {
|
||||
transform: translateX(75vw);
|
||||
}
|
||||
.hidden-drawer {
|
||||
transform: translateX(75vw);
|
||||
}
|
||||
|
||||
@media screen and (max-width: 768px) {
|
||||
.drawer-container {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
@media screen and (max-width: 768px) {
|
||||
.drawer-container {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,50 +1,49 @@
|
||||
---
|
||||
|
||||
import Drawer from "./Drawer.astro";
|
||||
import Nav from "./Nav.astro";
|
||||
---
|
||||
|
||||
<header>
|
||||
<div class="container">
|
||||
<a href="/">ghall.space</a>
|
||||
<Nav />
|
||||
<Drawer />
|
||||
</div>
|
||||
<div class="container">
|
||||
<a href="/">ghall.space</a>
|
||||
<Nav />
|
||||
<Drawer />
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<style>
|
||||
header {
|
||||
margin-bottom: 20px;
|
||||
height: 70px;
|
||||
background-color: rgba(252, 252, 252, 90%);
|
||||
backdrop-filter: blur(10px);
|
||||
width: 100%;
|
||||
top: 0;
|
||||
position: fixed;
|
||||
z-index: 1;
|
||||
border-bottom: var(--border);
|
||||
}
|
||||
header {
|
||||
margin-bottom: 20px;
|
||||
height: 70px;
|
||||
background-color: rgba(252, 252, 252, 90%);
|
||||
backdrop-filter: blur(10px);
|
||||
width: 100%;
|
||||
top: 0;
|
||||
position: fixed;
|
||||
z-index: 1;
|
||||
border-bottom: var(--border);
|
||||
}
|
||||
|
||||
.container {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
max-width: var(--max-page-width);
|
||||
margin: auto;
|
||||
padding: 0 16px;
|
||||
height: 100%;
|
||||
}
|
||||
.container {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
max-width: var(--max-page-width);
|
||||
margin: auto;
|
||||
padding: 0 16px;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
a {
|
||||
font-size: 1.6rem;
|
||||
color: var(--text);
|
||||
font-weight: bold;
|
||||
}
|
||||
a {
|
||||
font-size: 1.6rem;
|
||||
color: var(--text);
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
header {
|
||||
background-color: rgba(30, 30, 46, 90%);
|
||||
}
|
||||
}
|
||||
@media (prefers-color-scheme: dark) {
|
||||
header {
|
||||
background-color: rgba(30, 30, 46, 90%);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,65 +1,64 @@
|
||||
---
|
||||
|
||||
import { navLinks } from '../../data/nav-links';
|
||||
import { navLinks } from "../../data/nav-links";
|
||||
|
||||
const { pathname } = Astro.url;
|
||||
|
||||
const pathComponents = pathname.split('/').slice(1);
|
||||
const pathComponents = pathname.split("/").slice(1);
|
||||
---
|
||||
|
||||
<nav>
|
||||
{
|
||||
navLinks.map((link) => (
|
||||
<li>
|
||||
<a
|
||||
href={`/${link.path}`}
|
||||
class={pathComponents[0] === link.path ? 'selected' : null}
|
||||
>
|
||||
{link.label}
|
||||
</a>
|
||||
</li>
|
||||
))
|
||||
}
|
||||
{
|
||||
navLinks.map((link) => (
|
||||
<li>
|
||||
<a
|
||||
href={`/${link.path}`}
|
||||
class={pathComponents[0] === link.path ? "selected" : null}
|
||||
>
|
||||
{link.label}
|
||||
</a>
|
||||
</li>
|
||||
))
|
||||
}
|
||||
</nav>
|
||||
|
||||
<style>
|
||||
nav {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
list-style: none;
|
||||
gap: 10px;
|
||||
font-size: 1.15rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
nav {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
list-style: none;
|
||||
gap: 10px;
|
||||
font-size: 1.15rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--text);
|
||||
padding: 5px 10px;
|
||||
border-radius: var(--radius);
|
||||
transition: background-color 0.2s ease-in-out;
|
||||
}
|
||||
a {
|
||||
color: var(--text);
|
||||
padding: 5px 10px;
|
||||
border-radius: var(--radius);
|
||||
transition: background-color 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
background-color: rgba(0, 0, 0, 0.04);
|
||||
opacity: 1;
|
||||
}
|
||||
a:hover {
|
||||
background-color: rgba(0, 0, 0, 0.04);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.underline {
|
||||
height: 2px;
|
||||
width: 100%;
|
||||
background-color: var(--orange);
|
||||
}
|
||||
.underline {
|
||||
height: 2px;
|
||||
width: 100%;
|
||||
background-color: var(--orange);
|
||||
}
|
||||
|
||||
@media screen and (max-width: 768px) {
|
||||
nav {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
@media screen and (max-width: 768px) {
|
||||
nav {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
a:hover {
|
||||
background-color: rgba(231, 237, 255, 0.1);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
@media (prefers-color-scheme: dark) {
|
||||
a:hover {
|
||||
background-color: rgba(231, 237, 255, 0.1);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<div
|
||||
x-data="{
|
||||
x-data="{
|
||||
latestPost: null,
|
||||
fetchPost() {
|
||||
fetch('https://mastodon.social/@ghalldev.rss')
|
||||
@@ -21,32 +21,32 @@
|
||||
});
|
||||
},
|
||||
}"
|
||||
x-init="fetchPost()"
|
||||
x-init="fetchPost()"
|
||||
>
|
||||
<template x-if="latestPost">
|
||||
<div class="mastodon-post">
|
||||
<h2>Latest Mastodon Post</h2>
|
||||
<div x-html="latestPost.body"></div>
|
||||
<a x-bind:href="latestPost.link" target="_blank" rel="noopener noreferrer"
|
||||
>View Post</a
|
||||
>
|
||||
</div>
|
||||
</template>
|
||||
<template x-if="!latestPost">
|
||||
<p>Loading...</p>
|
||||
</template>
|
||||
<template x-if="latestPost">
|
||||
<div class="mastodon-post">
|
||||
<h2>Latest Mastodon Post</h2>
|
||||
<div x-html="latestPost.body"></div>
|
||||
<a x-bind:href="latestPost.link" target="_blank" rel="noopener noreferrer"
|
||||
>View Post</a
|
||||
>
|
||||
</div>
|
||||
</template>
|
||||
<template x-if="!latestPost">
|
||||
<p>Loading...</p>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.mastodon-post {
|
||||
margin: 2rem auto;
|
||||
padding: 1rem;
|
||||
max-width: 600px;
|
||||
border-radius: var(--radius);
|
||||
border: var(--border);
|
||||
}
|
||||
.mastodon-post {
|
||||
margin: 2rem auto;
|
||||
padding: 1rem;
|
||||
max-width: 600px;
|
||||
border-radius: var(--radius);
|
||||
border: var(--border);
|
||||
}
|
||||
|
||||
.mastodon-post > h2 {
|
||||
margin-top: 0;
|
||||
}
|
||||
.mastodon-post > h2 {
|
||||
margin-top: 0;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,27 +1,26 @@
|
||||
---
|
||||
|
||||
import type { CollectionEntry } from "astro:content";
|
||||
import BlogHeader from "@components/BlogHeader.astro";
|
||||
import Tags from "@components/Tags.astro";
|
||||
|
||||
interface Props {
|
||||
post: CollectionEntry<"blog">;
|
||||
post: CollectionEntry<"blog">;
|
||||
}
|
||||
|
||||
const { post } = Astro.props;
|
||||
---
|
||||
|
||||
<article>
|
||||
<BlogHeader title={post.data.title} date={post.data.pubDate} slug={post.id} />
|
||||
<Tags tags={post.data.tags} />
|
||||
<BlogHeader title={post.data.title} date={post.data.pubDate} slug={post.id} />
|
||||
<Tags tags={post.data.tags} />
|
||||
</article>
|
||||
|
||||
<style>
|
||||
article {
|
||||
padding-bottom: 20px;
|
||||
}
|
||||
article {
|
||||
padding-bottom: 20px;
|
||||
}
|
||||
|
||||
article:not(:first-child) {
|
||||
border-top: var(--border);
|
||||
}
|
||||
article:not(:first-child) {
|
||||
border-top: var(--border);
|
||||
}
|
||||
</style>
|
||||
|
||||
+11
-12
@@ -1,21 +1,20 @@
|
||||
---
|
||||
|
||||
interface Props {
|
||||
tags: string[];
|
||||
tags: string[];
|
||||
}
|
||||
|
||||
const { tags } = Astro.props;
|
||||
---
|
||||
|
||||
<span>
|
||||
{
|
||||
tags.sort().map((tag: string, index: number) => (
|
||||
<>
|
||||
<a class="tag" href={`/blog/tag/${tag}`}>
|
||||
{tag}
|
||||
</a>
|
||||
{index < tags.length - 1 ? ' | ' : ''}
|
||||
</>
|
||||
))
|
||||
}
|
||||
{
|
||||
tags.sort().map((tag: string, index: number) => (
|
||||
<>
|
||||
<a class="tag" href={`/blog/tag/${tag}`}>
|
||||
{tag}
|
||||
</a>
|
||||
{index < tags.length - 1 ? " | " : ""}
|
||||
</>
|
||||
))
|
||||
}
|
||||
</span>
|
||||
|
||||
-2241
File diff suppressed because it is too large
Load Diff
+34
-35
@@ -1,5 +1,4 @@
|
||||
---
|
||||
|
||||
import { ClientRouter } from "astro:transitions";
|
||||
|
||||
import "../global.css";
|
||||
@@ -8,10 +7,10 @@ import Footer from "@components/Footer.astro";
|
||||
import Header from "@components/Header/Header.astro";
|
||||
|
||||
export interface Props {
|
||||
title: string;
|
||||
frontmatter?: {
|
||||
title: string;
|
||||
};
|
||||
title: string;
|
||||
frontmatter?: {
|
||||
title: string;
|
||||
};
|
||||
}
|
||||
|
||||
const title = Astro.props.title || Astro.props.frontmatter?.title || "Unknown";
|
||||
@@ -19,37 +18,37 @@ const title = Astro.props.title || Astro.props.frontmatter?.title || "Unknown";
|
||||
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width" />
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||
<meta name="robots" content="noindex" /><meta
|
||||
name="description"
|
||||
content="My little space on the World Wide Web."
|
||||
/><link
|
||||
rel="alternate"
|
||||
type="application/rss+xml"
|
||||
title="ghall.space - RSS"
|
||||
href={`${Astro.site}rss.xml`}
|
||||
/><title>{`ghall.space - ${title}`}</title>
|
||||
<ClientRouter />
|
||||
</head>
|
||||
<body
|
||||
class="layout-simple"
|
||||
x-data="{ drawerOpen: false }"
|
||||
:class="drawerOpen ? 'lock-scroll' : ''"
|
||||
>
|
||||
<Header />
|
||||
<main transition:animate="initial">
|
||||
<slot />
|
||||
</main>
|
||||
<Footer />
|
||||
</body>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width" />
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||
<meta name="robots" content="noindex" /><meta
|
||||
name="description"
|
||||
content="My little space on the World Wide Web."
|
||||
/><link
|
||||
rel="alternate"
|
||||
type="application/rss+xml"
|
||||
title="ghall.space - RSS"
|
||||
href={`${Astro.site}rss.xml`}
|
||||
/><title>{`ghall.space - ${title}`}</title>
|
||||
<ClientRouter />
|
||||
</head>
|
||||
<body
|
||||
class="layout-simple"
|
||||
x-data="{ drawerOpen: false }"
|
||||
:class="drawerOpen ? 'lock-scroll' : ''"
|
||||
>
|
||||
<Header />
|
||||
<main transition:animate="initial">
|
||||
<slot />
|
||||
</main>
|
||||
<Footer />
|
||||
</body>
|
||||
</html>
|
||||
|
||||
<style>
|
||||
/* Prevent scrolling when drawer (located in Header) is open */
|
||||
.lock-scroll {
|
||||
overflow: hidden;
|
||||
}
|
||||
/* Prevent scrolling when drawer (located in Header) is open */
|
||||
.lock-scroll {
|
||||
overflow: hidden;
|
||||
}
|
||||
</style>
|
||||
|
||||
+15
-16
@@ -1,5 +1,4 @@
|
||||
---
|
||||
|
||||
import { Image } from "astro:assets";
|
||||
import Layout from "@layouts/Layout.astro";
|
||||
|
||||
@@ -7,23 +6,23 @@ import DataGif from "../assets/it-does-not-exist.gif";
|
||||
---
|
||||
|
||||
<Layout title="Not Found">
|
||||
<h2>404 - Page Not Found</h2>
|
||||
<Image
|
||||
class="gif"
|
||||
src={DataGif}
|
||||
alt="Data from Star Trek with the caption 'It does not exist'"
|
||||
/>
|
||||
<h2>404 - Page Not Found</h2>
|
||||
<Image
|
||||
class="gif"
|
||||
src={DataGif}
|
||||
alt="Data from Star Trek with the caption 'It does not exist'"
|
||||
/>
|
||||
</Layout>
|
||||
|
||||
<style>
|
||||
h2 {
|
||||
text-align: center;
|
||||
}
|
||||
h2 {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.gif {
|
||||
display: block;
|
||||
margin: auto;
|
||||
border-radius: var(--radius);
|
||||
box-shadow: var(--shadow);
|
||||
}
|
||||
.gif {
|
||||
display: block;
|
||||
margin: auto;
|
||||
border-radius: var(--radius);
|
||||
box-shadow: var(--shadow);
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
---
|
||||
|
||||
import { getCollection, render } from "astro:content";
|
||||
import BlogHeader from "@components/BlogHeader.astro";
|
||||
import Tags from "@components/Tags.astro";
|
||||
import Layout from "@layouts/Layout.astro";
|
||||
|
||||
export async function getStaticPaths() {
|
||||
const posts = await getCollection("blog");
|
||||
return posts.map((post) => ({
|
||||
params: { slug: post.id },
|
||||
props: { post },
|
||||
}));
|
||||
const posts = await getCollection("blog");
|
||||
return posts.map((post) => ({
|
||||
params: { slug: post.id },
|
||||
props: { post },
|
||||
}));
|
||||
}
|
||||
|
||||
const { post } = Astro.props;
|
||||
@@ -21,31 +20,30 @@ const { Content } = await render(post);
|
||||
---
|
||||
|
||||
<Layout title={data.title}>
|
||||
<h1></h1>
|
||||
<article>
|
||||
<BlogHeader title={data.title} date={data.pubDate} />
|
||||
<Content />
|
||||
<a href="https://notbyai.fyi/">
|
||||
<i class="not-by-ai"></i>
|
||||
</a>
|
||||
<Tags tags={data.tags} />
|
||||
</article>
|
||||
<article>
|
||||
<BlogHeader title={data.title} date={data.pubDate} />
|
||||
<Content />
|
||||
<a href="https://notbyai.fyi/">
|
||||
<i class="not-by-ai"></i>
|
||||
</a>
|
||||
<Tags tags={data.tags} />
|
||||
</article>
|
||||
</Layout>
|
||||
|
||||
<style>
|
||||
.not-by-ai {
|
||||
display: block;
|
||||
width: 134px;
|
||||
height: 45px;
|
||||
background-image: url(../../assets/svg/Written-By-Human-Not-By-AI-Badge-white.svg);
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
margin: 1rem 0;
|
||||
}
|
||||
.not-by-ai {
|
||||
display: block;
|
||||
width: 134px;
|
||||
height: 45px;
|
||||
background-image: url(../../assets/svg/Written-By-Human-Not-By-AI-Badge-white.svg);
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
margin: 1rem 0;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.not-by-ai {
|
||||
background-image: url(../../assets/svg/Written-By-Human-Not-By-AI-Badge-black.svg);
|
||||
}
|
||||
}
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.not-by-ai {
|
||||
background-image: url(../../assets/svg/Written-By-Human-Not-By-AI-Badge-black.svg);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,65 +1,64 @@
|
||||
---
|
||||
import { type CollectionEntry, getCollection } from "astro:content";
|
||||
import PostPreview from "@components/PostPreview.astro";
|
||||
|
||||
import { type CollectionEntry, getCollection } from 'astro:content';
|
||||
import PostPreview from '@components/PostPreview.astro';
|
||||
|
||||
import Layout from '@layouts/Layout.astro';
|
||||
import type { Page } from 'astro';
|
||||
import Layout from "@layouts/Layout.astro";
|
||||
import type { Page } from "astro";
|
||||
|
||||
export async function getStaticPaths({ paginate }) {
|
||||
const allPosts = await getCollection('blog');
|
||||
const allPosts = await getCollection("blog");
|
||||
|
||||
allPosts.sort(
|
||||
(a, b) =>
|
||||
Date.parse(String(b.data.pubDate)) - Date.parse(String(a.data.pubDate))
|
||||
);
|
||||
allPosts.sort(
|
||||
(a, b) =>
|
||||
Date.parse(String(b.data.pubDate)) - Date.parse(String(a.data.pubDate)),
|
||||
);
|
||||
|
||||
return paginate(allPosts, { pageSize: 10 });
|
||||
return paginate(allPosts, { pageSize: 10 });
|
||||
}
|
||||
|
||||
interface Props {
|
||||
page: Page<CollectionEntry<'blog'>>;
|
||||
page: Page<CollectionEntry<"blog">>;
|
||||
}
|
||||
|
||||
const { page } = Astro.props;
|
||||
---
|
||||
|
||||
<Layout title="Blog Archive">
|
||||
{page.data.map((post) => <PostPreview post={post} />)}
|
||||
{page.data.map((post) => <PostPreview post={post} />)}
|
||||
|
||||
<div class="pagination">
|
||||
{
|
||||
page.currentPage !== 1 ? (
|
||||
<a href={`/blog/page/${page.currentPage - 1}`}>Previous</a>
|
||||
) : (
|
||||
<span class="disabled">Previous</span>
|
||||
)
|
||||
}
|
||||
{
|
||||
page.currentPage !== page.lastPage ? (
|
||||
<a href={`/blog/page/${page.currentPage + 1}`}>Next</a>
|
||||
) : (
|
||||
<span class="disabled">Next</span>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
<div class="pagination">
|
||||
{
|
||||
page.currentPage !== 1 ? (
|
||||
<a href={`/blog/page/${page.currentPage - 1}`}>Previous</a>
|
||||
) : (
|
||||
<span class="disabled">Previous</span>
|
||||
)
|
||||
}
|
||||
{
|
||||
page.currentPage !== page.lastPage ? (
|
||||
<a href={`/blog/page/${page.currentPage + 1}`}>Next</a>
|
||||
) : (
|
||||
<span class="disabled">Next</span>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
</Layout>
|
||||
|
||||
<style>
|
||||
ul {
|
||||
list-style: none;
|
||||
}
|
||||
ul {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.pagination {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin: auto;
|
||||
margin-top: 2rem;
|
||||
gap: 1rem;
|
||||
}
|
||||
.pagination {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin: auto;
|
||||
margin-top: 2rem;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.disabled {
|
||||
color: var(--text);
|
||||
opacity: 0.5;
|
||||
}
|
||||
.disabled {
|
||||
color: var(--text);
|
||||
opacity: 0.5;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,73 +1,72 @@
|
||||
---
|
||||
|
||||
import { getCollection } from 'astro:content';
|
||||
import Layout from '@layouts/Layout.astro';
|
||||
import { add, format } from 'date-fns';
|
||||
import { getCollection } from "astro:content";
|
||||
import Layout from "@layouts/Layout.astro";
|
||||
import { add, format } from "date-fns";
|
||||
|
||||
export async function getStaticPaths() {
|
||||
const posts = await getCollection('blog');
|
||||
const posts = await getCollection("blog");
|
||||
|
||||
const tags = ['all'];
|
||||
const tags = ["all"];
|
||||
|
||||
posts.forEach((post) => {
|
||||
post.data.tags.forEach((tag) => {
|
||||
if (!tags.includes(tag)) {
|
||||
tags.push(tag);
|
||||
}
|
||||
});
|
||||
});
|
||||
posts.forEach((post) => {
|
||||
post.data.tags.forEach((tag) => {
|
||||
if (!tags.includes(tag)) {
|
||||
tags.push(tag);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
return tags.map((tag) => ({
|
||||
params: { tag },
|
||||
props: { tag },
|
||||
}));
|
||||
return tags.map((tag) => ({
|
||||
params: { tag },
|
||||
props: { tag },
|
||||
}));
|
||||
}
|
||||
|
||||
const { tag }: { tag?: string } = Astro.params;
|
||||
|
||||
const posts = await getCollection('blog', ({ data }) => {
|
||||
if (!tag) {
|
||||
return false;
|
||||
}
|
||||
const posts = await getCollection("blog", ({ data }) => {
|
||||
if (!tag) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (tag === 'all') {
|
||||
return true;
|
||||
}
|
||||
if (tag === "all") {
|
||||
return true;
|
||||
}
|
||||
|
||||
return data.tags.includes(tag);
|
||||
return data.tags.includes(tag);
|
||||
});
|
||||
|
||||
if (posts.length === 0) {
|
||||
return Astro.redirect('/404');
|
||||
return Astro.redirect("/404");
|
||||
}
|
||||
|
||||
posts.sort(
|
||||
(a, b) =>
|
||||
Date.parse(String(b.data.pubDate)) - Date.parse(String(a.data.pubDate))
|
||||
(a, b) =>
|
||||
Date.parse(String(b.data.pubDate)) - Date.parse(String(a.data.pubDate)),
|
||||
);
|
||||
---
|
||||
|
||||
<Layout title={`Blog - ${tag}`}>
|
||||
<h1>All posts tagged: {tag}</h1>
|
||||
<ul>
|
||||
{
|
||||
posts.map((post) => (
|
||||
<li>
|
||||
<a href={`/posts/${post.id}`}>{post.data.title}</a> -
|
||||
<span>
|
||||
{format(
|
||||
add(new Date(post.data.pubDate), { hours: 6 }),
|
||||
'MMM do, y'
|
||||
)}
|
||||
</span>
|
||||
</li>
|
||||
))
|
||||
}
|
||||
</ul>
|
||||
<h1>All posts tagged: {tag}</h1>
|
||||
<ul>
|
||||
{
|
||||
posts.map((post) => (
|
||||
<li>
|
||||
<a href={`/posts/${post.id}`}>{post.data.title}</a> -
|
||||
<span>
|
||||
{format(
|
||||
add(new Date(post.data.pubDate), { hours: 6 }),
|
||||
"MMM do, y",
|
||||
)}
|
||||
</span>
|
||||
</li>
|
||||
))
|
||||
}
|
||||
</ul>
|
||||
</Layout>
|
||||
|
||||
<style>
|
||||
ul {
|
||||
list-style: none;
|
||||
}
|
||||
ul {
|
||||
list-style: none;
|
||||
}
|
||||
</style>
|
||||
|
||||
+53
-54
@@ -1,5 +1,4 @@
|
||||
---
|
||||
|
||||
export const prerender = true;
|
||||
|
||||
import { getCollection } from "astro:content";
|
||||
@@ -13,64 +12,64 @@ const iconSize = 16;
|
||||
const posts = await getCollection("blog");
|
||||
|
||||
const latestPost = posts.sort(
|
||||
(a, b) =>
|
||||
new Date(b.data.pubDate).valueOf() - new Date(a.data.pubDate).valueOf(),
|
||||
(a, b) =>
|
||||
new Date(b.data.pubDate).valueOf() - new Date(a.data.pubDate).valueOf(),
|
||||
)[0];
|
||||
---
|
||||
|
||||
<Layout title="Welcome">
|
||||
<Avatar size={200} />
|
||||
<p>
|
||||
My name is <strong>Graham</strong> (he/him), a full-stack web developer, and
|
||||
tech enthusiast.
|
||||
</p>
|
||||
<p>
|
||||
When I'm not writing code, I'm usually enjoying one of my other hobbies;
|
||||
video games, board games, music, photography, art, the list goes on...
|
||||
</p>
|
||||
<p>
|
||||
I also like writing about stuff, so I put together this site to share
|
||||
whatever's on my mind about the world of tech, gaming, life, web
|
||||
development, and whatever else strikes my fancy.
|
||||
</p>
|
||||
<p>
|
||||
Read my latest blog post, <a href={`blog/${latestPost.id}`}
|
||||
>{latestPost.data.title}</a
|
||||
>, posted on {new Date(latestPost.data.pubDate).toLocaleDateString()}.
|
||||
</p>
|
||||
<p>
|
||||
If your interested in checking out my web dev projects, check out <a
|
||||
href="https://ghall.dev/"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer">my portfolio</a
|
||||
>.
|
||||
</p>
|
||||
<p>
|
||||
If you want to get in touch, I'm on <a
|
||||
rel="me"
|
||||
href="https://mastodon.social/@ghalldev"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
><MastodonIcon width={iconSize} height={iconSize} />Mastodon</a
|
||||
> and <a
|
||||
href="https://bsky.app/profile/ghalldev.bsky.social"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
><BlueskyIcon width={iconSize} height={iconSize} />Bluesky</a
|
||||
>.
|
||||
</p>
|
||||
<p>
|
||||
My portrait was drawn by <a
|
||||
href="https://www.nataliavazquezgarcia.com"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer">Natalia Vazquez</a
|
||||
>.
|
||||
</p>
|
||||
<LatestPost />
|
||||
<Avatar size={200} />
|
||||
<p>
|
||||
My name is <strong>Graham</strong> (he/him), a full-stack web developer, and
|
||||
tech enthusiast.
|
||||
</p>
|
||||
<p>
|
||||
When I'm not writing code, I'm usually enjoying one of my other hobbies;
|
||||
video games, board games, music, photography, art, the list goes on...
|
||||
</p>
|
||||
<p>
|
||||
I also like writing about stuff, so I put together this site to share
|
||||
whatever's on my mind about the world of tech, gaming, life, web
|
||||
development, and whatever else strikes my fancy.
|
||||
</p>
|
||||
<p>
|
||||
Read my latest blog post, <a href={`blog/${latestPost.id}`}
|
||||
>{latestPost.data.title}</a
|
||||
>, posted on {new Date(latestPost.data.pubDate).toLocaleDateString()}.
|
||||
</p>
|
||||
<p>
|
||||
If your interested in checking out my web dev projects, check out <a
|
||||
href="https://ghall.dev/"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer">my portfolio</a
|
||||
>.
|
||||
</p>
|
||||
<p>
|
||||
If you want to get in touch, I'm on <a
|
||||
rel="me"
|
||||
href="https://mastodon.social/@ghalldev"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
><MastodonIcon width={iconSize} height={iconSize} />Mastodon</a
|
||||
> and <a
|
||||
href="https://bsky.app/profile/ghalldev.bsky.social"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
><BlueskyIcon width={iconSize} height={iconSize} />Bluesky</a
|
||||
>.
|
||||
</p>
|
||||
<p>
|
||||
My portrait was drawn by <a
|
||||
href="https://www.nataliavazquezgarcia.com"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer">Natalia Vazquez</a
|
||||
>.
|
||||
</p>
|
||||
<LatestPost />
|
||||
</Layout>
|
||||
|
||||
<style>
|
||||
a > svg {
|
||||
transform: translateY(0.1rem);
|
||||
}
|
||||
a > svg {
|
||||
transform: translateY(0.1rem);
|
||||
}
|
||||
</style>
|
||||
|
||||
+33
-34
@@ -1,5 +1,4 @@
|
||||
---
|
||||
|
||||
import Card from "@components/Card.astro";
|
||||
import { projects } from "@data/projects";
|
||||
import Layout from "@layouts/Layout.astro";
|
||||
@@ -8,41 +7,41 @@ const title = "Projects";
|
||||
---
|
||||
|
||||
<Layout title={title}>
|
||||
<h1>{title}</h1>
|
||||
<div class="projects-grid">
|
||||
{
|
||||
projects.map((project) => (
|
||||
<Card
|
||||
title={project.title}
|
||||
image={project.image}
|
||||
links={[
|
||||
{
|
||||
label: 'More...',
|
||||
href: project.link,
|
||||
newWindow: true,
|
||||
},
|
||||
]}
|
||||
>
|
||||
<div class="project-content">
|
||||
<p>{project.description}</p>
|
||||
</div>
|
||||
</Card>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
<h1>{title}</h1>
|
||||
<div class="projects-grid">
|
||||
{
|
||||
projects.map((project) => (
|
||||
<Card
|
||||
title={project.title}
|
||||
image={project.image}
|
||||
links={[
|
||||
{
|
||||
label: "More...",
|
||||
href: project.link,
|
||||
newWindow: true,
|
||||
},
|
||||
]}
|
||||
>
|
||||
<div class="project-content">
|
||||
<p>{project.description}</p>
|
||||
</div>
|
||||
</Card>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
</Layout>
|
||||
|
||||
<style>
|
||||
.projects-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
|
||||
gap: 1rem;
|
||||
}
|
||||
.projects-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.project-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
height: 8rem;
|
||||
}
|
||||
.project-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
height: 8rem;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user