Biome upgrades, theme upgrades, lots of daisy changes

This commit is contained in:
2025-07-05 12:29:48 +01:00
parent 95f317fd75
commit 89875a9341
29 changed files with 746 additions and 584 deletions

View File

@@ -1,5 +1,5 @@
import { signIn } from "@/lib/auth";
import type React from "react";
import { signIn } from "@/lib/auth";
export default function Auth(props: {
searchParams: Promise<{ callbackUrl: string | undefined }>;
@@ -17,7 +17,7 @@ export default function Auth(props: {
<button
type="submit"
className={
"rounded-lg border-transparent px-2 py-2 font-normal transition-colors duration-100 dark:bg-dracula-bg-light dark:text-white"
"rounded-lg border-transparent px-2 py-2 font-normal transition-colors duration-100"
}
>
<span>Sign in with Authelia</span>

View File

@@ -1,11 +1,13 @@
import Cv from "@/app/_components/cv";
import type React from "react";
import Cv from "@/app/_components/cv";
export default function CvPage(): React.JSX.Element {
return (
<div>
<div className="flex flex-row justify-center pb-4">
<button type="button" className="border px-4 py-2">Download</button>
<button type="button" className="border px-4 py-2">
Download
</button>
</div>
<Cv />
</div>

View File

@@ -1,11 +1,7 @@
import HomeMdx from "@/markdown/page.mdx";
export default function Home(): React.JSX.Element {
return (
<>
<HomeMdx />
</>
);
return <HomeMdx />;
}
// import { auth } from "@/server/auth";

View File

@@ -7,22 +7,22 @@ export default async function Photos(): Promise<React.JSX.Element> {
return (
<div className="mx-auto">
<FilteredLightbox imageData={images}>
{images.map((image) => (
<Image
key={image.src}
alt={image.src}
src={image.src}
className="h-60 w-80 object-contain"
sizes="100vw"
loading="lazy"
width={image.width}
height={image.height}
blurDataURL={image.blur}
placeholder="blur"
/>
))}
</FilteredLightbox>
<FilteredLightbox imageData={images}>
{images.map((image) => (
<Image
key={image.src}
alt={image.src}
src={image.src}
className="h-60 w-80 object-contain"
sizes="100vw"
loading="lazy"
width={image.width}
height={image.height}
blurDataURL={image.blur}
placeholder="blur"
/>
))}
</FilteredLightbox>
</div>
);
}

View File

@@ -2,6 +2,8 @@ import type React from "react";
export default function Post({
children,
}: { children: React.JSX.Element }): React.JSX.Element {
}: {
children: React.JSX.Element;
}): React.JSX.Element {
return <>{children}</>;
}

View File

@@ -1,7 +1,7 @@
import { getBaseUrl } from "@/lib/base-url";
import { glob } from "glob";
import { unstable_cache } from "next/cache";
import Link from "next/link";
import { getBaseUrl } from "@/lib/base-url";
type postDetails = {
link: string;

View File

@@ -1,6 +1,6 @@
import UserIcon from "@heroicons/react/24/outline/UserIcon";
import { getBaseUrl } from "@/lib/base-url";
import { auth, signIn, signOut } from "@/server/auth";
import UserCircleIcon from "@heroicons/react/24/outline/UserCircleIcon";
// TODO
export default async function LogIn(): Promise<React.JSX.Element | undefined> {
@@ -21,13 +21,13 @@ export default async function LogIn(): Promise<React.JSX.Element | undefined> {
>
<button
type="submit"
className="group rounded-3xl p-1 transition-colors dark:hover:bg-dracula-bg-light"
className="btn btn-circle hover:bg-primary/25 group border-2 border-primary/75 p-1 transition-colors duration-100"
>
<UserCircleIcon
<UserIcon
className={`h-8 w-auto transition-colors ${
session?.user
? "dark:stroke-dracula-red dark:group-hover:stroke-dracula-green"
: "dark:stroke-dracula-cyan dark:group-hover:stroke-dracula-orange"
? "stroke-warning"
: ""
}`}
/>
<span className="sr-only">{session?.user ? "Log out" : "Log in"}</span>

View File

@@ -115,7 +115,9 @@ const content: ExperienceContent[] = [
function Experience({
content,
}: { content: ExperienceContent }): React.JSX.Element {
}: {
content: ExperienceContent;
}): React.JSX.Element {
return (
<div className="flex flex-row gap-4 border-b-2 border-b-dracula-bg-light last:border-b-0 dark:border-b-dracula-orange">
<div className="w-20 justify-center text-center">

View File

@@ -1,10 +1,10 @@
// TODO
export default function NavBar(): React.JSX.Element {
return (
<footer className="border-t-2 dark:border-dracula-purple dark:bg-dracula-bg-darker">
<footer className="border-t-2 border-accent bg-base-300">
<div className="mx-auto max-w-7xl px-4">
<div className="relative flex h-12 flex-row-reverse items-center justify-between">
<span className="select-none dark:text-white">© Joe Monk 2024</span>
<span className="select-none">© Joe Monk 2025</span>
</div>
</div>
</footer>

View File

@@ -6,8 +6,8 @@ import {
} from "@heroicons/react/24/outline";
import {
AnimatePresence,
LazyMotion,
domAnimation,
LazyMotion,
motion,
} from "framer-motion";
import Link from "next/link";
@@ -43,14 +43,15 @@ export default function NavBarClient({
}, [pathname, navigation]);
return (
<nav className="border-accent border-b-2">
<nav className="border-accent border-b-2 bg-base-300">
<LazyMotion features={domAnimation}>
<div className="mx-auto max-w-5xl px-4">
<div className="relative flex h-16 items-center justify-between">
<div className="flex">
<button
type="button"
className="btn rounded-sm border-2 border-primary/75 p-1 transition-colors duration-100 sm:hidden"
className="btn rounded-sm
border-2 border-primary/75 p-1 transition-colors duration-100 sm:hidden"
onClick={() => setOpen(!open)}
>
{open ? (
@@ -70,7 +71,7 @@ export default function NavBarClient({
<Link
key={item.name}
href={item.href}
className={`btn min-w-20 rounded-lg rounded-b-none border-transparent border-b-2 px-3 pt-2.5 pb-1 text-center text-lg outline-0 hover:border-primary hover:bg-primary/25 ${
className={`btn min-w-20 rounded-lg rounded-b-none border-transparent border-b-2 px-3 pt-2.5 pb-1 text-center text-lg font-medium outline-0 hover:border-primary hover:bg-primary/25 ${
item.current ? "border-b-accent/75" : ""
}`}
aria-current={item.current ? "page" : undefined}
@@ -80,7 +81,7 @@ export default function NavBarClient({
))}
</div>
</div>
<div className="flex gap-4">
<div className="flex gap-4 items-center-safe">
<ThemeSwitcher />
{LogIn}
</div>

View File

@@ -22,11 +22,9 @@ export default function PostHeader({
<div className="mb-6">
{metadata.tags.map((tag) => {
return (
<>
<span className="me-2 select-none rounded border border-dracula-pink px-2.5 py-1 text-sm dark:bg-dracula-bg-darker dark:text-dracula-pink">
{tag}
</span>
</>
<span className="me-2 select-none rounded border border-dracula-pink px-2.5 py-1 text-sm dark:bg-dracula-bg-darker dark:text-dracula-pink">
{tag}
</span>
);
})}
</div>

View File

@@ -5,22 +5,24 @@ import type React from "react";
export default function ThemeSwitcher(): React.JSX.Element {
const toggleTheme = (): void => {
const currentTheme = document.documentElement.getAttribute('data-theme');
if (currentTheme === 'light') {
localStorage.theme = 'dark';
document.documentElement.setAttribute('data-theme', 'dark');
} else {
localStorage.theme = 'light';
document.documentElement.setAttribute('data-theme', 'light');
}
const currentTheme = document.documentElement.getAttribute("data-theme");
if (currentTheme === "dracula-pastel") {
localStorage.theme = "dracula-soft";
document.documentElement.setAttribute("data-theme", "dracula-soft");
} else {
localStorage.theme = "dracula-pastel";
document.documentElement.setAttribute("data-theme", "dracula-pastel");
}
};
return (
<>
<button type="button" className="m-1 h-8 w-8 rounded-full" onClick={toggleTheme}>
<MoonIcon className="block dark:hidden" />
<SunIcon className="hidden dark:block" />
</button>
</>
<button
type="button"
className="btn w-9 h-9 btn-circle border-2 hover:bg-primary/25 border-primary/75 p-1 transition-colors duration-100"
onClick={toggleTheme}
>
<MoonIcon className="block dark:hidden" />
<SunIcon className="hidden dark:block" />
</button>
);
}

View File

@@ -1,7 +1,7 @@
import "@/styles/globals.css";
import type { Metadata } from "next";
import { Fira_Sans } from "next/font/google";
import { Inter } from "next/font/google";
import { TRPCReactProvider } from "@/trpc/react";
import { HydrateClient } from "@/trpc/server";
@@ -12,22 +12,23 @@ export const metadata: Metadata = {
icons: [{ rel: "icon", url: "/favicon.ico" }],
};
const fira = Fira_Sans({
subsets: ["latin"],
variable: "--font-fira-sans",
weight: ["400", "500", "600"]
const inter = Inter({
subsets: ["latin"],
variable: "--font-inter",
weight: ["300", "400", "500", "600"],
});
export default function RootLayout({
children,
}: Readonly<{ children: React.ReactNode }>) {
return (
<html lang="en" className={`${fira.variable}`} suppressHydrationWarning>
<head>
<script id="SetTheme"
// biome-ignore lint/security/noDangerouslySetInnerHtml: Doing some pre-render theming
dangerouslySetInnerHTML={{
__html: `
<html lang="en" className={`${inter.variable}`} suppressHydrationWarning>
<head>
<script
id="SetTheme"
// biome-ignore lint/security/noDangerouslySetInnerHtml: Doing some pre-render theming
dangerouslySetInnerHTML={{
__html: `
if ('theme' in localStorage) {
document.documentElement.setAttribute('data-theme', localStorage.theme)
} else {
@@ -37,8 +38,9 @@ export default function RootLayout({
document.documentElement.setAttribute('data-theme', 'light')
}
}`,
}}/>
</head>
}}
/>
</head>
<body className="flex min-h-screen flex-col">
<TRPCReactProvider>
<HydrateClient>{children}</HydrateClient>

View File

@@ -3,4 +3,37 @@ A small personal site I use to practice and try things out. When I remember I'll
Built to try and use multiple modern web technologies in tandem to produce a great user experience from real data. Most parts have been built in a way in which I can swap it out with more interesting methods and projects.
I'm using Next.js with react query to make the pages load nicely and tailwindcss to make them look good easily. Page content is loaded from mdx files, including preview pages locked behind AWS Cognito. The thought being I'll offload them to load from a database instead of direct files, inserting via a wysiwyg editor behind the auth.
Photos are currently loaded from the filesystem, the metadata and EXIF data is read and a small image created which are all passed back as part of the page. This allows the initial page to have a small blur image, correctly sorted, allowing the page to be loaded quickly. When the page is loaded, the images can then be lazy loaded and optimized to reduce the impact on the server and the data to the client. Opening a full image will then load an unoptimized version to allow detail to be viewed.
Photos are currently loaded from the filesystem, the metadata and EXIF data is read and a small image created which are all passed back as part of the page. This allows the initial page to have a small blur image, correctly sorted, allowing the page to be loaded quickly. When the page is loaded, the images can then be lazy loaded and optimized to reduce the impact on the server and the data to the client. Opening a full image will then load an unoptimized version to allow detail to be viewed.
# Header
A small personal site I use to practice and try things out. When I remember I'll use it to log interesting or difficult projects.
Built to try and use multiple modern web technologies in tandem to produce a great user experience from real data. Most parts have been built in a way in which I can swap it out with more interesting methods and projects.
I'm using Next.js with react query to make the pages load nicely and tailwindcss to make them look good easily. Page content is loaded from mdx files, including preview pages locked behind AWS Cognito. The thought being I'll offload them to load from a database instead of direct files, inserting via a wysiwyg editor behind the auth.
Photos are currently loaded from the filesystem, the metadata and EXIF data is read and a small image created which are all passed back as part of the page. This allows the initial page to have a small blur image, correctly sorted, allowing the page to be loaded quickly. When the page is loaded, the images can then be lazy loaded and optimized to reduce the impact on the server and the data to the client. Opening a full image will then load an unoptimized version to allow detail to be viewed.
A small personal site I use to practice and try things out. When I remember I'll use it to log interesting or difficult projects.
Built to try and use multiple modern web technologies in tandem to produce a great user experience from real data. Most parts have been built in a way in which I can swap it out with more interesting methods and projects.
I'm using Next.js with react query to make the pages load nicely and tailwindcss to make them look good easily. Page content is loaded from mdx files, including preview pages locked behind AWS Cognito. The thought being I'll offload them to load from a database instead of direct files, inserting via a wysiwyg editor behind the auth.
Photos are currently loaded from the filesystem, the metadata and EXIF data is read and a small image created which are all passed back as part of the page. This allows the initial page to have a small blur image, correctly sorted, allowing the page to be loaded quickly. When the page is loaded, the images can then be lazy loaded and optimized to reduce the impact on the server and the data to the client. Opening a full image will then load an unoptimized version to allow detail to be viewed.
## Other Header
A small personal site I use to practice and try things out. When I remember I'll use it to log interesting or difficult projects.
Built to try and use multiple modern web technologies in tandem to produce a great user experience from real data. Most parts have been built in a way in which I can swap it out with more interesting methods and projects.
I'm using Next.js with react query to make the pages load nicely and tailwindcss to make them look good easily. Page content is loaded from mdx files, including preview pages locked behind AWS Cognito. The thought being I'll offload them to load from a database instead of direct files, inserting via a wysiwyg editor behind the auth.
Photos are currently loaded from the filesystem, the metadata and EXIF data is read and a small image created which are all passed back as part of the page. This allows the initial page to have a small blur image, correctly sorted, allowing the page to be loaded quickly. When the page is loaded, the images can then be lazy loaded and optimized to reduce the impact on the server and the data to the client. Opening a full image will then load an unoptimized version to allow detail to be viewed.
A small personal site I use to practice and try things out. When I remember I'll use it to log interesting or difficult projects.
Built to try and use multiple modern web technologies in tandem to produce a great user experience from real data. Most parts have been built in a way in which I can swap it out with more interesting methods and projects.
I'm using Next.js with react query to make the pages load nicely and tailwindcss to make them look good easily. Page content is loaded from mdx files, including preview pages locked behind AWS Cognito. The thought being I'll offload them to load from a database instead of direct files, inserting via a wysiwyg editor behind the auth.
Photos are currently loaded from the filesystem, the metadata and EXIF data is read and a small image created which are all passed back as part of the page. This allows the initial page to have a small blur image, correctly sorted, allowing the page to be loaded quickly. When the page is loaded, the images can then be lazy loaded and optimized to reduce the impact on the server and the data to the client. Opening a full image will then load an unoptimized version to allow detail to be viewed.

View File

@@ -5,10 +5,10 @@ export function useMDXComponents(components: MDXComponents): MDXComponents {
return {
wrapper: ({
children,
}: { children: React.JSX.Element[] }): React.JSX.Element => {
return (
<article className="prose mx-auto">{children}</article>
);
}: {
children: React.JSX.Element[];
}): React.JSX.Element => {
return <article className="prose mx-auto">{children}</article>;
},
...components,
};

View File

@@ -1,6 +1,6 @@
import { shake } from "radash";
import { db } from "@/server/db";
import { photos } from "@/server/db/schema";
import { shake } from "radash";
export type ImageData = {
width: number;

View File

@@ -3,13 +3,12 @@ import {
ListObjectsV2Command,
S3Client,
} from "@aws-sdk/client-s3";
import { TRPCError } from "@trpc/server";
import exifReader from "exif-reader";
import { diff, sift } from "radash";
import sharp from "sharp";
import { db } from "@/server/db";
import { photos } from "@/server/db/schema";
import { TRPCError } from "@trpc/server";
export async function update(): Promise<string[]> {
const allPhotos = await db.select().from(photos);

View File

@@ -7,7 +7,7 @@
* need to use are documented accordingly near the end.
*/
import { TRPCError, initTRPC } from "@trpc/server";
import { initTRPC, TRPCError } from "@trpc/server";
import superjson from "superjson";
import { ZodError } from "zod";

View File

@@ -1,6 +1,6 @@
import type { DefaultSession, NextAuthConfig } from "next-auth";
import { env } from "@/env";
import { getBaseUrl } from "@/lib/base-url";
import type { DefaultSession, NextAuthConfig } from "next-auth";
/**
* Module augmentation for `next-auth` types. Allows us to add custom properties to the `session`

View File

@@ -1,17 +1,103 @@
@import "tailwindcss";
@plugin "@tailwindcss/typography";
@plugin "daisyui" {
themes: light --default, dark --prefersdark;
@plugin "daisyui";
@plugin "daisyui/theme" {
name: "dracula-pastel";
default: true;
prefersdark: false;
color-scheme: "light";
--color-base-100: oklch(100% 0 0);
--color-base-200: oklch(98.462% 0.001 247.838);
--color-base-300: oklch(92.462% 0.001 247.838);
--color-base-content: oklch(20% 0 0);
--color-primary: oklch(90% 0.063 306.703);
--color-primary-content: oklch(49% 0.265 301.924);
--color-secondary: oklch(89% 0.058 10.001);
--color-secondary-content: oklch(51% 0.222 16.935);
--color-accent: oklch(90% 0.093 164.15);
--color-accent-content: oklch(50% 0.118 165.612);
--color-neutral: oklch(55% 0.046 257.417);
--color-neutral-content: oklch(92% 0.013 255.508);
--color-info: oklch(86% 0.127 207.078);
--color-info-content: oklch(52% 0.105 223.128);
--color-success: oklch(87% 0.15 154.449);
--color-success-content: oklch(52% 0.154 150.069);
--color-warning: oklch(83% 0.128 66.29);
--color-warning-content: oklch(55% 0.195 38.402);
--color-error: oklch(80% 0.114 19.571);
--color-error-content: oklch(50% 0.213 27.518);
--radius-selector: 0.5rem;
--radius-field: 0.5rem;
--radius-box: 0.5rem;
--size-selector: 0.25rem;
--size-field: 0.25rem;
--border: 1px;
--depth: 0;
--noise: 0;
}
@plugin "daisyui/theme" {
/* Nicked from the vscode soft theme https://github.com/dracula/visual-studio-code/blob/master/src/dracula.yml */
name: "dracula-soft";
default: false;
prefersdark: false;
color-scheme: "dark";
/* --color-base-50: oklch(34.02% 0.027 276.05); */
--color-base-100: oklch(28.82% 0.022 277.51);
--color-base-200: oklch(25.54% 0.019 280.49);
--color-base-300: oklch(21.99% 0.014 278.80);
--color-base-content: oklch(91% 0.020 278);
--color-primary: oklch(88.263% 0.093 212.846);
--color-primary-content: oklch(17.652% 0.018 212.846);
--color-secondary: oklch(83.392% 0.124 66.558);
--color-secondary-content: oklch(16.678% 0.024 66.558);
--color-accent: oklch(74.202% 0.148 301.883);
--color-accent-content: oklch(14.84% 0.029 301.883);
--color-neutral: oklch(38.94% 0.020 277.93);
--color-neutral-content: oklch(87.889% 0.006 275.524);
--color-info: oklch(75.461% 0.183 346.812);
--color-info-content: oklch(15.092% 0.036 346.812);
--color-success: oklch(87.099% 0.219 148.024);
--color-success-content: oklch(17.419% 0.043 148.024);
--color-warning: oklch(95.533% 0.134 112.757);
--color-warning-content: oklch(15.106% 0.026 112.757);
--color-error: oklch(68.22% 0.206 24.43);
--color-error-content: oklch(13.644% 0.041 24.43);
--radius-selector: 0.5rem;
--radius-field: 0.5rem;
--radius-box: 0.5rem;
--size-selector: 0.25rem;
--size-field: 0.25rem;
--border: 1px;
--depth: 0;
--noise: 0;
}
@theme {
--font-sans: var(--font-fira-sans), ui-sans-serif, system-ui, sans-serif,
--font-sans:
var(--font-inter), ui-sans-serif, system-ui, sans-serif,
"Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
}
@custom-variant dark (&:where([data-theme=dark], [data-theme=dark] *));
@custom-variant dark (&:where([data-theme=dracula-soft], [data-theme=dracula-soft] *));
@utility btn {
@apply shadow-none bg-transparent;
@apply shadow-none bg-transparent;
}
:root .prose {
--tw-prose-body: color-mix(in oklab, var(--color-base-content) 92%, #0000) !important;
}

View File

@@ -1,6 +1,6 @@
import {
QueryClient,
defaultShouldDehydrateQuery,
QueryClient,
} from "@tanstack/react-query";
import SuperJSON from "superjson";

View File

@@ -12,7 +12,7 @@ import { getBaseUrl } from "@/lib/base-url";
import type { AppRouter } from "@/server/api/root";
import { createQueryClient } from "./query-client";
let clientQueryClientSingleton: QueryClient | undefined = undefined;
let clientQueryClientSingleton: QueryClient | undefined;
const getQueryClient = () => {
if (typeof window === "undefined") {
// Server: always make a new query client