style: tweaks

This commit is contained in:
Thomas Bishop 2025-07-31 16:44:15 +01:00
parent 43eec03edd
commit a9440d6844
6 changed files with 232 additions and 213 deletions

View file

@ -65,7 +65,7 @@ const Menu = () => {
const Header = () => { const Header = () => {
const { theme, setTheme } = useTheme() const { theme, setTheme } = useTheme()
return ( return (
<header className="w-full h-12 flex items-center justify-center border-b fixed top-0 z-20 bg-background"> <header className="w-full h-12 flex items-center justify-center border-b fixed top-0 z-20 bg-sidebar">
<div className="w-full px-0 md:px-4 flex items-center justify-between"> <div className="w-full px-0 md:px-4 flex items-center justify-between">
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<Button variant="ghost" asChild> <Button variant="ghost" asChild>

View file

@ -13,8 +13,8 @@ import { convertDate } from "@/utils/convertDate"
const PostListing = ({ posts, title, showAllButton }) => { const PostListing = ({ posts, title, showAllButton }) => {
return ( return (
<> <>
<div className="mb-5 "> <div className="mb-5">
<h2 className="scroll-m-20 text-2xl font-semibold lg:text-2xl border-b pb-3"> <h2 className="scroll-m-20 text-[1.3rem] font-semibold border-b pb-2">
{title} {title}
</h2> </h2>
</div> </div>
@ -44,7 +44,7 @@ const PostListing = ({ posts, title, showAllButton }) => {
))} ))}
</div> </div>
{showAllButton && ( {showAllButton && (
<Button asChild variant="" className="w-full mt-4"> <Button asChild variant="secondary" className="w-full mt-4">
<Link to="/posts/page/1">View all</Link> <Link to="/posts/page/1">View all</Link>
</Button> </Button>
)} )}

View file

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

View file

@ -9,14 +9,25 @@ const HomePage = () => {
const { posts } = usePosts() const { posts } = usePosts()
return ( return (
<MainTemplate> <MainTemplate>
<Card className="mb-8 rounded-none"> <div className="mb-7 border border-foreground py-7 px-6 bg-sidebar">
<h1 className="scroll-m-20 text-left text-2xl font-semibold">
Another software engineer with a blog
</h1>
<p className="leading-[1.7] [&:not(:first-child)]:mt-5">
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>
</div>
{/*
<Card className="mb-8 rounded-none">
<CardHeader> <CardHeader>
<h1 className="scroll-m-20 text-left text-3xl font-semibold"> <h1 className="scroll-m-20 text-left text-2xl font-semibold">
Another software engineer with a blog Another software engineer with a blog!
</h1> </h1>
</CardHeader> </CardHeader>
<CardContent> <CardContent>
<p className="leading-[1.5] [&:not(:first-child)]:mt-6"> <p className="leading-[1.7] [&:not(:first-child)]:mt-7">
I'm a self-taught software engineer currently working at ITV, I'm a self-taught software engineer currently working at ITV,
previously at the BBC. This blog is a technical scrapbook and previously at the BBC. This blog is a technical scrapbook and
digital garden. digital garden.
@ -28,6 +39,13 @@ const HomePage = () => {
</Button> </Button>
</CardFooter> </CardFooter>
</Card> </Card>
<Button asChild>
<Link to="/about">About</Link>
</Button>
*/}
<PostListing <PostListing
title="Recent posts" title="Recent posts"

View file

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

View file

@ -37,10 +37,10 @@ const BlogTemplate = () => {
</div> </div>
<div <div
className=" className="
[&>h2]:text-xl [&>h2]:border-b [&>h2]:pb-2 [&>h2]:font-semibold [&>h2]:first:mt-0 [&>h2:not(:first-child)]:mt-8 [&>h2]:text-xl [&>h2]:font-semibold [&>h2]:first:mt-0 [&>h2:not(:first-child)]:mt-8
[&>h3]:text-xl [&>h3]:sm:text-1xl [&>h3]:font-semibold [&>h3:not(:first-child)]:mt-5 [&>h3]:text-xl [&>h3]:sm:text-1xl [&>h3]:font-semibold [&>h3:not(:first-child)]:mt-5
[&>h4]:text-lg [&>h4]:sm:text-xl [&>h4]:font-semibold [&>h4:not(:first-child)]:mt-4 [&>h4]:text-lg [&>h4]:sm:text-xl [&>h4]:font-semibold [&>h4:not(:first-child)]:mt-4
[&>p]:leading-7 [&>p:not(:first-child)]:mt-6 [&>p]:leading-7 [&>p:not(:first-child)]:mt-4
[&>p+:is(h1,h2,h3,h4,h5,h6)]:mt-6 [&>p+:is(h1,h2,h3,h4,h5,h6)]:mt-6
[&>blockquote]:mt-4 [&>blockquote]:border-l-2 [&>blockquote]:pl-6 [&>blockquote]:text-muted-foreground [&>blockquote]:text-sm [&>blockquote]:mt-4 [&>blockquote]:border-l-2 [&>blockquote]:pl-6 [&>blockquote]:text-muted-foreground [&>blockquote]:text-sm
[&>ul]:my-4 [&>ul]:ml-6 [&>ul]:list-disc [&>ul>li]:mt-2 [&>ul]:my-4 [&>ul]:ml-6 [&>ul]:list-disc [&>ul>li]:mt-2
@ -57,10 +57,10 @@ const BlogTemplate = () => {
[&>p+pre]:mt-6 [&>p+pre]:mt-6
[&>pre+p]:mt-6 [&>pre+p]:mt-6
[&>ul+pre]:mt-6 [&>ul+pre]:mt-6
[&>li]:leading-[1.5] [&>li]:leading-[1.6]
[&_li_code]:relative [&_li_code]:rounded [&_li_code]:bg-muted [&_li_code]:px-[0.3rem] [&_li_code]:py-[0.2rem] [&_li_code]:font-mono [&_li_code]:text-sm [&_li_code]:font-normal [&_li_code]:relative [&_li_code]:rounded [&_li_code]:bg-muted [&_li_code]:px-[0.3rem] [&_li_code]:py-[0.2rem] [&_li_code]:font-mono [&_li_code]:text-sm [&_li_code]:font-normal
[&>p]:mt-6 [&>p]:leading-[1.5] [&>p]:mt-6 [&>p]:leading-[1.6]
[&_a]:underline [&_a]:underline-offset-4 [&_a]:hover:text-muted-foreground [&_a]:underline [&_a]:underline-offset-4 [&_a]:hover:text-muted-foreground [&_a]:font-medium
[&>figure]:w-full [&>figure]:flex [&>figure]:flex-col [&>figure]:items-center [&>figure]:justify-center [&>figure]:mb-6 [&>figure]:mx-auto [&>figure]:w-full [&>figure]:flex [&>figure]:flex-col [&>figure]:items-center [&>figure]:justify-center [&>figure]:mb-6 [&>figure]:mx-auto
[&>figure>img]:w-full [&>figure>img]:w-full
[&>figure>figcaption]:text-sm [&>figure>figcaption]:text-muted-foreground [&>figure>figcaption]:mt-3 [&>figure>figcaption]:text-center [&>figure>figcaption]:text-sm [&>figure>figcaption]:text-muted-foreground [&>figure>figcaption]:mt-3 [&>figure>figcaption]:text-center