Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,12 @@ Animata is a free, open-source library of animated React components built with N
- Co-located `<name>.css` imported from the TSX for keyframes, pseudo-elements, and selectors Tailwind cannot express (see `roll-text.tsx`, `metis-text.tsx`)
- Inline `<style>` blocks are fine for small self-contained keyframes when a separate file would be overkill (see `marquee.tsx`)
- No CSS modules, no styled-components
- Fonts: `--font-display` = Young Serif (headings/display), `--font-sans` = IBM Plex Sans (body), `--font-mono` = Lilex
- Fonts: `--font-display` = Instrument Sans (headings/display), `--font-sans` = IBM Plex Sans (body), `--font-mono` = Lilex
- Brand yellow: `#ffcc00` (from logo) — use for highlights and badges
- shadcn registry URL format: `https://animata.design/r/{category}/{name}.json` — always use this in docs/changelog, never bare `npx shadcn add component-name`
- Theme accent: `hsl(var(--accent))` = purple/violet
- All new components must be theme-responsive (light + dark)
- **New component category?** Add a category glyph for `/components` — read `content/docs/contributing/category-glyphs.mdx` (three-shade tokens, stroke tiers, `cg-motion` hover). Full repo spec: `content/docs/contributing/category-glyphs-spec.md`. Do not put spec markdown under `animata/` (webpack lazy-loads that tree).

## Changelog rule — ALWAYS update this

Expand Down Expand Up @@ -73,6 +74,10 @@ Add to the relevant `YYYY-MM.mdx` file. No need to touch the index unless the su

```text
animata/ # Component source (copy-paste friendly)
skeleton/
category-glyphs.tsx # /components tile SVG pictograms (GLYPHS map)
category-glyphs.css # cg-motion base + keyframes (hover transforms live in TSX)
category-skeleton.tsx # Tile frame + shade tokens
container/ # Layout wrappers (marquee, dock, ribbon…)
text/ # Text animation effects
button/ # Button variants
Expand All @@ -89,7 +94,9 @@ content/docs/
changelog/
index.mdx # overview + table of all months — ← KEEP THIS UPDATED
YYYY-MM.mdx # one file per month (e.g. 2026-04.mdx)
contributing/ # Contributor guides
contributing/
category-glyphs.mdx # Category tile SVG guide (published)
category-glyphs-spec.md # Full spec — keep in sync; not under animata/
config/
docs.ts # Sidebar nav config — register new component categories here
styles/globals.css # Tailwind v4 theme tokens
Expand Down
9 changes: 9 additions & 0 deletions app/(main)/components/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { DocsShellLayout } from "@/components/docs-shell-layout";

interface ComponentsLayoutProps {
children: React.ReactNode;
}

export default function ComponentsLayout({ children }: ComponentsLayoutProps) {
return <DocsShellLayout>{children}</DocsShellLayout>;
}
58 changes: 27 additions & 31 deletions app/(main)/components/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,37 +16,33 @@ export default function ComponentsPage() {
const categories = getComponentCategories();

return (
<div className="bg-background text-foreground">
<div className="mx-auto max-w-7xl px-4 pb-24 pt-16 sm:px-6 sm:pt-20 lg:px-8">
<header className="max-w-3xl">
<h1 className="font-(family-name:--font-display) text-[clamp(2.5rem,6vw,4rem)] leading-[1.05] tracking-tight">
Components
</h1>
<p className="mt-4 text-[clamp(1rem,2.2vw,1.25rem)] leading-relaxed text-muted-foreground text-balance">
Everything in Animata lives here, sorted by type. Open a category for live previews and
copy-paste docs.
</p>
</header>
<main id="main-content" className="relative min-w-0 py-6 lg:py-8">
<header className="max-w-3xl">
<h1 className="scroll-m-20 text-4xl font-bold tracking-tight">Components</h1>
<p className="mt-4 text-balance text-muted-foreground">
Everything in Animata lives here, sorted by type. Open a category for live previews and
copy-paste docs.
</p>
</header>

<ul className="mt-14 grid list-none grid-cols-2 gap-x-4 gap-y-8 p-0 sm:gap-x-6 sm:gap-y-10 md:grid-cols-3 lg:grid-cols-4">
{categories.map((category) => (
<li key={category.slug}>
<Link href={category.href} className="group group/cg block">
<CategorySkeleton variant={category.slug} />
<p className="mt-3 px-2 text-[1.2rem] leading-tight tracking-tight">
<span className="font-semibold text-foreground transition-colors duration-300 group-hover:text-foreground/40">
{category.title}
</span>
<span className="text-foreground/35"> · </span>
<span className="font-normal text-foreground/40 transition-colors duration-300 group-hover:font-semibold group-hover:text-foreground">
{category.count}
</span>
</p>
</Link>
</li>
))}
</ul>
</div>
</div>
<ul className="mt-14 grid list-none grid-cols-2 gap-x-4 gap-y-8 p-0 sm:gap-x-6 sm:gap-y-10 lg:grid-cols-3">
{categories.map((category) => (
<li key={category.slug} className="min-w-0">
<Link href={category.href} className="group group/cg block">
<CategorySkeleton variant={category.slug} />
<p className="mt-3 px-2 text-base leading-snug tracking-tight sm:text-[1.2rem]">
<span className="font-semibold text-foreground transition-colors duration-300 group-hover:text-foreground/40">
{category.title}
</span>
<span className="text-foreground/35"> · </span>
<span className="font-normal text-foreground/40 transition-colors duration-300 group-hover:font-semibold group-hover:text-foreground">
{category.count}
</span>
</p>
</Link>
</li>
))}
</ul>
</main>
);
}
2 changes: 1 addition & 1 deletion app/(main)/docs/[[...slug]]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ export default async function DocPage({ params }: DocPageProps) {
return (
<main id="main-content" className="relative py-6 lg:gap-10 lg:py-8 xl:grid">
<DocJsonLd doc={doc} />
<div className="mx-auto w-full min-w-0">
<div className="docs-content mx-auto w-full min-w-0">
<div className="mb-4 flex items-center space-x-1 text-sm text-muted-foreground">
<div className="overflow-hidden text-ellipsis whitespace-nowrap">Docs</div>
<ChevronRightIcon className="h-4 w-4" />
Expand Down
17 changes: 2 additions & 15 deletions app/(main)/docs/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,9 @@
import { DocsSidebarNav } from "@/components/sidebar-nav";
import { docsConfig } from "@/config/docs";

import "@fontsource-variable/instrument-sans";
import "@/styles/docs.css";
import { DocsShellLayout } from "@/components/docs-shell-layout";

interface DocsLayoutProps {
children: React.ReactNode;
}

export default function DocsLayout({ children }: DocsLayoutProps) {
return (
<div className="docs-shell border-b border-border">
<div className="mx-auto w-full max-w-7xl flex-1 items-start px-4 sm:px-6 md:grid md:grid-cols-[220px_minmax(0,1fr)] md:gap-4 lg:grid-cols-[240px_minmax(0,1fr)] lg:gap-6">
<aside className="sticky hidden md:block top-(--site-header-height) h-[calc(100svh-var(--site-header-height))] w-full max-w-64 shrink-0 self-start overflow-hidden pt-6 pb-6 lg:pt-8">
<DocsSidebarNav items={docsConfig.sidebarNav} className="h-full" />
</aside>
{children}
</div>
</div>
);
return <DocsShellLayout>{children}</DocsShellLayout>;
}
11 changes: 3 additions & 8 deletions app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { Metadata, Viewport } from "next";
import { IBM_Plex_Sans, Young_Serif } from "next/font/google";
import { IBM_Plex_Sans } from "next/font/google";
import { NuqsAdapter } from "nuqs/adapters/next/app";

import { ThemeProvider } from "@/components/providers";
Expand All @@ -8,6 +8,7 @@ import { brandFont } from "@/lib/brand-font";
import { cn } from "@/lib/utils";

import "@fontsource-variable/lilex";
import "@/styles/fonts.css";
import "@/styles/globals.css";

const ibmPlex = IBM_Plex_Sans({
Expand All @@ -16,12 +17,6 @@ const ibmPlex = IBM_Plex_Sans({
variable: "--font-sans",
});

const youngSerif = Young_Serif({
subsets: ["latin"],
weight: ["400"],
variable: "--font-display",
});

export const metadata: Metadata = {
title: {
default: `Free & Open Source Animated ReactJS Components | ${siteConfig.name}`,
Expand Down Expand Up @@ -93,7 +88,7 @@ export default function RootLayout({ children }: RootLayoutProps) {
<html
lang="en"
suppressHydrationWarning
className={`${ibmPlex.variable} ${youngSerif.variable} ${brandFont.variable}`}
className={`${ibmPlex.variable} ${brandFont.variable}`}
>
<head />
<body className={cn("min-h-screen bg-background antialiased", ibmPlex.className)}>
Expand Down
21 changes: 21 additions & 0 deletions components/docs-shell-layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { DocsSidebarNav } from "@/components/sidebar-nav";
import { docsConfig } from "@/config/docs";

import "@/styles/docs.css";

interface DocsShellLayoutProps {
children: React.ReactNode;
}

export function DocsShellLayout({ children }: DocsShellLayoutProps) {
return (
<div className="docs-shell border-b border-border">
<div className="mx-auto w-full max-w-7xl flex-1 items-start px-4 sm:px-6 md:grid md:grid-cols-[220px_minmax(0,1fr)] md:gap-4 lg:grid-cols-[240px_minmax(0,1fr)] lg:gap-6">
<aside className="sticky top-(--site-header-height) hidden h-[calc(100svh-var(--site-header-height))] w-full max-w-64 shrink-0 self-start overflow-hidden pt-6 pb-6 md:block lg:pt-8">
<DocsSidebarNav items={docsConfig.sidebarNav} className="h-full" />
</aside>
{children}
</div>
</div>
);
}
2 changes: 1 addition & 1 deletion components/sidebar-nav.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,7 @@ export function DocsSidebarNav({ items, variant = "docs", className }: DocsSideb
) : null}

{item.title === "Contributing" ? (
<div className="mt-2 mb-1 pl-4 text-xs font-semibold text-muted-foreground uppercase">
<div className="mt-2 mb-1 pl-5.5 text-xs font-semibold text-muted-foreground uppercase">
Components
</div>
) : null}
Expand Down
1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,6 @@
"@commitlint/config-conventional": "^19.2.2",
"@fontsource/ibm-plex-sans": "^5.2.8",
"@fontsource/outfit": "^5.2.8",
"@fontsource/young-serif": "^5.2.7",
"@mdx-js/mdx": "^3.1.1",
"@storybook/addon-docs": "^10.2.15",
"@storybook/addon-themes": "^10.2.15",
Expand Down
8 changes: 0 additions & 8 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Binary file not shown.
Binary file removed public/og-fonts/young-serif-latin-400-normal.woff2
Binary file not shown.
10 changes: 5 additions & 5 deletions scripts/lib/og-template.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@
// or "side" (tall demo left fills height, branding right — TALL/square components).
// variant: "a" warm editorial (smoke glow + serif title glow)
// or "b" textured minimal (vertical hairlines, crisp serif).
// Brand type: Young Serif (display title), Outfit (wordmark/eyebrow), IBM Plex Sans (body) — self-hosted
// Brand type: Instrument Sans (display title), Outfit (wordmark/eyebrow), IBM Plex Sans (body) — self-hosted
// under public/og-fonts/ (see scripts/sync-og-fonts.mjs) so renders are offline-deterministic.
//
// Bump TEMPLATE_VERSION when the design changes — it's part of the cache hash.
export const TEMPLATE_VERSION = "24";
export const TEMPLATE_VERSION = "25";

const esc = (s) =>
String(s ?? "").replace(
Expand Down Expand Up @@ -43,7 +43,7 @@ export function ogFontsCss(fontBaseUrl) {
face("Outfit", "outfit-latin-500-normal.woff2", 500),
face("Outfit", "outfit-latin-600-normal.woff2", 600),
face("Outfit", "outfit-latin-700-normal.woff2", 700),
face("Young Serif", "young-serif-latin-400-normal.woff2", 400),
face("Instrument Sans Variable", "instrument-sans-latin-wght-normal.woff2", "400 700"),
face("IBM Plex Sans", "ibm-plex-sans-latin-400-normal.woff2", 400),
face("IBM Plex Sans", "ibm-plex-sans-latin-500-normal.woff2", 500),
face("IBM Plex Sans", "ibm-plex-sans-latin-600-normal.woff2", 600),
Expand All @@ -66,7 +66,7 @@ const COMMON = `
.accent { width: 52px; height: 5px; border-radius: 3px; background: #ffcc00; }
.accent-row { display: flex; align-items: center; gap: 14px; }
.accent-row .eyebrow { line-height: 1; }
.title { font-family: "Young Serif", Georgia, serif; font-weight: 400; letter-spacing: -0.005em; line-height: 1.03; }
.title { font-family: "Instrument Sans Variable", ui-sans-serif, system-ui, sans-serif; font-weight: 600; letter-spacing: -0.02em; line-height: 1.03; }
.desc { font-family: "IBM Plex Sans", -apple-system, sans-serif; font-size: 22px; line-height: 1.45; color: #6b665c; max-width: 760px; margin-top: 16px;
display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.chips { display: flex; gap: 9px; flex-wrap: wrap; }
Expand Down Expand Up @@ -216,7 +216,7 @@ const BRANDED_BASE = `
.word { font-family:"Outfit", sans-serif; font-size:34px; font-weight:600; letter-spacing:-0.01em; line-height:1; }
.eyebrow { font-family:"Outfit", sans-serif; font-size:21px; font-weight:600; letter-spacing:0.18em; text-transform:uppercase; color:#8c877d; }
.accent { width:64px; height:6px; border-radius:3px; background:#ffcc00; }
.title { font-family:"Young Serif", Georgia, serif; font-weight:400; letter-spacing:-0.01em; line-height:1.0; color:#16140f; }
.title { font-family:"Instrument Sans Variable", ui-sans-serif, system-ui, sans-serif; font-weight:600; letter-spacing:-0.02em; line-height:1.0; color:#16140f; }
.desc { font-size:26px; line-height:1.45; color:#6b665c; max-width:880px;
display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical; overflow:hidden; }
.url { font-family:"IBM Plex Sans", sans-serif; font-size:21px; font-weight:500; letter-spacing:0.01em; color:#8c877d; }
Expand Down
4 changes: 2 additions & 2 deletions scripts/sync-og-fonts.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ const FILES = [
["@fontsource/outfit/files/outfit-latin-600-normal.woff2", "outfit-latin-600-normal.woff2"],
["@fontsource/outfit/files/outfit-latin-700-normal.woff2", "outfit-latin-700-normal.woff2"],
[
"@fontsource/young-serif/files/young-serif-latin-400-normal.woff2",
"young-serif-latin-400-normal.woff2",
"@fontsource-variable/instrument-sans/files/instrument-sans-latin-wght-normal.woff2",
"instrument-sans-latin-wght-normal.woff2",
],
[
"@fontsource/ibm-plex-sans/files/ibm-plex-sans-latin-400-normal.woff2",
Expand Down
13 changes: 6 additions & 7 deletions styles/docs.css
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
@reference "./globals.css";

.docs-shell {
--font-docs:
"Instrument Sans Variable", ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji",
"Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";

font-family: var(--font-docs);
font-feature-settings: "ss02", "ss09";
font-family: var(--font-display);
font-optical-sizing: auto;
}

.docs-shell .font-heading {
font-family: var(--font-docs);
font-family: var(--font-display);
}

.docs-content {
font-feature-settings: "ss02", "ss09";
}

.docs-sidebar-scroller {
Expand Down
7 changes: 7 additions & 0 deletions styles/fonts.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
@import "@fontsource-variable/instrument-sans";

:root {
--font-display:
"Instrument Sans Variable", ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji",
"Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
}