diff --git a/CLAUDE.md b/CLAUDE.md index bbcaf7d..0f0309d 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -49,7 +49,11 @@ All blog, portfolio, and research entries are MDX files on disk — there's no C - `Mermaid` — renders ` ```mermaid ` blocks; mermaid + stylis are transpiled and pre-bundled (see `transpilePackages` and `experimental.optimizePackageImports` in `next.config.js`). - `ContentImage` — explicit component for ImageKit-hosted images. The default `img` MDX mapping was intentionally removed so external images / SVGs in MDX don't break; use `` (or a raw ``) explicitly. - `MDXLink`, `Blockquote`, `Table`, `Headings` — styled overrides. -6. **Research-specific components** live in `src/components/content/research/`: `HeadingResearch` (status chip, title, structured authors + affiliations, venue, action-button row, hr), `ResearchTeaser` (full-bleed figure + caption, lives outside `prose` so it can breathe), `ResearchAbstract` (purple-accented `not-prose` card), `ResearchBibTeX` (anchored at `#bibtex`, copy-to-clipboard, mirrors `Pre.tsx` chrome), `ResearchItem` + `ResearchSections` (listing card + section grouping). The detail page composition is in `src/app/research/[slug]/page.tsx`. +6. **Research-specific components** live in `src/components/content/research/`: + - **Detail-page**: `HeadingResearch` (status chip, title, structured authors + affiliations, venue, action-button row, hr — author/affiliation `` markers are conditional + carry `title` tooltips, see Conventions), `ResearchTeaser` (full-bleed figure + caption, lives outside `prose` so it can breathe), `ResearchAbstract` (purple-accented `not-prose` card), `ResearchBibTeX` (anchored at `#bibtex`, copy-to-clipboard, mirrors `Pre.tsx` chrome). + - **Listing-page**: `ResearchOverview` (intro paragraph), `ResearchNews` (date-column timeline; entries live in the `NEWS` array inside the file), `ResearchAreas` (colored chip row), `ResearchItem` + `ResearchSections` (listing card + section grouping). `ComingSoonImage` is a pastel-gradient placeholder used in place of the listing thumbnail when an entry has `comingSoon: true`. + - **Shared**: `SectionHeading` is the bordered `text-lg md:text-xl font-bold text-black dark:text-white` h2 used by every section on the listing page (Overview / News / Research Areas / Sections groupers). Use it for any new sections to keep the rhythm consistent. + - The detail page composition is in `src/app/research/[slug]/page.tsx`. The listing page is in `src/app/research/page.tsx` — both pass `pt-6 md:pt-10` to `AppLayoutPage` to give the first section breathing room from the sticky header. 7. **SEO** is generated per-page in `src/app/blog/[slug]/page.tsx` and the portfolio / research equivalents: `generateMetadata` builds Next.js Metadata (OG, Twitter, canonical, `modifiedTime`), and the page itself injects a single `@graph` JSON-LD block: - Blog: `BlogPosting` + `WebPage` + `BreadcrumbList` - Portfolio: `SoftwareSourceCode` + `WebPage` + `BreadcrumbList` @@ -86,7 +90,7 @@ Two layers feed Tailwind v4: Net result: `bg-primary` (shadcn) and `bg-primary-500` (legacy) render the same color; both surfaces remain valid during the transition. When adding new code, prefer the semantic tokens. Don't write `dark:` overrides for shadcn classes — `--background` etc. switch automatically via `.dark`. -Fonts: `--font-sans` resolves to `var(--font-inter)` (Next/font Google) with the local `@font-face` Inter as a fallback. The legacy `font-primary` utility still maps to Inter via `tailwind.config.js`. +Fonts: `--font-sans` resolves to `'Google Sans', system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, 'Noto Sans', sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji'`. Google Sans is loaded via Google Fonts CSS (`` in `layout.tsx` head, with `preconnect` to `fonts.gstatic.com` — `font-src` in `next.config.js` CSP must include it). Local Inter `@font-face` stays in `globals.css` as a deeper fallback so the page never FOUCs. `tailwind.config.js` `fontFamily.primary` mirrors the same stack so the legacy `font-primary` utility picks up Google Sans too. ## Two Build Modes @@ -123,7 +127,14 @@ ISR endpoint at `GET /api/revalidate?secret=&slug=/blog/`. Secret - Commits go through `commitizen` with `cz-conventional-changelog` — run `yarn commit` for the prompt. `lint-staged` runs Prettier on staged `js/jsx/ts/tsx/mdx/md/css` files. - Frontmatter `published` for blog and research is `MM/DD/YYYY` (portfolio uses `date`). `featured: true` surfaces an entry on the home page. Optional `updated: "MM/DD/YYYY"` overrides `published` as the freshness signal — it flows into `dateModified` (BlogPosting / ScholarlyArticle JSON-LD), ``, and the sitemap `lastmod`. Set it whenever you make a substantive content edit; leave it off for typo fixes. - Authoring convention: open every blog post with a 60–120 word TL;DR paragraph immediately under the H1. LLM crawlers (ChatGPT, Perplexity, AI Overviews) draw ~44% of their citations from the first 30% of an article, so front-loading the answer compounds visibility. -- **Research authoring**: use the YAML folded scalar (`abstract: >-`) — block scalar (`|`) preserves newlines and the abstract renders with broken lines. Authors use a structured array; `affiliations` indices are 1-based and reference the entry's `affiliations` array. `section` drives listing-page grouping (`top-tier` / `conferences` / `journals` / `workshops` / `others`). `new: true` renders a NEW badge inline with the title on the listing card. Authors matching `SITE_AUTHOR.name` are auto-bolded across `HeadingResearch` and `ResearchItem`. +- **Research authoring**: use the YAML folded scalar (`abstract: >-`) — block scalar (`|`) preserves newlines and the abstract renders with broken lines. Authors use a structured array; `affiliations` indices are 1-based and reference the entry's `affiliations` array. `section` drives listing-page grouping (`top-tier` / `conferences` / `journals` / `workshops` / `others`). `new: true` renders a NEW badge inline with the title on the listing card. `comingSoon: true` swaps the listing thumbnail for the `ComingSoonImage` pastel placeholder (no thumbnail/teaser required). Authors matching `SITE_AUTHOR.name` are auto-bolded across `HeadingResearch` and `ResearchItem`. +- **Author / affiliation superscripts** in `HeadingResearch` are conditional. Each marker only renders when it actually disambiguates something: + - `*` (corresponding author) — `corresponding: true` on the author. Shows when **2+ authors** AND at least one is corresponding. + - `†` (equal contribution) — `equalContribution: true` on the author. Shows when **2+ authors** AND at least 2 are flagged. Renders on every flagged author. + - `‡` (principal investigator) — `principalInvestigator: true` on the author. Shows when **2+ authors** AND at least one is PI. + - `¹ ² ³` (affiliation indices) — derived from `affiliations: [...]` on each author. Shows when **2+ affiliations** exist on the entry. + + Marker order on each author follows academic convention: `*†‡` then numeric indices. The affiliation legend below the author line drops its leading `` under the same `showAffSup` rule. Single-author / single-affiliation entries collapse to clean text — no orphan markers. Each `` has `cursor-help` + a native `title` tooltip (affiliation index sups resolve to the full affiliation name; symbol sups resolve to their caption text). A combined caption line appears below the affiliation row when any symbol marker is shown — e.g. `*Corresponding author · †Equal contribution · ‡Principal investigator` — joined by ` · `. ## Indexing Helper diff --git a/README.md b/README.md index 036ed42..4f58f97 100644 --- a/README.md +++ b/README.md @@ -70,6 +70,14 @@ Use it as your portfolio. Fork it as a template. Read the source as a reference - **Topic archive pages** auto-generated from frontmatter `topics`. - **RSS** at `/rss.xml` (summaries) and `/rss-full.xml` (full content) with `` discovery in ``. +### Design system + +- **shadcn/ui** (radix-nova style) as the single primitive layer. Components live under `src/components/ui/` and are managed by the shadcn CLI (`npx shadcn@latest add `); rerun with `--diff` to merge upstream updates without losing local edits. +- **Theme bridge** in `src/styles/globals.css` `@theme inline` — shadcn semantic tokens (`--primary`, `--background`, `--foreground`, `--muted`, `--border`, `--ring`) map onto the legacy blue-primary / neutral-theme palette in OKLCH so legacy classes (`bg-primary-500`, `text-theme-700`) and shadcn classes (`bg-primary`, `text-foreground`) render identically across light/dark. +- **Site composition components** under `src/components/site/` (Header, Footer, Nav, Hero, Searchbar, BackToTop, MobileNav, ThemeMenu, link/image wrappers, AppLayoutPage) — bespoke pieces that compose shadcn primitives + project state. +- **Single primitive ecosystem** — `radix-ui` (shadcn's base), `lucide-react` (icons inside shadcn), `sonner` (toasts), `tw-animate-css` (animations), `class-variance-authority` (variants), `tailwind-merge` (className merging). +- **Typography** — Google Sans loaded from Google Fonts with a system-font fallback chain (`system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, …`). Local Inter `@font-face` is kept as a deeper fallback so the page never FOUCs. + ### Performance - **Core Web Vitals attribution** for CLS, LCP, **INP**, FCP, TTFB via Vercel Speed Insights. @@ -80,13 +88,15 @@ Use it as your portfolio. Fork it as a template. Read the source as a reference ### Tooling -- **TypeScript** strict-mode, `@/*` path aliases. +- **TypeScript** strict-mode, `@/*` path alias. - **ESLint flat config** (`eslint.config.mjs`, replacing the deprecated `next lint`). -- **Vitest** suite (48 tests, ~1.7s) covering schema generators, sorters, content readers. +- **Prettier** with `@ianvs/prettier-plugin-sort-imports` (actively maintained successor to `@trivago`) + `prettier-plugin-tailwindcss`. +- **shadcn CLI** for primitive sync — `npx shadcn@latest add`, `--diff`, `--dry-run`. +- **Vitest** suite (48 tests, ~1.7s) covering schema generators, sorters, content readers. Vite resolves `@/*` paths via the native `resolve.tsconfigPaths: true` (no plugin). - **JSON-LD validator** (`yarn validate:json-ld`) walks the build output and verifies every `