homepage: typography upgrade + ArticleFeed polish
- layout.tsx: load Lora (serif) + Inter (sans) via next/font/google, self-hosted at build time, exposed as --font-serif / --font-sans CSS variables on <html>. No CDN fetch, no FOIT, no GDPR concern. - tailwind.css: --font-heading and --font-body now reference the next/font variables with Georgia / -apple-system fallbacks for the brief window before the self-hosted face is ready. --font-mono upgraded to IBM Plex Mono → ui-monospace → Courier New fallback (DoubleTicker chips already specify IBM Plex Mono directly). - ArticleFeed: drop the FEATURED filter chip (duplicates the Staff Editorial section above), add publish date alongside the byline on every card, lift the homepage feed cap from 200 → 5000. - api/public/posts: lift MAX_LIMIT to 10000 so client-side pagination + infinite-scroll see the full archive.
This commit is contained in:
@@ -3,7 +3,10 @@ import { getLatestImportedArticles } from "@/lib/articles/legacy-source";
|
|||||||
|
|
||||||
export const revalidate = 300;
|
export const revalidate = 300;
|
||||||
|
|
||||||
const MAX_LIMIT = 200;
|
// Homepage feed now renders every imported post; pagination + infinite-
|
||||||
|
// scroll happen client-side over the full set. Keep an absolute ceiling
|
||||||
|
// only as a safety valve, not a UX cap.
|
||||||
|
const MAX_LIMIT = 10000;
|
||||||
const DEFAULT_LIMIT = 100;
|
const DEFAULT_LIMIT = 100;
|
||||||
|
|
||||||
export async function GET(request: Request) {
|
export async function GET(request: Request) {
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ interface ArticleItem {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
const ALL_CATEGORIES = ["All", "BROADCAST", "FEATURED", "POST PRODUCTION", "ANIMATION"];
|
const ALL_CATEGORIES = ["All", "BROADCAST", "POST PRODUCTION", "ANIMATION"];
|
||||||
const SOURCE_FILTERS = ["All", "Live", "Imported"] as const;
|
const SOURCE_FILTERS = ["All", "Live", "Imported"] as const;
|
||||||
type SourceFilter = typeof SOURCE_FILTERS[number];
|
type SourceFilter = typeof SOURCE_FILTERS[number];
|
||||||
type FeedMode = "pagination" | "infinite";
|
type FeedMode = "pagination" | "infinite";
|
||||||
@@ -94,7 +94,7 @@ export default function ArticleFeed() {
|
|||||||
let cancelled = false;
|
let cancelled = false;
|
||||||
(async () => {
|
(async () => {
|
||||||
try {
|
try {
|
||||||
const res = await fetch("/api/public/posts?limit=200", { cache: "no-store" });
|
const res = await fetch("/api/public/posts?limit=5000", { cache: "no-store" });
|
||||||
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
||||||
const json = await res.json();
|
const json = await res.json();
|
||||||
const items = Array.isArray(json?.items) ? (json.items as ArticleItem[]) : [];
|
const items = Array.isArray(json?.items) ? (json.items as ArticleItem[]) : [];
|
||||||
@@ -613,11 +613,19 @@ export default function ArticleFeed() {
|
|||||||
{article?.excerpt}
|
{article?.excerpt}
|
||||||
</p>
|
</p>
|
||||||
<div className="flex items-center justify-between gap-2">
|
<div className="flex items-center justify-between gap-2">
|
||||||
<div className="flex items-center gap-1 hidden sm:flex">
|
<div className="hidden sm:flex items-center gap-2 text-xs text-[#666] min-w-0">
|
||||||
<PersonIcon size={11} className="text-[#666] flex-shrink-0" />
|
<PersonIcon size={11} className="text-[#666] flex-shrink-0" />
|
||||||
<span className="font-body text-xs text-[#666]">
|
<span className="font-body truncate">
|
||||||
By {article?.author}
|
By {article?.author}
|
||||||
</span>
|
</span>
|
||||||
|
{article?.date && (
|
||||||
|
<>
|
||||||
|
<span aria-hidden="true" className="text-[#333]">·</span>
|
||||||
|
<time className="font-body whitespace-nowrap" dateTime={article.date}>
|
||||||
|
{article.date}
|
||||||
|
</time>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
<Link
|
<Link
|
||||||
href={articleHref}
|
href={articleHref}
|
||||||
|
|||||||
@@ -1,6 +1,25 @@
|
|||||||
import React, { Suspense } from 'react';
|
import React, { Suspense } from 'react';
|
||||||
import type { Metadata, Viewport } from 'next';
|
import type { Metadata, Viewport } from 'next';
|
||||||
|
import { Lora, Inter } from 'next/font/google';
|
||||||
import '../styles/tailwind.css';
|
import '../styles/tailwind.css';
|
||||||
|
|
||||||
|
// Self-hosted at build time by next/font — no runtime CDN fetch, no FOIT,
|
||||||
|
// no GDPR concern. The two variables are consumed by --font-heading and
|
||||||
|
// --font-body in src/styles/tailwind.css with Georgia/Arial fallbacks.
|
||||||
|
const lora = Lora({
|
||||||
|
subsets: ['latin'],
|
||||||
|
weight: ['400', '500', '600', '700'],
|
||||||
|
style: ['normal', 'italic'],
|
||||||
|
variable: '--font-serif',
|
||||||
|
display: 'swap',
|
||||||
|
});
|
||||||
|
|
||||||
|
const inter = Inter({
|
||||||
|
subsets: ['latin'],
|
||||||
|
weight: ['300', '400', '500', '600', '700', '800'],
|
||||||
|
variable: '--font-sans',
|
||||||
|
display: 'swap',
|
||||||
|
});
|
||||||
// SystemStatusBar removed — the "articles indexed / events tracked / [v2.0.0]"
|
// SystemStatusBar removed — the "articles indexed / events tracked / [v2.0.0]"
|
||||||
// strip was eating vertical space without serving readers.
|
// strip was eating vertical space without serving readers.
|
||||||
import AskBBAI from '@/components/AskBBAI';
|
import AskBBAI from '@/components/AskBBAI';
|
||||||
@@ -81,7 +100,7 @@ export default function RootLayout({
|
|||||||
|
|
||||||
}: Readonly<{children: React.ReactNode;}>) {
|
}: Readonly<{children: React.ReactNode;}>) {
|
||||||
return (
|
return (
|
||||||
<html lang="en">
|
<html lang="en" className={`${lora.variable} ${inter.variable}`}>
|
||||||
<head>
|
<head>
|
||||||
{/* hreflang entries removed pending real i18n routes (Phase D). */}
|
{/* hreflang entries removed pending real i18n routes (Phase D). */}
|
||||||
|
|
||||||
|
|||||||
@@ -17,9 +17,12 @@
|
|||||||
--color-border: #2a2a2a;
|
--color-border: #2a2a2a;
|
||||||
--color-top-bar: #1a2535;
|
--color-top-bar: #1a2535;
|
||||||
--color-sub-nav: #161c28;
|
--color-sub-nav: #161c28;
|
||||||
--font-heading: Georgia, 'Times New Roman', serif;
|
/* --font-serif and --font-sans are injected on <html> by next/font/google
|
||||||
--font-body: Arial, Helvetica, sans-serif;
|
(Lora + Inter) in src/app/layout.tsx. The system-font tails are real
|
||||||
--font-mono: 'Courier New', Courier, monospace;
|
fallbacks for the brief window before the self-hosted face is ready. */
|
||||||
|
--font-heading: var(--font-serif), Georgia, 'Times New Roman', serif;
|
||||||
|
--font-body: var(--font-sans), -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif;
|
||||||
|
--font-mono: 'IBM Plex Mono', ui-monospace, 'SF Mono', Menlo, Consolas, 'Courier New', monospace;
|
||||||
--transition-fast: 0.18s ease;
|
--transition-fast: 0.18s ease;
|
||||||
--transition-base: 0.25s ease;
|
--transition-base: 0.25s ease;
|
||||||
--transition-slow: 0.4s ease;
|
--transition-slow: 0.4s ease;
|
||||||
|
|||||||
Reference in New Issue
Block a user