From 0903a3560a27a625c5ca209b0e76ab79a66a3f74 Mon Sep 17 00:00:00 2001 From: ankitsejwal Date: Sun, 28 Jun 2026 17:39:14 +0530 Subject: [PATCH 1/8] build(web): add bundled handwriting fonts via fontsource --- apps/web/package.json | 4 ++++ pnpm-lock.yaml | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/apps/web/package.json b/apps/web/package.json index e9102eb..c2a8692 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -10,6 +10,10 @@ "lint": "oxlint" }, "dependencies": { + "@fontsource/caveat": "^5.2.8", + "@fontsource/kalam": "^5.2.8", + "@fontsource/patrick-hand": "^5.2.8", + "@fontsource/shadows-into-light": "^5.2.8", "@radix-ui/react-slot": "^1.3.0", "@tanstack/react-router": "^1.170.16", "class-variance-authority": "^0.7.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 06e6f32..1347897 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -17,6 +17,18 @@ importers: apps/web: dependencies: + '@fontsource/caveat': + specifier: ^5.2.8 + version: 5.2.8 + '@fontsource/kalam': + specifier: ^5.2.8 + version: 5.2.8 + '@fontsource/patrick-hand': + specifier: ^5.2.8 + version: 5.2.8 + '@fontsource/shadows-into-light': + specifier: ^5.2.8 + version: 5.2.8 '@radix-ui/react-slot': specifier: ^1.3.0 version: 1.3.0(@types/react@19.2.17)(react@19.2.7) @@ -157,6 +169,18 @@ packages: '@emnapi/wasi-threads@1.2.2': resolution: {integrity: sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==} + '@fontsource/caveat@5.2.8': + resolution: {integrity: sha512-9fUUfFE2IQFKbx+xOcaeQxxmh8iJguEb8z+j1PeueO4UUx+XfT4pRm/B04ZDvFA794/iRxY/IibmP8ZKtIf4rw==} + + '@fontsource/kalam@5.2.8': + resolution: {integrity: sha512-9WDYPX1llS28xRjrqAoz3IOLeaBhCtL3HCtNanYnF1fthFPmSD5aESL6s7C1SJxAlxq/qzTlpIpw4S1W9+q3OA==} + + '@fontsource/patrick-hand@5.2.8': + resolution: {integrity: sha512-FZx34VLuiF+Pjv/A/m1bTxjR6hAvmL6vdCp62uypVdgFvSrPrBOxX+vCXVy8lfNVFqvKfm/ghun+aaYVYqfBEg==} + + '@fontsource/shadows-into-light@5.2.8': + resolution: {integrity: sha512-ZTiWDiLQmG8kjWEb2o3vicAqpEi02/SziTClaSXRRKpdpXv5w/Jv3kG7EVK9d7wlFm3/4U3hhaFOcIuHznmbSQ==} + '@jridgewell/gen-mapping@0.3.13': resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} @@ -1293,6 +1317,14 @@ snapshots: tslib: 2.8.1 optional: true + '@fontsource/caveat@5.2.8': {} + + '@fontsource/kalam@5.2.8': {} + + '@fontsource/patrick-hand@5.2.8': {} + + '@fontsource/shadows-into-light@5.2.8': {} + '@jridgewell/gen-mapping@0.3.13': dependencies: '@jridgewell/sourcemap-codec': 1.5.5 From e5e29dddff9c9e41641f132da0ddb0ab7f28f4cd Mon Sep 17 00:00:00 2001 From: ankitsejwal Date: Sun, 28 Jun 2026 17:39:32 +0530 Subject: [PATCH 2/8] feat(web): load bundled caption fonts --- apps/web/src/main.tsx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/apps/web/src/main.tsx b/apps/web/src/main.tsx index 592f518..53bac28 100644 --- a/apps/web/src/main.tsx +++ b/apps/web/src/main.tsx @@ -4,6 +4,10 @@ import { RouterProvider, createRouter } from '@tanstack/react-router' import { routeTree } from './routeTree.gen' import './styles.css' +import '@fontsource/caveat' +import '@fontsource/patrick-hand' +import '@fontsource/shadows-into-light' +import '@fontsource/kalam' const router = createRouter({ routeTree }) From 271dd6b6164bbdf785b3a0387a00fee05157057d Mon Sep 17 00:00:00 2001 From: ankitsejwal Date: Sun, 28 Jun 2026 17:39:49 +0530 Subject: [PATCH 3/8] feat(web): add caption font registry --- apps/web/src/lib/fonts.ts | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 apps/web/src/lib/fonts.ts diff --git a/apps/web/src/lib/fonts.ts b/apps/web/src/lib/fonts.ts new file mode 100644 index 0000000..f0fe892 --- /dev/null +++ b/apps/web/src/lib/fonts.ts @@ -0,0 +1,25 @@ +export interface CaptionFont { + id: string + label: string + /** CSS font-family stack applied to caption text. */ + stack: string +} + +export const CAPTION_FONTS: CaptionFont[] = [ + { id: 'caveat', label: 'Caveat', stack: '"Caveat", cursive' }, + { id: 'patrick-hand', label: 'Patrick Hand', stack: '"Patrick Hand", cursive' }, + { + id: 'shadows-into-light', + label: 'Shadows Into Light', + stack: '"Shadows Into Light", cursive', + }, + { id: 'kalam', label: 'Kalam', stack: '"Kalam", cursive' }, +] + +export const DEFAULT_CAPTION_FONT_ID = CAPTION_FONTS[0].id + +export function captionFontStack(id: string): string { + return ( + CAPTION_FONTS.find((font) => font.id === id)?.stack ?? CAPTION_FONTS[0].stack + ) +} From fad32657bbcc5b475b114d01c4ffa7447cac8031 Mon Sep 17 00:00:00 2001 From: ankitsejwal Date: Sun, 28 Jun 2026 17:40:12 +0530 Subject: [PATCH 4/8] feat(web): track selected caption font in settings --- apps/web/src/stores/settings-store.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/apps/web/src/stores/settings-store.ts b/apps/web/src/stores/settings-store.ts index 23d3290..f1d72ae 100644 --- a/apps/web/src/stores/settings-store.ts +++ b/apps/web/src/stores/settings-store.ts @@ -1,5 +1,7 @@ import { create } from 'zustand' +import { DEFAULT_CAPTION_FONT_ID } from '@/lib/fonts' + export const MIN_FRAME_PADDING = 6 export const MAX_FRAME_PADDING = 28 @@ -7,9 +9,13 @@ interface SettingsState { /** White polaroid border width in px (sides/top); the bottom is thicker. */ framePadding: number setFramePadding: (px: number) => void + captionFontId: string + setCaptionFont: (id: string) => void } export const useSettingsStore = create((set) => ({ framePadding: 14, setFramePadding: (px) => set({ framePadding: px }), + captionFontId: DEFAULT_CAPTION_FONT_ID, + setCaptionFont: (id) => set({ captionFontId: id }), })) From 0d55fb46320649dd194e11e85ed4b20ac5b12d34 Mon Sep 17 00:00:00 2001 From: ankitsejwal Date: Sun, 28 Jun 2026 17:41:17 +0530 Subject: [PATCH 5/8] feat(web): apply selected caption font to polaroids --- apps/web/src/components/polaroid.tsx | 10 +++++++++- apps/web/src/styles.css | 1 - 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/apps/web/src/components/polaroid.tsx b/apps/web/src/components/polaroid.tsx index 03f7518..a7b435f 100644 --- a/apps/web/src/components/polaroid.tsx +++ b/apps/web/src/components/polaroid.tsx @@ -2,6 +2,7 @@ import { useState } from 'react' import { ImageOff } from 'lucide-react' import { cn } from '@/lib/utils' +import { captionFontStack } from '@/lib/fonts' import { type Photo } from '@/lib/photos' import { usePhotoStore } from '@/stores/photo-store' import { useSettingsStore } from '@/stores/settings-store' @@ -9,6 +10,8 @@ import { useSettingsStore } from '@/stores/settings-store' export function Polaroid({ photo }: { photo: Photo }) { const setCaption = usePhotoStore((state) => state.setCaption) const framePadding = useSettingsStore((state) => state.framePadding) + const captionFontId = useSettingsStore((state) => state.captionFontId) + const fontFamily = captionFontStack(captionFontId) const [failed, setFailed] = useState(false) return ( @@ -43,11 +46,13 @@ export function Polaroid({ photo }: { photo: Photo }) { value={photo.captionTop} onChange={(value) => setCaption(photo.id, 'captionTop', value)} placeholder="Add a caption" + fontFamily={fontFamily} className="text-lg leading-tight" /> setCaption(photo.id, 'captionBottom', value)} + fontFamily={fontFamily} className="text-sm text-neutral-500" /> @@ -60,11 +65,13 @@ function CaptionInput({ onChange, placeholder, className, + fontFamily, }: { value: string onChange: (value: string) => void placeholder?: string className?: string + fontFamily: string }) { return ( onChange(event.target.value)} placeholder={placeholder} aria-label="Polaroid caption" + style={{ fontFamily }} className={cn( - 'font-handwriting w-full border-0 bg-transparent text-center text-neutral-800 placeholder:text-neutral-300 focus:outline-none', + 'w-full border-0 bg-transparent text-center text-neutral-800 placeholder:text-neutral-300 focus:outline-none', className, )} /> diff --git a/apps/web/src/styles.css b/apps/web/src/styles.css index 4f93859..269edc5 100644 --- a/apps/web/src/styles.css +++ b/apps/web/src/styles.css @@ -42,7 +42,6 @@ } @theme inline { - --font-handwriting: "Caveat", "Segoe Print", "Bradley Hand", cursive; --radius-sm: calc(var(--radius) - 4px); --radius-md: calc(var(--radius) - 2px); --radius-lg: var(--radius); From 9a9645d4522624447381b45d9f5fcb94f7a94be3 Mon Sep 17 00:00:00 2001 From: ankitsejwal Date: Sun, 28 Jun 2026 17:41:36 +0530 Subject: [PATCH 6/8] feat(web): add caption font picker control --- .../src/components/caption-font-control.tsx | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 apps/web/src/components/caption-font-control.tsx diff --git a/apps/web/src/components/caption-font-control.tsx b/apps/web/src/components/caption-font-control.tsx new file mode 100644 index 0000000..8ef360c --- /dev/null +++ b/apps/web/src/components/caption-font-control.tsx @@ -0,0 +1,34 @@ +import { CAPTION_FONTS } from '@/lib/fonts' +import { useSettingsStore } from '@/stores/settings-store' + +export function CaptionFontControl() { + const captionFontId = useSettingsStore((state) => state.captionFontId) + const setCaptionFont = useSettingsStore((state) => state.setCaptionFont) + + return ( +
+ + +
+ ) +} From 156a39da6d621d08513c829aac627ba07ca8333b Mon Sep 17 00:00:00 2001 From: ankitsejwal Date: Sun, 28 Jun 2026 17:42:09 +0530 Subject: [PATCH 7/8] feat(web): add font picker to grid controls --- apps/web/src/components/photo-grid.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/web/src/components/photo-grid.tsx b/apps/web/src/components/photo-grid.tsx index f5bd4e8..88bea39 100644 --- a/apps/web/src/components/photo-grid.tsx +++ b/apps/web/src/components/photo-grid.tsx @@ -1,5 +1,6 @@ import { X } from 'lucide-react' +import { CaptionFontControl } from '@/components/caption-font-control' import { FrameControls } from '@/components/frame-controls' import { Polaroid } from '@/components/polaroid' import { Button } from '@/components/ui/button' @@ -18,7 +19,8 @@ export function PhotoGrid() {

{photos.length} {photos.length === 1 ? 'photo' : 'photos'}

-
+
+