"use client"; /** * Helpers shared by PostGridCard and PostScrollCard. * * • postSourceHref — synthesizes `?hl=` if the stored URL lacks one * • themeGradientStyle — picks one of four palette-aware gradients * deterministically from a seed (post id) so a * feed of image-less posts looks varied without * shifting between renders. */ import type { Post } from "@/lib/social"; import { appendHighlight, hasHighlight } from "@/lib/text-highlight"; export function postSourceHref(post: Post): string { return hasHighlight(post.source_url) || !post.caption ? post.source_url : appendHighlight(post.source_url, post.caption); } // Quote-card gradients. // // Four CSS custom properties (`--quote-gradient-1` … 4) are defined per // palette in `app/globals.css`. Each palette ships its own hand-tuned // gradient set so the cards swap colour with the active theme: // // • Aged Parchment → mahogany binding + temple gold // • Tulasī Garden → peacock depths + temple gold // • Yamunā Mist → river dusk navy + sandbank // • Ancient Manuscript → midnight leather + saffron // • Nīlamaṇi → peacock night over still water // • Vraja Twilight → deep wine + rose dusk // // All variants are dark-leaning so `text-white` reads cleanly. The card // picks one of the four deterministically from `post.id`. const GRADIENT_VARS = [ "var(--quote-gradient-1)", "var(--quote-gradient-2)", "var(--quote-gradient-3)", "var(--quote-gradient-4)", ]; /** Returns an inline-style object that applies the chosen gradient. The * style approach (vs a className) lets us reference CSS custom properties * that vary per palette — Tailwind can't generate utility classes for * values that change at runtime via `:root` selectors. */ export function themeGradientStyle(seed: string): React.CSSProperties { // Cheap, stable hash — same seed always picks the same gradient. let h = 0; for (let i = 0; i < seed.length; i++) h = (h * 31 + seed.charCodeAt(i)) >>> 0; return { backgroundImage: GRADIENT_VARS[h % GRADIENT_VARS.length] }; } /** Flat background for post cards without an image: pure white on every * light palette, palette-specific darker contrast on dark palettes. * Defined as `--quote-card-bg` per `:root` / `.dark` block in * globals.css. Combine with `text-foreground` for legibility — both * sides of the theme switch reverse together. */ export function quoteCardBgStyle(): React.CSSProperties { return { backgroundColor: "var(--quote-card-bg)" }; } // ─────────────────────────────────────────────────────────────────────────── // Variable quote sizing // ─────────────────────────────────────────────────────────────────────────── // // A pithy two-word quote and a full paragraph need very different // treatments inside a fixed-size card. We pick a tier from the caption's // character count: // // tier 1 (very short) — biggest font, dead-centre, no truncation needed // tier 2 (short) — large font, centred, ~5-line clamp // tier 3 (medium) — medium font, centred, ~6/7-line clamp // tier 4 (long) — smallest font, anchored bottom-LEFT and left- // aligned (a centred wall of small text would // cover the entire card and look claustrophobic; // bottom-left mimics the image variant's caption // placement and breathes better). // // Two scales: "grid" for the small square card, "scroll" for the larger // Instagram-style card. export type QuoteSize = { /** Tailwind font-size classes, e.g. "text-xl sm:text-2xl" */ fontClass: string; /** Tailwind line-clamp class limiting overflow */ clampClass: string; /** Where to anchor the text block inside its container */ alignment: "center" | "bottom-left"; }; export function quoteSize(text: string, variant: "grid" | "scroll"): QuoteSize { const len = (text ?? "").trim().length; if (variant === "grid") { if (len <= 50) return { fontClass: "text-xl sm:text-2xl", clampClass: "line-clamp-3", alignment: "center", }; if (len <= 150) return { fontClass: "text-base sm:text-lg", clampClass: "line-clamp-5", alignment: "center", }; if (len <= 300) return { fontClass: "text-sm sm:text-base", clampClass: "line-clamp-[7]", alignment: "center", }; return { fontClass: "text-xs sm:text-sm", clampClass: "line-clamp-[9]", alignment: "bottom-left", }; } // scroll — bigger card, larger fonts at every tier, higher thresholds // so paragraph-length quotes still get centred at a comfortable size. if (len <= 60) return { fontClass: "text-3xl sm:text-4xl", clampClass: "line-clamp-3", alignment: "center", }; if (len <= 180) return { fontClass: "text-2xl sm:text-3xl", clampClass: "line-clamp-5", alignment: "center", }; if (len <= 380) return { fontClass: "text-lg sm:text-xl", clampClass: "line-clamp-[8]", alignment: "center", }; return { fontClass: "text-base sm:text-lg", clampClass: "line-clamp-[10]", alignment: "bottom-left", }; }