refactor: style tweaks
All checks were successful
Deploy Blog / deploy (push) Successful in 1m8s

This commit is contained in:
Thomas Bishop 2025-07-13 14:27:48 +01:00
parent 987dff1dce
commit 9a62f7bd1b
7 changed files with 288 additions and 240 deletions

View file

@ -1,9 +1,9 @@
// @ts-nocheck
import {
Card,
CardDescription,
CardHeader,
CardTitle,
Card,
CardDescription,
CardHeader,
CardTitle,
} from "@/components/ui/card"
import { Button } from "@/components/ui/button"
import { Link } from "react-router"
@ -11,45 +11,45 @@ import { Link } from "react-router"
import { convertDate } from "@/utils/convertDate"
const PostListing = ({ posts, title, showAllButton }) => {
return (
<>
<div className="mb-5 ">
<h2 className="scroll-m-20 text-2xl font-semibold lg:text-2xl border-b pb-3">
{title}
</h2>
</div>
<div className="grid grid-cols-1 md:grid-cols-1 gap-3">
{posts.map((post) => (
<Link
to={`/posts/${post.slug}`}
key={post.slug}
className="block no-underline"
>
<Card
key={post.slug}
className="flex flex-col h-full hover:bg-primary/5 py-4 px-0"
>
<CardHeader className="">
<CardTitle className="leading-snug font-semibold ">
{post.title}
</CardTitle>
<CardDescription className="text-sm text-muted-foreground">
<div className="flex justify-between gap-2">
<span className="text-sm">{convertDate(post.date)}</span>
</div>
</CardDescription>
</CardHeader>
</Card>
</Link>
))}
</div>
{showAllButton && (
<Button asChild variant="secondary" className="w-full mt-4">
<Link to="/posts/page/1">View all</Link>
</Button>
)}
</>
)
return (
<>
<div className="mb-5 ">
<h2 className="scroll-m-20 text-2xl font-bold lg:text-2xl border-b pb-3">
{title}
</h2>
</div>
<div className="grid grid-cols-1 md:grid-cols-1 gap-3">
{posts.map((post) => (
<Link
to={`/posts/${post.slug}`}
key={post.slug}
className="block no-underline"
>
<Card
key={post.slug}
className="flex flex-col h-full hover:bg-primary/5 py-4 px-0"
>
<CardHeader className="px-4 md:px-6">
<CardTitle className="leading-snug font-semibold ">
{post.title}
</CardTitle>
<CardDescription className="text-sm text-muted-foreground">
<div className="flex justify-between gap-2">
<span className="text-sm">{convertDate(post.date)}</span>
</div>
</CardDescription>
</CardHeader>
</Card>
</Link>
))}
</div>
{showAllButton && (
<Button asChild variant="" className="w-full mt-4">
<Link to="/posts/page/1">View all</Link>
</Button>
)}
</>
)
}
export default PostListing

View file

@ -2,89 +2,114 @@ import MainTemplate from "@/templates/MainTemplate"
import portrait from "../images/portrait-compressed.jpg"
const AboutPage = () => {
return (
<MainTemplate>
<div className="mb-5 ">
<h2 className="scroll-m-20 text-2xl font-semibold lg:text-2xl border-b pb-3">
About
</h2>
</div>
return (
<MainTemplate>
<div className="mb-5 ">
<h2 className="scroll-m-20 text-2xl font-bold lg:text-2xl border-b pb-3">
About
</h2>
</div>
<figure className="w-full flex flex-col items-center mb-6">
<img
alt="A portrait of the blog author"
src={portrait}
className="w-0 flex"
/>
<figcaption className="text-sm text-muted-foreground mt-3 text-center">
Pictured with the WITCH computer at the{" "}
<a
href="https://www.tnmoc.org/"
target="_blank"
className="text-primary hover:text-primary/80"
>
National Museum of Computing
</a>
, Bletchley Park.
</figcaption>
</figure>
<p className="leading-[1.5] [&:not(:first-child)]:mt-6">
Another software engineer with a blog!{" "}
</p>
<figure className="w-full flex flex-col items-center mb-6">
<img
alt="A portrait of the blog author"
src={portrait}
className="w-0 flex"
/>
<figcaption className="text-sm text-muted-foreground mt-3 text-center">
Pictured with the WITCH computer at the{" "}
<a
href="https://www.tnmoc.org/"
target="_blank"
className="text-primary hover:text-primary/80"
>
National Museum of Computing
</a>
, Bletchley Park.
</figcaption>
</figure>
<p className="leading-[1.5] [&:not(:first-child)]:mt-6">
Husband. Dad. Christian.
</p>
<p className="leading-[1.5] [&:not(:first-child)]:mt-6">
I am a self-taught engineer from London. This blog is a technical
scrapbook. I document the details of my technical life so I can have a
record of progress when I look back. Doing this publicly motivates me to
take care with my writing and to be as clear as possible.{" "}
</p>
<p className="leading-[1.5] [&:not(:first-child)]:mt-6">
Currently I work for{" "}
<a
href="https://en.wikipedia.org/wiki/ITV_(TV_network)"
target="_blank"
className="underline decoration-1 hover:text-primary/80 underline-offset-2"
>
ITV
</a>{" "}
{""}
as a backend software engineer. Before that, I worked as a full-stack
engineer at the{" "}
<a
href="https://en.wikipedia.org/wiki/BBC"
target="_blank"
className="underline decoration-1 hover:text-primary/80 underline-offset-2"
>
BBC
</a>{" "}
and as a frontend engineer at{" "}
<a
href="https://www.arria.com/"
target="_blank"
className="underline decoration-1 hover:text-primary/80 underline-offset-2"
>
Arria NLG
</a>{" "}
and in several web developer roles.{" "}
</p>
<p className="leading-[1.5] [&:not(:first-child)]:mt-6">
No, not really. I am a Londoner living on the south coast of the UK. I
am a self-taught software engineer and this blog is my technical
scrapbook. I document the details of my technical life so I can have a
record of progress when I look back.{" "}
</p>
<h3 className="mt-5 mb-0 scroll-m-20 text-xl font-semibold tracking-tight">
Code
</h3>
<p className="leading-[1.5] [&:not(:first-child)]:mt-6">
I completed a degree in Philosophy at the University of Warwick and hold
a Postgraduate Certificate of Education.
</p>
<p className="leading-[1.5] [&:not(:first-child)]:mt-6">
Currently I work for{" "}
<a
href="https://en.wikipedia.org/wiki/ITV_(TV_network)"
target="_blank"
className="underline decoration-1 hover:text-primary/80 underline-offset-2"
>
ITV
</a>{" "}
{""}
as a backend software engineer. Before that, I worked as a full-stack
engineer at the{" "}
<a
href="https://en.wikipedia.org/wiki/BBC"
target="_blank"
className="underline decoration-1 hover:text-primary/80 underline-offset-2"
>
BBC
</a>{" "}
and as a frontend engineer at{" "}
<a
href="https://www.arria.com/"
target="_blank"
className="underline decoration-1 hover:text-primary/80 underline-offset-2"
>
Arria NLG
</a>{" "}
and in several web developer roles. Before software I was a
teacher.{" "}
</p>
<p className="leading-[1.5] [&:not(:first-child)]:mt-6">
I self-host my own Git forge at{" "}
<a
href="https://forgejo.systemsobscure.net/thomasabishop"
className="underline decoration-1 hover:text-primary/80 underline-offset-2"
>
forgejo.systemsobscure.net
</a>{" "}
rather than use Microsoft GitHub. You can view my personal projects
there.
</p>
</MainTemplate>
)
<p className="leading-[1.5] [&:not(:first-child)]:mt-6">
Some things I like:
<ul className="pt-2">
<li className="mb-1">🐶 Staffies and bull-breeds</li>
<li className="mb-1">🎼 Classical music (Haydn, Mozart, JSB)</li>
<li className="mb-1">🛸 Science fiction </li>
</ul>
</p>
<p className="leading-[1.5] [&:not(:first-child)]:mt-6">
Some things I'm interested in:
<ul className="pt-2">
<li className="mb-1">🧑💻 Self-hosting and digital resiliance</li>
<li className="mb-1">🖳 The history of computing and networks</li>
<li className="mb-1">🇮🇪 Irish history and culture</li>
<li className="mb-1"> Buddhism</li>
{/*
<li className="mb-1">📡 Civil communications infrastructure</li>
*/}
</ul>
</p>
<p className="leading-[1.5] [&:not(:first-child)]:mt-6">
I self-host my own Git forge at{" "}
<a
href="https://forgejo.systemsobscure.net/thomasabishop"
className="underline decoration-1 hover:text-primary/80 underline-offset-2"
>
forgejo.systemsobscure.net
</a>{" "}
rather than use Microsoft GitHub. You can view my personal projects
there.
</p>
</MainTemplate>
)
}
export { AboutPage }

View file

@ -1,18 +1,41 @@
import MainTemplate from "@/templates/MainTemplate"
import PostListing from "@/containers/PostListing"
import { usePosts } from "@/hooks/usePosts"
import { Card, CardHeader, CardContent, CardFooter } from "@/components/ui/card"
import { Button } from "@/components/ui/button"
import { Link } from "react-router"
const HomePage = () => {
const { posts } = usePosts()
return (
<MainTemplate>
<PostListing
title="Recent posts"
posts={posts.slice(0, 5)}
showAllButton
/>
</MainTemplate>
)
const { posts } = usePosts()
return (
<MainTemplate>
<Card className="mb-8">
<CardHeader>
<h1 className="scroll-m-20 text-left text-3xl font-bold text-balance">
Another software engineer with a blog 🥱
</h1>
</CardHeader>
<CardContent>
<p className="leading-[1.5] [&:not(:first-child)]:mt-6">
I'm a self-taught software engineer currently working at ITV,
previously at the BBC. This blog is a technical scrapbook and
digital garden.
</p>
</CardContent>
<CardFooter>
<Button asChild>
<Link to="/about">About</Link>
</Button>
</CardFooter>
</Card>
<PostListing
title="Recent posts"
posts={posts.slice(0, 5)}
showAllButton
/>
</MainTemplate>
)
}
export { HomePage }

View file

@ -4,128 +4,128 @@ import MainTemplate from "@/templates/MainTemplate"
import { useParams, useNavigate } from "react-router"
import { convertDate } from "@/utils/convertDate"
import {
Pagination,
PaginationContent,
PaginationItem,
PaginationNext,
PaginationPrevious,
Pagination,
PaginationContent,
PaginationItem,
PaginationNext,
PaginationPrevious,
} from "@/components/ui/pagination"
import {
Card,
CardDescription,
CardHeader,
CardTitle,
Card,
CardDescription,
CardHeader,
CardTitle,
} from "@/components/ui/card"
import { Badge } from "@/components/ui/badge"
import { Link } from "react-router"
import { usePosts } from "@/hooks/usePosts"
const PostsPage = () => {
const { page } = useParams()
const { page } = useParams()
const navigate = useNavigate()
const navigate = useNavigate()
const { posts } = usePosts()
const postsPerPage = 10
const { posts } = usePosts()
const postsPerPage = 10
const currentPage = Number(page) || 1
const totalPages = Math.ceil(posts.length / postsPerPage)
const currentPage = Number(page) || 1
const totalPages = Math.ceil(posts.length / postsPerPage)
useEffect(() => {
if (currentPage < 1 || currentPage > totalPages) {
navigate(`/posts/page/1`, { replace: true })
}
}, [currentPage, totalPages, navigate])
useEffect(() => {
if (currentPage < 1 || currentPage > totalPages) {
navigate(`/posts/page/1`, { replace: true })
}
}, [currentPage, totalPages, navigate])
const currentPosts = useMemo(() => {
const startIndex = (currentPage - 1) * postsPerPage
const endIndex = startIndex + postsPerPage
return posts.slice(startIndex, endIndex)
}, [posts, currentPage, postsPerPage])
const currentPosts = useMemo(() => {
const startIndex = (currentPage - 1) * postsPerPage
const endIndex = startIndex + postsPerPage
return posts.slice(startIndex, endIndex)
}, [posts, currentPage, postsPerPage])
const hasNextPage = currentPage < totalPages
const hasPrevPage = currentPage > 1
const hasNextPage = currentPage < totalPages
const hasPrevPage = currentPage > 1
const goToNextPage = () => {
if (hasNextPage) {
navigate(`/posts/page/${currentPage + 1}`)
}
}
const goToNextPage = () => {
if (hasNextPage) {
navigate(`/posts/page/${currentPage + 1}`)
}
}
const goToPrevPage = () => {
if (hasPrevPage) {
navigate(`/posts/page/${currentPage - 1}`)
}
}
const goToPrevPage = () => {
if (hasPrevPage) {
navigate(`/posts/page/${currentPage - 1}`)
}
}
return (
<MainTemplate>
<div className="mb-5 ">
<h2 className="scroll-m-20 text-2xl font-semibold lg:text-2xl border-b pb-3">
All posts
</h2>
</div>
<div className="min-h-[calc(100vh-200px)] flex flex-col">
<div className="grid grid-cols-1 md:grid-cols-1 gap-3 flex-grow">
{currentPosts.map((post) => (
<Link
to={`/posts/${post.slug}`}
key={post.slug}
className="block no-underline"
>
<Card
key={post.slug}
className="flex flex-col h-full hover:bg-primary/5 py-4 px-0"
>
<CardHeader>
<CardTitle className="leading-snug font-semibold">
{post.title}
</CardTitle>
<CardDescription className="text-sm text-muted-foreground">
<div className="flex justify-between gap-2">
<span className="text-sm">{convertDate(post.date)}</span>
<div>
{post.tags.map((tag, i) => (
<Badge
className="ml-2 cursor-pointer"
key={i}
variant="secondary"
onClick={(e) => {
e.preventDefault()
e.stopPropagation()
navigate(`/tags/${tag}`)
}}
>
{tag}
</Badge>
))}
</div>
</div>
</CardDescription>
</CardHeader>
</Card>
</Link>
))}
</div>
<Pagination className="mt-4">
<PaginationContent>
<PaginationItem>
<PaginationPrevious
className={`select-none ${hasPrevPage ? "cursor-pointer" : "cursor-not-allowed opacity-50"}`}
onClick={goToPrevPage}
/>
</PaginationItem>
<PaginationItem>
<PaginationNext
className={`select-none ${hasNextPage ? "cursor-pointer" : "cursor-not-allowed opacity-50"}`}
onClick={goToNextPage}
/>
</PaginationItem>
</PaginationContent>
</Pagination>
</div>
</MainTemplate>
)
return (
<MainTemplate>
<div className="mb-5 ">
<h2 className="scroll-m-20 text-2xl font-bold lg:text-2xl border-b pb-3">
All posts
</h2>
</div>
<div className="min-h-[calc(100vh-200px)] flex flex-col">
<div className="grid grid-cols-1 md:grid-cols-1 gap-3 flex-grow">
{currentPosts.map((post) => (
<Link
to={`/posts/${post.slug}`}
key={post.slug}
className="block no-underline"
>
<Card
key={post.slug}
className="flex flex-col h-full hover:bg-primary/5 py-4 px-0"
>
<CardHeader>
<CardTitle className="leading-snug font-semibold">
{post.title}
</CardTitle>
<CardDescription className="text-sm text-muted-foreground">
<div className="flex justify-between gap-2">
<span className="text-sm">{convertDate(post.date)}</span>
<div className="hidden md:block">
{post.tags.map((tag, i) => (
<Badge
className="ml-2 cursor-pointer"
key={i}
variant="secondary"
onClick={(e) => {
e.preventDefault()
e.stopPropagation()
navigate(`/tags/${tag}`)
}}
>
{tag}
</Badge>
))}
</div>
</div>
</CardDescription>
</CardHeader>
</Card>
</Link>
))}
</div>
<Pagination className="mt-4">
<PaginationContent>
<PaginationItem>
<PaginationPrevious
className={`select-none ${hasPrevPage ? "cursor-pointer" : "cursor-not-allowed opacity-50"}`}
onClick={goToPrevPage}
/>
</PaginationItem>
<PaginationItem>
<PaginationNext
className={`select-none ${hasNextPage ? "cursor-pointer" : "cursor-not-allowed opacity-50"}`}
onClick={goToNextPage}
/>
</PaginationItem>
</PaginationContent>
</Pagination>
</div>
</MainTemplate>
)
}
export { PostsPage }

View file

@ -15,7 +15,7 @@ p code {
border-radius: var(--radius);
overflow-x: auto;
margin: 1.5rem 0;
/* line-height: 1.3; */
line-height: 1.3;
/* counter-reset: line; */
font-family: var(--font-monospaced) !important;
font-size: 14px !important;

View file

@ -17,7 +17,7 @@ const BlogTemplate = () => {
) : (
<>
<div className="mb-5">
<h2 className="text-2xl font-semibold lg:text-2xl border-b pb-3">
<h2 className="text-2xl font-bold lg:text-2xl border-b pb-3">
{post?.title}
</h2>
</div>

View file

@ -22,7 +22,7 @@ const MainContent = ({ children }) => {
<div className={classes}>
<Header />
<main className="flex-1 w-full px-2 md:px-4 flex justify-center pt-16">
<div className="w-full max-w-3xl lg:max-w-3xl xl:max-w-3xl px-4 py-3">
<div className="w-full max-w-3xl lg:max-w-3xl xl:max-w-3xl px-2 md:px-4 md:py-3 py-0">
{children}
</div>
</main>