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/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 ( +
+ + +
+ ) +} 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'}

-
+
+
@@ -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/fontsource.d.ts b/apps/web/src/fontsource.d.ts new file mode 100644 index 0000000..ffd952a --- /dev/null +++ b/apps/web/src/fontsource.d.ts @@ -0,0 +1,3 @@ +// @fontsource packages ship CSS with no type declarations; declare them so +// side-effect imports satisfy `noUncheckedSideEffectImports`. +declare module '@fontsource/*' 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 + ) +} 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 }) 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 }), })) 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); 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