diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 801288b..533e772 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -1,6 +1,6 @@
-# Contributing to Trophy UI
+# Contributing to Gamification UI Kit by Trophy
-Thank you for helping improve [Trophy UI](https://ui.trophy.so). This document explains how to set up the repo locally, run the site and tooling, and what we look for in contributions.
+Thank you for helping improve [Gamification UI Kit by Trophy](https://ui.trophy.so). This document explains how to set up the repo locally, run the site and tooling, and what we look for in contributions.
## Prerequisites
diff --git a/README.md b/README.md
index 280e993..e72819e 100644
--- a/README.md
+++ b/README.md
@@ -1,12 +1,12 @@
-
+
-# Trophy UI
+# Gamification UI Kit by Trophy
-[Trophy UI](https://ui.trophy.so) is a component library built on top of [shadcn/ui](https://ui.shadcn.com/) to help you build gamification experiences faster.
+[Gamification UI Kit by Trophy](https://ui.trophy.so) is a component library built on top of [shadcn/ui](https://ui.shadcn.com/) to help you build gamification experiences faster.
## Overview
-Trophy UI provides pre-built, customizable React components for gamification—streaks, achievements, leaderboards, points, XP charts, event banners, and more. The [shadcn/ui](https://ui.shadcn.com/) CLI pulls components straight from the registry so you can own the code and tailor it to your product.
+Trophy's Gamification UI Kit provides pre-built, customizable React components for gamification—streaks, achievements, leaderboards, points, XP charts, event banners, and more. The [shadcn/ui](https://ui.shadcn.com/) CLI pulls components straight from the registry so you can own the code and tailor it to your product.
## Installation
@@ -18,7 +18,7 @@ npx shadcn@latest add https://ui.trophy.so/streak-badge
## Prerequisites
-Before using Trophy UI, ensure your project meets these requirements:
+Before using Trophy's Gamification UI Kit, ensure your project meets these requirements:
- **Node.js 18** or later
- **React 18+** — components are client-side React
@@ -35,7 +35,7 @@ Install every registry component at once:
npx shadcn@latest add https://ui.trophy.so/all
```
-This adds Trophy UI components (and any shared primitives they depend on) into your configured components directory.
+This adds components (and any shared primitives they depend on) into your configured components directory.
### Install specific components
@@ -65,7 +65,7 @@ Browse the full set on the docs site: [Components](https://ui.trophy.so/docs/com
## Contributing
-If you would like to contribute to Trophy UI:
+If you would like to contribute to Gamification UI Kit by Trophy:
1. Fork the repository
2. Create a branch for your change
diff --git a/apps/www/.source/source.config.mjs b/apps/www/.source/source.config.mjs
index 21c5284..0c9bb47 100644
--- a/apps/www/.source/source.config.mjs
+++ b/apps/www/.source/source.config.mjs
@@ -1,7 +1,15 @@
// source.config.ts
-import { defineConfig, defineDocs } from "fumadocs-mdx/config";
+import { defineConfig, defineDocs, frontmatterSchema } from "fumadocs-mdx/config";
+import { z } from "zod";
+var docsPageSchema = frontmatterSchema.extend({
+ seoTitle: z.string().optional(),
+ keywords: z.array(z.string()).optional()
+});
var docs = defineDocs({
- dir: "content/docs"
+ dir: "content/docs",
+ docs: {
+ schema: docsPageSchema
+ }
});
var source_config_default = defineConfig({
mdxOptions: {
diff --git a/apps/www/app/(app)/(root)/page.tsx b/apps/www/app/(app)/(root)/page.tsx
index 6ecbada..744c51a 100644
--- a/apps/www/app/(app)/(root)/page.tsx
+++ b/apps/www/app/(app)/(root)/page.tsx
@@ -10,6 +10,7 @@ import {
PageHeaderHeading,
} from "@/components/page-header"
import { Button } from "@/registry/trophy/ui/button"
+import { TextAnimate } from "@/components/ui/text-animate"
export const dynamic = "force-static"
export const revalidate = false
@@ -17,11 +18,12 @@ export const revalidate = false
export const metadata: Metadata = {
title: {
template: `%s | ${siteConfig.title}`,
- default: `${siteConfig.tagline} | ${siteConfig.title}`,
+ default: siteConfig.title,
},
description: siteConfig.description,
+ keywords: siteConfig.keywords,
openGraph: {
- title: `${siteConfig.tagline} | ${siteConfig.title}`,
+ title: siteConfig.title,
description: siteConfig.description,
url: siteConfig.url,
siteName: siteConfig.title,
@@ -29,12 +31,99 @@ export const metadata: Metadata = {
locale: "en_US",
type: "website",
},
+ twitter: {
+ card: "summary_large_image",
+ title: siteConfig.title,
+ description: siteConfig.description,
+ images: [siteConfig.ogImage],
+ },
+ alternates: {
+ canonical: siteConfig.url,
+ },
metadataBase: new URL(siteConfig.url),
}
+const jsonLd = {
+ "@context": "https://schema.org",
+ "@type": "SoftwareApplication",
+ name: "Gamification UI Kit by Trophy",
+ applicationCategory: "DeveloperApplication",
+ operatingSystem: "Any",
+ description: siteConfig.description,
+ url: siteConfig.url,
+ keywords:
+ "gamification UI kit, gamification component library, open source gamification UI, free gamification components, React gamification, Next.js gamification, shadcn gamification, shadcn registry, Tailwind gamification, streak component, achievement component, leaderboard component, points component",
+ featureList: [
+ "Streak tracking components",
+ "Achievement badges and cards",
+ "Leaderboard rankings and podiums",
+ "Points displays and level systems",
+ "Built on shadcn/ui and Tailwind CSS for React and Next.js",
+ "Install with the shadcn CLI from this open registry",
+ "Fully customizable and open source",
+ ],
+ softwareHelp: {
+ "@type": "CreativeWork",
+ url: `${siteConfig.url}/docs`,
+ },
+ author: {
+ "@type": "Organization",
+ name: "Trophy",
+ url: "https://trophy.so",
+ },
+}
+
+const categories = [
+ {
+ title: "Streak Components",
+ description:
+ "Keep users coming back with streak tracking UI. Display daily streaks, streak calendars, and streak badges that motivate consistent engagement.",
+ href: "/docs/components/streak-card",
+ components: ["Streak Card", "Streak Calendar", "Streak Badge"],
+ },
+ {
+ title: "Achievement Components",
+ description:
+ "Celebrate milestones with achievement badges, unlock animations, and achievement grids. Give users a sense of progress and accomplishment.",
+ href: "/docs/components/achievement-badge",
+ components: [
+ "Achievement Badge",
+ "Achievement Card",
+ "Achievement Grid",
+ "Achievement List",
+ "Achievement Unlocked",
+ ],
+ },
+ {
+ title: "Leaderboard Components",
+ description:
+ "Drive competition with leaderboard rankings, podium displays, and leaderboard cards. Show users where they stand among peers.",
+ href: "/docs/components/leaderboard-rankings",
+ components: ["Leaderboard Rankings", "Leaderboard Podium", "Leaderboard Card"],
+ },
+ {
+ title: "Points & Levels Components",
+ description:
+ "Reward actions with points badges, level timelines, points charts, and boost indicators. Build complete progression systems.",
+ href: "/docs/components/points-badge",
+ components: [
+ "Points Badge",
+ "Points Awards",
+ "Points Boost",
+ "Points Chart",
+ "Points Levels List",
+ "Points Levels Timeline",
+ ],
+ },
+]
+
export default function IndexPage() {
return (
+
-
- Trophy
-
-
- UI
-
+ {/*
+ Gamification UI Kit
+ */}
+
+ Gamification UI Kit by Trophy
+
+ {/*
+ by Trophy
+ */}
@@ -68,6 +160,92 @@ export default function IndexPage() {
+
+
+
+ Gamification UI Components for React
+
+
+ Trophy's Gamification UI Kit gives you production-ready
+ gamification components — streak trackers, achievement badges,
+ leaderboards, points displays, and more. Built on{" "}
+
+ shadcn/ui
+ {" "}
+ and Tailwind CSS, every component is open source, fully
+ customizable, and installs with a single CLI command into any React
+ or Next.js project.
+
+
+
+
+
+
+ Everything You Need to Ship Gamification
+
+
+ Four categories of components covering the most impactful
+ gamification patterns — all composable, accessible, and
+ theme-aware.
+
+
+ {categories.map((category) => (
+
+
+ {category.title}
+
+
+ {category.description}
+
+
+ {category.components.map((component) => (
+
+ {component}
+
+ ))}
+
+
+ ))}
+
+
+
+
+
+
+ Built for Developers
+
+
+
+
One-Command Install
+
+ Add any component to your project with a single CLI command.
+ No extra dependencies to manage.
+
+
+
+
Fully Customizable
+
+ Every component lives in your codebase. Tweak styles, layout,
+ and behavior to match your brand.
+
+
+
+
Open Source
+
+ MIT licensed and community-driven. Use in personal projects or
+ production apps without restrictions.
+
+
+
+
+
)
}
diff --git a/apps/www/app/(app)/docs/[[...slug]]/page.tsx b/apps/www/app/(app)/docs/[[...slug]]/page.tsx
index a580ded..f422d19 100644
--- a/apps/www/app/(app)/docs/[[...slug]]/page.tsx
+++ b/apps/www/app/(app)/docs/[[...slug]]/page.tsx
@@ -5,6 +5,11 @@ import { findNeighbour } from "fumadocs-core/server"
import { ChevronLeft, ChevronRight, CircleAlert, GitFork } from "lucide-react"
import { siteConfig } from "@/lib/config"
+import {
+ resolveDocsMetaKeywords,
+ resolveDocsSeoTitle,
+ type TrophyDocsFrontmatter,
+} from "@/lib/docs-metadata"
import { source } from "@/lib/source"
import { absoluteUrl } from "@/lib/utils"
import { DocsCopyPage } from "@/components/docs-copy-page"
@@ -30,26 +35,38 @@ export async function generateMetadata(props: {
notFound()
}
- const doc = page.data
+ const doc = page.data as typeof page.data & TrophyDocsFrontmatter
if (!doc.title || !doc.description) {
notFound()
}
+ const metaTitle = resolveDocsSeoTitle(doc)
+ const metaKeywords = resolveDocsMetaKeywords(doc)
+
return {
- title: doc.title,
+ title: metaTitle,
description: doc.description,
+ keywords: metaKeywords,
+ alternates: {
+ canonical: `${siteConfig.url}${page.url}`,
+ },
openGraph: {
- title: doc.title,
+ title: metaTitle,
description: doc.description,
type: "article",
url: `${siteConfig.url}${page.url}`,
images: [
{
- url: `/og?title=${encodeURIComponent(doc.title)}&description=${encodeURIComponent(doc.description)}`,
+ url: `/og?title=${encodeURIComponent(metaTitle)}&description=${encodeURIComponent(doc.description)}`,
},
],
},
+ twitter: {
+ card: "summary_large_image",
+ title: metaTitle,
+ description: doc.description,
+ },
}
}
diff --git a/apps/www/app/(app)/layout.tsx b/apps/www/app/(app)/layout.tsx
index e7b67cf..633e90d 100644
--- a/apps/www/app/(app)/layout.tsx
+++ b/apps/www/app/(app)/layout.tsx
@@ -1,6 +1,13 @@
+import type { Metadata } from "next"
+
+import { siteConfig } from "@/lib/config"
import { SiteFooter } from "@/components/site-footer"
import { SiteHeader } from "@/components/site-header"
+export const metadata: Metadata = {
+ metadataBase: new URL(siteConfig.url),
+}
+
export default function AppLayout({ children }: { children: React.ReactNode }) {
return (
diff --git a/apps/www/app/layout.tsx b/apps/www/app/layout.tsx
index 9d976da..e574483 100644
--- a/apps/www/app/layout.tsx
+++ b/apps/www/app/layout.tsx
@@ -31,7 +31,7 @@ export default function RootLayout({
}}
/>
-
+
({
+ url: `${siteConfig.url}${page.url}`,
+ lastModified: new Date(),
+ changeFrequency: "weekly",
+ priority: page.url.includes("/components/") ? 0.9 : 0.7,
+ }))
+
+ return [
+ {
+ url: siteConfig.url,
+ lastModified: new Date(),
+ changeFrequency: "weekly",
+ priority: 1.0,
+ },
+ ...docEntries,
+ ]
+}
diff --git a/apps/www/components/docs-copy-page.tsx b/apps/www/components/docs-copy-page.tsx
index f8b4ac1..c159e7d 100644
--- a/apps/www/components/docs-copy-page.tsx
+++ b/apps/www/components/docs-copy-page.tsx
@@ -20,7 +20,7 @@ import { Separator } from "@/registry/trophy/ui/separator"
function getPromptUrl(baseURL: string, url: string) {
return `${baseURL}?q=${encodeURIComponent(
- `I'm looking at this Trophy UI documentation: ${url}.
+ `I'm looking at this Gamification UI Kit by Trophy documentation: ${url}.
Help me understand how to use it. Be ready to explain concepts, give examples, or help debug based on it.
`
)}`
diff --git a/apps/www/components/home-component-mosaic.tsx b/apps/www/components/home-component-mosaic.tsx
index 1029cf7..b6d251b 100644
--- a/apps/www/components/home-component-mosaic.tsx
+++ b/apps/www/components/home-component-mosaic.tsx
@@ -1,7 +1,6 @@
"use client"
import { useState } from "react"
-import * as DialogPrimitive from "@radix-ui/react-dialog"
import { AchievementGrid } from "@/registry/trophy/ui/achievement-grid"
import { AchievementList } from "@/registry/trophy/ui/achievement-list"
@@ -15,6 +14,7 @@ import { PointsBoost } from "@/registry/trophy/ui/points-boost"
import { PointsChart } from "@/registry/trophy/ui/points-chart"
import { PointsLevelsList } from "@/registry/trophy/ui/points-levels-list"
import { StreakBadge } from "@/registry/trophy/ui/streak-badge"
+import { BlurFade } from "./ui/blur-fade"
const chartData7Days = (() => {
// 1 data point per day for previous 7 days, always increasing by highly variable random increments
@@ -448,24 +448,33 @@ export function HomeComponentMosaic() {
-
+
-
+
-
+
-
+
-
+
+
+
-
+
-
+
setAchievementModalOpen(true)}
@@ -537,8 +557,11 @@ export function HomeComponentMosaic() {
>
Unlock achievement
-
-
+
+
-
-
+
diff --git a/apps/www/components/ui/blur-fade.tsx b/apps/www/components/ui/blur-fade.tsx
new file mode 100644
index 0000000..4dd7ff0
--- /dev/null
+++ b/apps/www/components/ui/blur-fade.tsx
@@ -0,0 +1,94 @@
+"use client"
+
+import { useRef } from "react"
+import {
+ AnimatePresence,
+ motion,
+ useInView,
+ type MotionProps,
+ type UseInViewOptions,
+ type Variants,
+} from "motion/react"
+
+type MarginType = UseInViewOptions["margin"]
+
+interface BlurFadeProps extends MotionProps {
+ children: React.ReactNode
+ className?: string
+ variant?: {
+ hidden: { y: number }
+ visible: { y: number }
+ }
+ duration?: number
+ delay?: number
+ offset?: number
+ direction?: "up" | "down" | "left" | "right"
+ inView?: boolean
+ inViewMargin?: MarginType
+ blur?: string
+}
+
+const getFilter = (v: Variants[string]) =>
+ typeof v === "function" ? undefined : v.filter
+
+export function BlurFade({
+ children,
+ className,
+ variant,
+ duration = 0.4,
+ delay = 0,
+ offset = 6,
+ direction = "down",
+ inView = false,
+ inViewMargin = "-50px",
+ blur = "6px",
+ ...props
+}: BlurFadeProps) {
+ const ref = useRef(null)
+ const inViewResult = useInView(ref, { once: true, margin: inViewMargin })
+ const isInView = !inView || inViewResult
+ const defaultVariants: Variants = {
+ hidden: {
+ [direction === "left" || direction === "right" ? "x" : "y"]:
+ direction === "right" || direction === "down" ? -offset : offset,
+ opacity: 0,
+ filter: `blur(${blur})`,
+ },
+ visible: {
+ [direction === "left" || direction === "right" ? "x" : "y"]: 0,
+ opacity: 1,
+ filter: `blur(0px)`,
+ },
+ }
+ const combinedVariants = variant ?? defaultVariants
+
+ const hiddenFilter = getFilter(combinedVariants.hidden)
+ const visibleFilter = getFilter(combinedVariants.visible)
+
+ const shouldTransitionFilter =
+ hiddenFilter != null &&
+ visibleFilter != null &&
+ hiddenFilter !== visibleFilter
+
+ return (
+
+
+ {children}
+
+
+ )
+}
diff --git a/apps/www/components/ui/text-animate.tsx b/apps/www/components/ui/text-animate.tsx
new file mode 100644
index 0000000..46f4bfc
--- /dev/null
+++ b/apps/www/components/ui/text-animate.tsx
@@ -0,0 +1,445 @@
+"use client"
+
+import { memo } from "react"
+import {
+ AnimatePresence,
+ motion,
+ Variants,
+ type DOMMotionComponents,
+ type MotionProps,
+} from "motion/react"
+
+import { cn } from "@/lib/utils"
+
+type AnimationType = "text" | "word" | "character" | "line"
+type AnimationVariant =
+ | "fadeIn"
+ | "blurIn"
+ | "blurInUp"
+ | "blurInDown"
+ | "slideUp"
+ | "slideDown"
+ | "slideLeft"
+ | "slideRight"
+ | "scaleUp"
+ | "scaleDown"
+
+const motionElements = {
+ article: motion.article,
+ div: motion.div,
+ h1: motion.h1,
+ h2: motion.h2,
+ h3: motion.h3,
+ h4: motion.h4,
+ h5: motion.h5,
+ h6: motion.h6,
+ li: motion.li,
+ p: motion.p,
+ section: motion.section,
+ span: motion.span,
+} as const
+
+type MotionElementType = Extract<
+ keyof DOMMotionComponents,
+ keyof typeof motionElements
+>
+
+interface TextAnimateProps extends Omit
{
+ /**
+ * The text content to animate
+ */
+ children: string
+ /**
+ * The class name to be applied to the component
+ */
+ className?: string
+ /**
+ * The class name to be applied to each segment
+ */
+ segmentClassName?: string
+ /**
+ * The delay before the animation starts
+ */
+ delay?: number
+ /**
+ * The duration of the animation
+ */
+ duration?: number
+ /**
+ * Custom motion variants for the animation
+ */
+ variants?: Variants
+ /**
+ * The element type to render
+ */
+ as?: MotionElementType
+ /**
+ * How to split the text ("text", "word", "character")
+ */
+ by?: AnimationType
+ /**
+ * Whether to start animation when component enters viewport
+ */
+ startOnView?: boolean
+ /**
+ * Whether to animate only once
+ */
+ once?: boolean
+ /**
+ * The animation preset to use
+ */
+ animation?: AnimationVariant
+ /**
+ * Whether to enable accessibility features (default: true)
+ */
+ accessible?: boolean
+}
+
+const staggerTimings: Record = {
+ text: 0.06,
+ word: 0.05,
+ character: 0.03,
+ line: 0.06,
+}
+
+const defaultContainerVariants = {
+ hidden: { opacity: 1 },
+ show: {
+ opacity: 1,
+ transition: {
+ delayChildren: 0,
+ staggerChildren: 0.05,
+ },
+ },
+ exit: {
+ opacity: 0,
+ transition: {
+ staggerChildren: 0.05,
+ staggerDirection: -1,
+ },
+ },
+}
+
+const defaultItemVariants: Variants = {
+ hidden: { opacity: 0 },
+ show: {
+ opacity: 1,
+ },
+ exit: {
+ opacity: 0,
+ },
+}
+
+const defaultItemAnimationVariants: Record<
+ AnimationVariant,
+ { container: Variants; item: Variants }
+> = {
+ fadeIn: {
+ container: defaultContainerVariants,
+ item: {
+ hidden: { opacity: 0, y: 20 },
+ show: {
+ opacity: 1,
+ y: 0,
+ transition: {
+ duration: 0.3,
+ },
+ },
+ exit: {
+ opacity: 0,
+ y: 20,
+ transition: { duration: 0.3 },
+ },
+ },
+ },
+ blurIn: {
+ container: defaultContainerVariants,
+ item: {
+ hidden: { opacity: 0, filter: "blur(10px)" },
+ show: {
+ opacity: 1,
+ filter: "blur(0px)",
+ transition: {
+ duration: 0.3,
+ },
+ },
+ exit: {
+ opacity: 0,
+ filter: "blur(10px)",
+ transition: { duration: 0.3 },
+ },
+ },
+ },
+ blurInUp: {
+ container: defaultContainerVariants,
+ item: {
+ hidden: { opacity: 0, filter: "blur(10px)", y: 20 },
+ show: {
+ opacity: 1,
+ filter: "blur(0px)",
+ y: 0,
+ transition: {
+ y: { duration: 0.3 },
+ opacity: { duration: 0.4 },
+ filter: { duration: 0.3 },
+ },
+ },
+ exit: {
+ opacity: 0,
+ filter: "blur(10px)",
+ y: 20,
+ transition: {
+ y: { duration: 0.3 },
+ opacity: { duration: 0.4 },
+ filter: { duration: 0.3 },
+ },
+ },
+ },
+ },
+ blurInDown: {
+ container: defaultContainerVariants,
+ item: {
+ hidden: { opacity: 0, filter: "blur(10px)", y: -20 },
+ show: {
+ opacity: 1,
+ filter: "blur(0px)",
+ y: 0,
+ transition: {
+ y: { duration: 0.3 },
+ opacity: { duration: 0.4 },
+ filter: { duration: 0.3 },
+ },
+ },
+ },
+ },
+ slideUp: {
+ container: defaultContainerVariants,
+ item: {
+ hidden: { y: 20, opacity: 0 },
+ show: {
+ y: 0,
+ opacity: 1,
+ transition: {
+ duration: 0.3,
+ },
+ },
+ exit: {
+ y: -20,
+ opacity: 0,
+ transition: {
+ duration: 0.3,
+ },
+ },
+ },
+ },
+ slideDown: {
+ container: defaultContainerVariants,
+ item: {
+ hidden: { y: -20, opacity: 0 },
+ show: {
+ y: 0,
+ opacity: 1,
+ transition: { duration: 0.3 },
+ },
+ exit: {
+ y: 20,
+ opacity: 0,
+ transition: { duration: 0.3 },
+ },
+ },
+ },
+ slideLeft: {
+ container: defaultContainerVariants,
+ item: {
+ hidden: { x: 20, opacity: 0 },
+ show: {
+ x: 0,
+ opacity: 1,
+ transition: { duration: 0.3 },
+ },
+ exit: {
+ x: -20,
+ opacity: 0,
+ transition: { duration: 0.3 },
+ },
+ },
+ },
+ slideRight: {
+ container: defaultContainerVariants,
+ item: {
+ hidden: { x: -20, opacity: 0 },
+ show: {
+ x: 0,
+ opacity: 1,
+ transition: { duration: 0.3 },
+ },
+ exit: {
+ x: 20,
+ opacity: 0,
+ transition: { duration: 0.3 },
+ },
+ },
+ },
+ scaleUp: {
+ container: defaultContainerVariants,
+ item: {
+ hidden: { scale: 0.5, opacity: 0 },
+ show: {
+ scale: 1,
+ opacity: 1,
+ transition: {
+ duration: 0.3,
+ scale: {
+ type: "spring",
+ damping: 15,
+ stiffness: 300,
+ },
+ },
+ },
+ exit: {
+ scale: 0.5,
+ opacity: 0,
+ transition: { duration: 0.3 },
+ },
+ },
+ },
+ scaleDown: {
+ container: defaultContainerVariants,
+ item: {
+ hidden: { scale: 1.5, opacity: 0 },
+ show: {
+ scale: 1,
+ opacity: 1,
+ transition: {
+ duration: 0.3,
+ scale: {
+ type: "spring",
+ damping: 15,
+ stiffness: 300,
+ },
+ },
+ },
+ exit: {
+ scale: 1.5,
+ opacity: 0,
+ transition: { duration: 0.3 },
+ },
+ },
+ },
+}
+
+const TextAnimateBase = ({
+ children,
+ delay = 0,
+ duration = 0.3,
+ variants,
+ className,
+ segmentClassName,
+ as: Component = "p",
+ startOnView = true,
+ once = false,
+ by = "word",
+ animation = "fadeIn",
+ accessible = true,
+ ...props
+}: TextAnimateProps) => {
+ const MotionComponent = motionElements[Component]
+
+ let segments: string[] = []
+ switch (by) {
+ case "word":
+ segments = children.split(/(\s+)/)
+ break
+ case "character":
+ segments = children.split("")
+ break
+ case "line":
+ segments = children.split("\n")
+ break
+ case "text":
+ default:
+ segments = [children]
+ break
+ }
+
+ const finalVariants = variants
+ ? {
+ container: {
+ hidden: { opacity: 0 },
+ show: {
+ opacity: 1,
+ transition: {
+ opacity: { duration: 0.01, delay },
+ delayChildren: delay,
+ staggerChildren: duration / segments.length,
+ },
+ },
+ exit: {
+ opacity: 0,
+ transition: {
+ staggerChildren: duration / segments.length,
+ staggerDirection: -1,
+ },
+ },
+ },
+ item: variants,
+ }
+ : animation
+ ? {
+ container: {
+ ...defaultItemAnimationVariants[animation].container,
+ show: {
+ ...defaultItemAnimationVariants[animation].container.show,
+ transition: {
+ delayChildren: delay,
+ staggerChildren: duration / segments.length,
+ },
+ },
+ exit: {
+ ...defaultItemAnimationVariants[animation].container.exit,
+ transition: {
+ staggerChildren: duration / segments.length,
+ staggerDirection: -1,
+ },
+ },
+ },
+ item: defaultItemAnimationVariants[animation].item,
+ }
+ : { container: defaultContainerVariants, item: defaultItemVariants }
+
+ return (
+
+
+ {accessible && {children} }
+ {segments.map((segment, i) => (
+
+ {segment}
+
+ ))}
+
+
+ )
+}
+
+// Export the memoized version
+export const TextAnimate = memo(TextAnimateBase)
diff --git a/apps/www/content/docs/components/achievement-badge.mdx b/apps/www/content/docs/components/achievement-badge.mdx
index aab5056..ff4a6a2 100644
--- a/apps/www/content/docs/components/achievement-badge.mdx
+++ b/apps/www/content/docs/components/achievement-badge.mdx
@@ -1,8 +1,17 @@
---
title: Achievement Badge
-description: A single achievement card with locked states, progress rings, rarity labels, and click handling.
+description: "Open source React achievement badge component with locked/unlocked states, progress rings and rarity support. Use for achievement UI design and collections. Built on shadcn/ui + Tailwind."
+seoTitle: "Achievement Badge — React Achievement Badge Component"
+keywords:
+ - react achievement component
+ - achievement badge component
+ - achievement UI design
+ - shadcn achievements
+ - gamification components
---
+The Achievement Badge is the atomic unit of achievement gamification UI—a compact tile users recognize from games and productivity apps, with progress and rarity built in. Compose with [Achievement Card](/docs/components/achievement-card) and [Achievement Grid](/docs/components/achievement-grid) when you need achievement gamification at scale.
+
## Installation
diff --git a/apps/www/content/docs/components/achievement-card.mdx b/apps/www/content/docs/components/achievement-card.mdx
index ddaec87..30f5944 100644
--- a/apps/www/content/docs/components/achievement-card.mdx
+++ b/apps/www/content/docs/components/achievement-card.mdx
@@ -1,8 +1,17 @@
---
title: Achievement Card
-description: A composed achievement overview card with totals, featured badges, and an achievements list.
+description: "React achievement card with totals, featured badges, and scrollable lists for profile and progress surfaces. Fits achievement UI examples in SaaS, consumer and community products. Open source, built on shadcn/ui + Tailwind."
+seoTitle: "Achievement Card — Achievement UI Examples in React"
+keywords:
+ - achievement UI examples
+ - react achievement component
+ - gamification interface design
+ - shadcn gamification
+ - react gamification library
---
+The Achievement Card summarizes achievement gamification for a user—headline stats, spotlighted badges, and scrollable collections without leaving the card, suited to profile and progress hubs in gamification-heavy products.
+
## Installation
diff --git a/apps/www/content/docs/components/achievement-grid.mdx b/apps/www/content/docs/components/achievement-grid.mdx
index 2fb60a7..eaee1c3 100644
--- a/apps/www/content/docs/components/achievement-grid.mdx
+++ b/apps/www/content/docs/components/achievement-grid.mdx
@@ -1,8 +1,17 @@
---
title: Achievement Grid
-description: A responsive grid layout for displaying collections of achievements using the reusable achievement badge component.
+description: "Responsive React achievement grid for displaying collections of badges with progress and rarity across breakpoints. Ideal achievement UI design for galleries and showcases in consumer apps. Open source, built on shadcn/ui + Tailwind."
+seoTitle: "Achievement Grid — Achievement Badge Component for React"
+keywords:
+ - achievement badge component
+ - achievement UI design
+ - react gamification components
+ - gamification UI patterns
+ - tailwind gamification components
---
+Achievement Grid lays out badge collections in a scannable gamification surface—balanced columns, spacing rhythm, and built-in rarity or completion metadata for trophy rooms and showcase-style achievement gamification.
+
## Installation
diff --git a/apps/www/content/docs/components/achievement-list.mdx b/apps/www/content/docs/components/achievement-list.mdx
index 25d9e56..35bc76a 100644
--- a/apps/www/content/docs/components/achievement-list.mdx
+++ b/apps/www/content/docs/components/achievement-list.mdx
@@ -1,8 +1,17 @@
---
title: Achievement List
-description: A vertical list layout for achievements with badge, description, and optional progress indicator.
+description: "Vertical React achievement list with icons, descriptions, and optional progress for dense achievement UI. Complements grids and cards in gamification dashboards. Open source, built on shadcn/ui + Tailwind."
+seoTitle: "Achievement List — React Achievement Component List"
+keywords:
+ - react achievement component
+ - achievement UI examples
+ - gamification widgets
+ - react gamification
+ - shadcn ui gamification
---
+Achievement List is built for dense gamification layouts where each row carries a title, supporting text, and progress—ideal for onboarding missions, quest logs, and checklist-style achievement gamification.
+
## Installation
diff --git a/apps/www/content/docs/components/achievement-unlocked.mdx b/apps/www/content/docs/components/achievement-unlocked.mdx
index d7f4c9d..7e8c16c 100644
--- a/apps/www/content/docs/components/achievement-unlocked.mdx
+++ b/apps/www/content/docs/components/achievement-unlocked.mdx
@@ -1,8 +1,17 @@
---
title: Achievement Unlocked
-description: A celebratory modal dialog for announcing newly unlocked achievements with optional sharing.
+description: "Celebratory React dialog for newly unlocked achievements with headline, badge, and share actions. Use for unlock moments, level up animations and more. Built on shadcn/ui + Tailwind. Open source."
+seoTitle: "Achievement Unlocked — Level Up Animation UI for React"
+keywords:
+ - level up animation
+ - react achievement component
+ - achievement UI examples
+ - gamification frontend library
+ - shadcn gamification components
---
+Achievement Unlocked is a focused gamification moment for celebrating unlocks, level up animations, congratulating users, reinforcing rewards, and encouraging sharing in achievement-driven gamification experiences.
+
## Installation
diff --git a/apps/www/content/docs/components/index.mdx b/apps/www/content/docs/components/index.mdx
index 2e91a31..0dcaa74 100644
--- a/apps/www/content/docs/components/index.mdx
+++ b/apps/www/content/docs/components/index.mdx
@@ -1,8 +1,17 @@
---
title: Components
-description: Explore all components available in the UI library.
+description: "Browse Trophy's Gamification UI Kit with React widgets for streaks, achievements, leaderboards, and points. Free, open source. Install via shadcn registry."
+seoTitle: "Components — Gamification UI Library for React"
+keywords:
+ - gamification ui library
+ - gamification components
+ - react gamification components
+ - free gamification components
+ - gamification widgets
---
+Trophy's Gamification UI Kit ships gamification widgets as focused React building blocks like streak counters, achievement badges, leaderboard rankings, and points surfaces rather than a monolithic SDK. Each page documents one component you can add with the shadcn CLI, then customize like any other file in your repo.
+
- [Achievement Badge](/docs/components/achievement-badge)
- [Achievement Card](/docs/components/achievement-card)
- [Achievement Grid](/docs/components/achievement-grid)
diff --git a/apps/www/content/docs/components/leaderboard-card.mdx b/apps/www/content/docs/components/leaderboard-card.mdx
index db5629e..7f82270 100644
--- a/apps/www/content/docs/components/leaderboard-card.mdx
+++ b/apps/www/content/docs/components/leaderboard-card.mdx
@@ -1,8 +1,17 @@
---
title: Leaderboard Card
-description: A composed leaderboard view with title, date range, podium, and rankings.
+description: "React leaderboard component that composes title, date range, podium, and rankings—one card for competitive gamification UIs. Fits Next.js and React apps. Built on shadcn/ui + Tailwind. Open source."
+seoTitle: "Leaderboard Card — React Leaderboard UI & Shadcn Leaderboard"
+keywords:
+ - react leaderboard component
+ - leaderboard UI component
+ - leaderboard UI design
+ - shadcn leaderboard
+ - nextjs leaderboard
---
+Leaderboard Card bundles competitive gamification UI for React—a [Leaderboard Podium](/docs/components/leaderboard-podium) plus [Leaderboard Rankings](/docs/components/leaderboard-rankings) in one card for challenges, contests, and social gamification surfaces, with collapsible rows and easy highlighting for the current user.
+
## Installation
diff --git a/apps/www/content/docs/components/leaderboard-podium.mdx b/apps/www/content/docs/components/leaderboard-podium.mdx
index 7b260c0..9a2664c 100644
--- a/apps/www/content/docs/components/leaderboard-podium.mdx
+++ b/apps/www/content/docs/components/leaderboard-podium.mdx
@@ -1,8 +1,17 @@
---
title: Leaderboard Podium
-description: A visual podium display for the top 3 ranked users with crowns and avatars.
+description: "Top-three React leaderboard podium with crowns, avatars, and ranks for competitive gamification. Leaderboard UI component for highlights and recap screens. Built on shadcn/ui + Tailwind. Open source."
+seoTitle: "Leaderboard Podium — Leaderboard UI Component"
+keywords:
+ - leaderboard UI component
+ - leaderboard UI examples
+ - react leaderboard
+ - gamification widgets
+ - shadcn gamification components
---
+The Leaderboard Podium spotlights top-three ranks in competitive gamification UIs. Pair with [Leaderboard Rankings](/docs/components/leaderboard-rankings) for a hero podium plus a full table—ideal after weekly resets, seasonal contests, or anywhere gamification needs a clear winner moment.
+
## Installation
diff --git a/apps/www/content/docs/components/leaderboard-rankings.mdx b/apps/www/content/docs/components/leaderboard-rankings.mdx
index 89a2ad0..1820214 100644
--- a/apps/www/content/docs/components/leaderboard-rankings.mdx
+++ b/apps/www/content/docs/components/leaderboard-rankings.mdx
@@ -1,8 +1,17 @@
---
title: Leaderboard Rankings
-description: A leaderboard rankings list with rank, avatar, userName, optional byline, value, and crown highlights for the top 3.
+description: "React leaderboard rankings list—avatars, scores, bylines, and crown highlights for full tables. Open-source leaderboard UI for games, communities, and SaaS challenges. shadcn/ui + Tailwind."
+seoTitle: "Leaderboard Rankings — React Leaderboard Table & UI"
+keywords:
+ - react leaderboard
+ - leaderboard component
+ - open source leaderboard react
+ - leaderboard UI examples
+ - react gamification components
---
+Leaderboard Rankings is a dense React table for competitive gamification—full rankings with avatars, scores, bylines, and crown highlights for the top three, plus pagination and collapsible rows with built-in highlighting for the current user in leaderboard-driven gamification flows.
+
## Installation
diff --git a/apps/www/content/docs/components/points-awards.mdx b/apps/www/content/docs/components/points-awards.mdx
index 1a484de..c18b2a2 100644
--- a/apps/www/content/docs/components/points-awards.mdx
+++ b/apps/www/content/docs/components/points-awards.mdx
@@ -1,8 +1,16 @@
---
title: Points Awards
-description: A points activity feed with date, totals, and dynamic trigger types
+description: "React points activity feed with dated events, totals, and trigger types for gamification timelines. Useful for XP and currency ledgers in dashboards. Built on shadcn/ui + Tailwind. Open source."
+seoTitle: "Points Awards — Gamification Points Feed for React"
+keywords:
+ - react gamification library
+ - gamification UI patterns
+ - gamification interface design
+ - react gamification components
---
+Points Awards surfaces what changed in a gamification ledger—grants, spends, bonuses, and campaign events in a readable timeline. Use when gamification UI needs transparency where trust grows when users see exactly how points moved.
+
## Installation
diff --git a/apps/www/content/docs/components/points-badge.mdx b/apps/www/content/docs/components/points-badge.mdx
index f62cff9..d30f073 100644
--- a/apps/www/content/docs/components/points-badge.mdx
+++ b/apps/www/content/docs/components/points-badge.mdx
@@ -1,8 +1,17 @@
---
title: Points Badge
-description: A compact component for displaying user point totals.
+description: "Compact React points badge for totals and currency-style scores in gamification UIs. Fits XP-style displays alongside charts and lists. Built on shadcn/ui + Tailwind. Open source."
+seoTitle: "Points Badge — React XP Bar Companion & Points UI"
+keywords:
+ - xp bar component
+ - react xp bar
+ - gamification widgets
+ - react gamification components
+ - tailwind gamification
---
+The Points Badge is a lightweight gamification readout for points and XP totals—use it anywhere gamification scores need a compact header. Compose with [Points Chart](/docs/components/points-chart) or [Points Levels List](/docs/components/points-levels-list) when users need headline totals plus deeper progress gamification elsewhere in the layout.
+
## Installation
diff --git a/apps/www/content/docs/components/points-boost.mdx b/apps/www/content/docs/components/points-boost.mdx
index 6aabc62..855f60d 100644
--- a/apps/www/content/docs/components/points-boost.mdx
+++ b/apps/www/content/docs/components/points-boost.mdx
@@ -1,8 +1,17 @@
---
title: Points Boost
-description: A promotional callout for points multipliers with optional countdown timer.
+description: "React callout for points multipliers and limited-time boosts. Supports optional countdown for near-ending gamification campaigns. Built on shadcn/ui + Tailwind. Open source."
+seoTitle: "Points Boost — Gamification Multiplier UI for React"
+keywords:
+ - gamification widgets
+ - gamification UI examples
+ - react gamification
+ - tailwind gamification components
+ - shadcn gamification
---
+Points Boost highlights time-bound gamification incentives like double XP weekends, referral multipliers, or seasonal bonuses with optional countdowns. Drop it above leaderboards, checkout, or task lists where gamification UI should nudge action without changing underlying points logic.
+
## Installation
diff --git a/apps/www/content/docs/components/points-chart.mdx b/apps/www/content/docs/components/points-chart.mdx
index 58747ad..badc057 100644
--- a/apps/www/content/docs/components/points-chart.mdx
+++ b/apps/www/content/docs/components/points-chart.mdx
@@ -1,8 +1,17 @@
---
title: Points Chart
-description: A line chart component for displaying points trends over time.
+description: "React chart for points over time. Displays trends, campaigns, and seasonality in gamification analytics views. Complements badges and levels. Built on Recharts + shadcn/ui + Tailwind. Open source."
+seoTitle: "Points Chart — Progress and XP Trends in React"
+keywords:
+ - react xp chart
+ - xp chart design
+ - progress chart gamification
+ - gamification UI library
+ - react gamification library
---
+Points Chart shows how gamification momentum shifted over time—spikes after launches, steady grind weeks, or drops when engagement cools. Use on dashboards alongside [Points Badge](/docs/components/points-badge) or [Points Levels List](/docs/components/points-levels-list) so gamification trends and point totals stay aligned.
+
## Installation
diff --git a/apps/www/content/docs/components/points-levels-list.mdx b/apps/www/content/docs/components/points-levels-list.mdx
index cf85837..11aa719 100644
--- a/apps/www/content/docs/components/points-levels-list.mdx
+++ b/apps/www/content/docs/components/points-levels-list.mdx
@@ -1,8 +1,16 @@
---
title: Points Levels List
-description: A compact points levels table with optional current-level label and progress header.
+description: "React levels list with optional progress bar between tiers. Built on shadcn/ui + Tailwind. Open source."
+seoTitle: "Points Levels List — React Level System & Progress Bar"
+keywords:
+ - react level system
+ - progress bar gamification
+ - xp bar component
+ - react progress bar component
---
+Points Levels List is a vertical level-system UI for gamification progression—current tier, next milestone, and optional progress between thresholds in points- and XP-driven gamification experiences.
+
## Installation
diff --git a/apps/www/content/docs/components/points-levels-timeline.mdx b/apps/www/content/docs/components/points-levels-timeline.mdx
index ef56b79..da72cf2 100644
--- a/apps/www/content/docs/components/points-levels-timeline.mdx
+++ b/apps/www/content/docs/components/points-levels-timeline.mdx
@@ -1,8 +1,17 @@
---
title: Points Levels Timeline
-description: A vertical levels timeline with optional sub-level details and progress tracking.
+description: "Vertical React timeline for level progression. Supports sub-levels, thresholds, and level progress for gamification roadmaps. Complements Points Levels List. Built on shadcn/ui + Tailwind. Open source."
+seoTitle: "Points Levels Timeline — React Level System Timeline"
+keywords:
+ - react level system
+ - xp bar design
+ - level design system
+ - react gamification components
+ - tailwind gamification
---
+Points Levels Timeline tells a gamification leveling story top to bottom—which tiers users passed, current progress, and XP to the next level—built for progression-heavy gamification products. Use [Points Levels List](/docs/components/points-levels-list) when you need a more compact view of level progression.
+
## Installation
diff --git a/apps/www/content/docs/components/streak-badge.mdx b/apps/www/content/docs/components/streak-badge.mdx
index de25a95..95404a8 100644
--- a/apps/www/content/docs/components/streak-badge.mdx
+++ b/apps/www/content/docs/components/streak-badge.mdx
@@ -1,8 +1,17 @@
---
title: Streak Badge
-description: A compact badge displaying the current streak count with customizable styles.
+description: "React streak counter UI in a compact badge supporting daily, weekly, monthly or yearly streaks. Built on shadcn/ui + Tailwind. Open source."
+seoTitle: "Streak Badge — React Streak Component & Streak Counter"
+keywords:
+ - react streak component
+ - streak counter component
+ - streak UI component
+ - streak UI design component
+ - habit tracking UI
---
+The Streak Badge is a compact building block for streak gamification UI in React. Display current streak length, embed optional flame or custom icons, and control layout for habit tracking and engagement gamification flows. Compose with [Streak Card](/docs/components/streak-card) or [Streak Calendar](/docs/components/streak-calendar) when you need streak gamification UI at scale.
+
## Installation
diff --git a/apps/www/content/docs/components/streak-calendar.mdx b/apps/www/content/docs/components/streak-calendar.mdx
index d36c398..93353d1 100644
--- a/apps/www/content/docs/components/streak-calendar.mdx
+++ b/apps/www/content/docs/components/streak-calendar.mdx
@@ -1,8 +1,18 @@
---
title: Streak Calendar
-description: A streak calendar with week, month, and git-style history views.
+description: "React streak calendar for gamification—week, month, and git-style grids to visualize consistency. Native support for streak freezes. Ideal streak UI design for habit tracking and personal progress. Built on shadcn/ui and Tailwind CSS. Open source."
+seoTitle: "Streak Calendar — Streak UI Design & Habit Tracking Component"
+keywords:
+ - streak UI design
+ - streak UI examples
+ - react streak component
+ - habit tracking UI
+ - habit tracking component
+ - streak freeze UI
---
+The Streak Calendar is a flexible gamification component for visualizing streak consistency with active days, weeks, and months with native support for streak freezes in retention-focused gamification UIs. Compose with [Streak Badge](/docs/components/streak-badge) or [Streak Card](/docs/components/streak-card) when you need streak gamification UI at scale.
+
## Installation
diff --git a/apps/www/content/docs/components/streak-card.mdx b/apps/www/content/docs/components/streak-card.mdx
index 7066712..b4403bc 100644
--- a/apps/www/content/docs/components/streak-card.mdx
+++ b/apps/www/content/docs/components/streak-card.mdx
@@ -1,8 +1,17 @@
---
title: Streak Card
-description: A composed streak summary card built from streak calendar and metrics.
+description: "Full streak UI card for React. Daily streak display, longest streak record with optional section for streak rules. Built on shadcn/ui + Tailwind. Open source."
+seoTitle: "Streak Card — React Streak UI & Habit Tracking Component"
+keywords:
+ - streak UI component
+ - react streak component
+ - streak UI examples
+ - streak record UI
+ - habit tracking UI
---
+The Streak Card is a full streak gamification surface for React, composed of [Streak Calendar](/docs/components/streak-calendar) and [Streak Badge](/docs/components/streak-badge). Display daily streak, longest streak record, and optional streak rules—ideal for fitness, productivity, and other habit gamification experiences.
+
## Installation
diff --git a/apps/www/content/docs/index.mdx b/apps/www/content/docs/index.mdx
index ed18a11..cdd3707 100644
--- a/apps/www/content/docs/index.mdx
+++ b/apps/www/content/docs/index.mdx
@@ -1,9 +1,16 @@
---
title: Introduction
-description: Trophy UI - Gamification components for shadcn/ui
+description: "Gamification UI Kit by Trophy is a free, open-source gamification UI kit for React and Next.js—copy-paste streak, achievement, leaderboard, and points components via the shadcn registry. Built on shadcn/ui and Tailwind CSS."
+seoTitle: "Introduction — Gamification UI Kit & React Component Library"
+keywords:
+ - gamification ui kit
+ - gamification component library
+ - react gamification library
+ - next js gamification
+ - shadcn gamification components
---
-Trophy UI is a component library built on top of [shadcn/ui](https://ui.shadcn.com) that helps you build gamification experiences faster. It provides pre-built components for streaks, achievements, leaderboards, points, and more.
+Trophy's Gamification UI Kit is a component library built on top of [shadcn/ui](https://ui.shadcn.com) that helps you build gamification experiences faster. It provides pre-built components for streaks, achievements, leaderboards, points, and more.
Components are available via the shadcn CLI:
diff --git a/apps/www/content/docs/styles.mdx b/apps/www/content/docs/styles.mdx
index ce3b1a7..8cb6456 100644
--- a/apps/www/content/docs/styles.mdx
+++ b/apps/www/content/docs/styles.mdx
@@ -1,9 +1,16 @@
---
title: Styles
-description: Token and theming standards for keeping Trophy UI unopinionated.
+description: "Tailwind-first design tokens and theming for Trophy's Gamification UI Kit—semantic colors, typography, and spacing so gamification components stay on-brand. Part of a Tailwind + shadcn gamification stack."
+seoTitle: "Styles — Tailwind Gamification Design Tokens"
+keywords:
+ - tailwind gamification
+ - tailwind gamification components
+ - gamification design system
+ - shadcn ui gamification
+ - gamification interface design
---
-Trophy UI components are themeable and unopinionated by default. Component styles are built on semantic tokens, not fixed palette classes, so they remain portable across customer design systems.
+Components in Trophy's Gamification UI Kit are themeable and unopinionated by default. Component styles are built on semantic tokens, not fixed palette classes, so they remain portable across customer design systems.
## Core Standards
diff --git a/apps/www/content/docs/usage.mdx b/apps/www/content/docs/usage.mdx
index 47c68ad..3b6778a 100644
--- a/apps/www/content/docs/usage.mdx
+++ b/apps/www/content/docs/usage.mdx
@@ -1,9 +1,16 @@
---
title: Usage
-description: How to use Trophy UI components in your application.
+description: "Install components in Trophy's Gamification UI Kit in React or Next.js with the shadcn CLI—open-code files, Tailwind v4, and client components you compose in App Router or SPA pages."
+seoTitle: "Usage — Next.js & React Gamification Components"
+keywords:
+ - next js gamification
+ - nextjs leaderboard
+ - react gamification library
+ - shadcn registry gamification
+ - gamification npm package
---
-Once a Trophy UI component is installed, you can import and use it like any other React component. Files are added into your project (the same [open-code](https://ui.shadcn.com/docs) model as shadcn/ui), so you can open them, adjust markup, and align styles with your design system.
+Once a component in Trophy's Gamification UI Kit is installed, you can import and use it like any other React component. Files are added into your project (the same [open-code](https://ui.shadcn.com/docs) model as shadcn/ui), so you can open them, adjust markup, and align styles with your design system.
## Example
diff --git a/apps/www/lib/config.ts b/apps/www/lib/config.ts
index a643a03..54e0153 100644
--- a/apps/www/lib/config.ts
+++ b/apps/www/lib/config.ts
@@ -7,13 +7,39 @@ function siteOrigin(): string {
}
export const siteConfig = {
- name: "Trophy UI",
+ name: "Gamification UI Kit by Trophy",
url: siteOrigin(),
ogImage: `${siteOrigin()}/og.jpg`,
- title: "Trophy UI",
- tagline: "Open Source gamification components",
+ title: "Gamification UI Kit by Trophy",
description:
- "A collection of Open Source gamification components that you can customize, extend, and build on.",
+ "Trophy's Gamification UI Kit is an open-source library of gamification UI components built on shadcn/ui and Tailwind CSS. Drop-in React components for streaks, achievements, leaderboards, points, and more — ready to copy and customize.",
+ keywords: [
+ "gamification UI kit",
+ "gamification UI components",
+ "gamification component library",
+ "gamification React components",
+ "Next.js gamification",
+ "streak UI component",
+ "achievement UI component",
+ "leaderboard UI component",
+ "points UI component",
+ "gamification design system",
+ "React gamification library",
+ "shadcn gamification",
+ "shadcn registry",
+ "Tailwind CSS gamification",
+ "Tailwind gamification components",
+ "user engagement components",
+ "open source gamification",
+ "open source gamification UI",
+ "free gamification components",
+ "gamification toolkit",
+ "streak tracker React",
+ "leaderboard React component",
+ "achievement badge React",
+ "points system UI",
+ "gamification UX"
+ ],
links: {
github: "https://github.com/trophyso/ui",
},
diff --git a/apps/www/lib/docs-metadata.ts b/apps/www/lib/docs-metadata.ts
new file mode 100644
index 0000000..0f7744f
--- /dev/null
+++ b/apps/www/lib/docs-metadata.ts
@@ -0,0 +1,37 @@
+import { siteConfig } from "@/lib/config"
+
+export type TrophyDocsFrontmatter = {
+ title: string
+ description?: string
+ seoTitle?: string
+ keywords?: string[]
+}
+
+export function formatDocsSeoTitle(core: string): string {
+ const trimmed = core.trim()
+ const suffix = siteConfig.title.trim()
+ if (!suffix) return trimmed
+ return `${trimmed} | ${suffix}`
+}
+
+export function resolveDocsSeoTitle(
+ doc: Pick
+): string {
+ const custom = doc.seoTitle?.trim()
+ if (custom) return formatDocsSeoTitle(custom)
+ return formatDocsSeoTitle(doc.title)
+}
+
+export function resolveDocsMetaKeywords(
+ doc: Pick
+): string[] {
+ const base = [
+ doc.title,
+ "gamification UI component",
+ "React component",
+ "shadcn UI",
+ "Tailwind CSS"
+ ]
+ const extra = doc.keywords?.map((k) => k.trim()).filter(Boolean) ?? []
+ return [...new Set([...extra, ...base])]
+}
diff --git a/apps/www/package.json b/apps/www/package.json
index 9d45dc3..df5319d 100644
--- a/apps/www/package.json
+++ b/apps/www/package.json
@@ -45,7 +45,10 @@
"react-syntax-highlighter": "^16.1.0",
"recharts": "^3.8.1",
"shiki": "3.22.0",
- "tailwind-merge": "^3.0.1"
+ "tailwind-merge": "^3.0.1",
+ "yaml": "^2.8.2",
+ "zod": "^4.3.6",
+ "motion": "^11.18.1"
},
"prettier": {
"endOfLine": "lf",
diff --git a/apps/www/source.config.ts b/apps/www/source.config.ts
index 4855976..165fd8a 100644
--- a/apps/www/source.config.ts
+++ b/apps/www/source.config.ts
@@ -1,7 +1,16 @@
-import { defineConfig, defineDocs } from "fumadocs-mdx/config"
+import { defineConfig, defineDocs, frontmatterSchema } from "fumadocs-mdx/config"
+import { z } from "zod"
+
+const docsPageSchema = frontmatterSchema.extend({
+ seoTitle: z.string().optional(),
+ keywords: z.array(z.string()).optional(),
+})
export const docs = defineDocs({
dir: "content/docs",
+ docs: {
+ schema: docsPageSchema,
+ },
})
export default defineConfig({
diff --git a/apps/www/styles/globals.css b/apps/www/styles/globals.css
index beebbf2..1b3e863 100644
--- a/apps/www/styles/globals.css
+++ b/apps/www/styles/globals.css
@@ -1,6 +1,22 @@
@import "tailwindcss";
@import "tw-animate-css";
+@keyframes expand-in {
+ from {
+ max-width: 0;
+ opacity: 0;
+ }
+
+ to {
+ max-width: 8.5ch;
+ opacity: 0.9;
+ }
+}
+
+@utility animate-expand-in {
+ animation: expand-in 350ms ease-out 200ms both;
+}
+
@custom-variant dark (&:is(.dark *));
@custom-variant fixed (&:is(.layout-fixed *));
@@ -370,16 +386,16 @@ pre code span {
@apply !mt-0;
}
- &:first-child > h3:first-child {
+ &:first-child>h3:first-child {
@apply !mt-0;
}
- > h3 {
+ >h3 {
@apply !mt-16;
}
- > h3 + p {
+ >h3+p {
@apply !mt-2;
}
}
-}
+}
\ No newline at end of file
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index ced110b..21ef5c6 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -138,6 +138,9 @@ importers:
lucide-react:
specifier: 0.474.0
version: 0.474.0(react@19.2.1)
+ motion:
+ specifier: ^11.18.1
+ version: 11.18.2(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
next:
specifier: 16.0.10
version: 16.0.10(@babel/core@7.29.0)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
@@ -162,6 +165,12 @@ importers:
tailwind-merge:
specifier: ^3.0.1
version: 3.5.0
+ yaml:
+ specifier: ^2.8.2
+ version: 2.8.2
+ zod:
+ specifier: ^4.3.6
+ version: 4.3.6
devDependencies:
'@eslint/eslintrc':
specifier: ^3
@@ -2550,6 +2559,20 @@ packages:
fraction.js@5.3.4:
resolution: {integrity: sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==}
+ framer-motion@11.18.2:
+ resolution: {integrity: sha512-5F5Och7wrvtLVElIpclDT0CBzMVg3dL22B64aZwHtsIY8RB4mXICLrkajK4G9R+ieSAGcgrLeae2SeUTg2pr6w==}
+ peerDependencies:
+ '@emotion/is-prop-valid': '*'
+ react: ^18.0.0 || ^19.0.0
+ react-dom: ^18.0.0 || ^19.0.0
+ peerDependenciesMeta:
+ '@emotion/is-prop-valid':
+ optional: true
+ react:
+ optional: true
+ react-dom:
+ optional: true
+
fs-extra@7.0.1:
resolution: {integrity: sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==}
engines: {node: '>=6 <7 || >=8'}
@@ -3366,6 +3389,26 @@ packages:
minimist@1.2.8:
resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==}
+ motion-dom@11.18.1:
+ resolution: {integrity: sha512-g76KvA001z+atjfxczdRtw/RXOM3OMSdd1f4DL77qCTF/+avrRJiawSG4yDibEQ215sr9kpinSlX2pCTJ9zbhw==}
+
+ motion-utils@11.18.1:
+ resolution: {integrity: sha512-49Kt+HKjtbJKLtgO/LKj9Ld+6vw9BjH5d9sc40R/kVyH8GLAXgT42M2NnuPcJNuA3s9ZfZBUcwIgpmZWGEE+hA==}
+
+ motion@11.18.2:
+ resolution: {integrity: sha512-JLjvFDuFr42NFtcVoMAyC2sEjnpA8xpy6qWPyzQvCloznAyQ8FIXioxWfHiLtgYhoVpfUqSWpn1h9++skj9+Wg==}
+ peerDependencies:
+ '@emotion/is-prop-valid': '*'
+ react: ^18.0.0 || ^19.0.0
+ react-dom: ^18.0.0 || ^19.0.0
+ peerDependenciesMeta:
+ '@emotion/is-prop-valid':
+ optional: true
+ react:
+ optional: true
+ react-dom:
+ optional: true
+
mri@1.2.0:
resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==}
engines: {node: '>=4'}
@@ -7135,6 +7178,15 @@ snapshots:
fraction.js@5.3.4: {}
+ framer-motion@11.18.2(react-dom@19.2.1(react@19.2.1))(react@19.2.1):
+ dependencies:
+ motion-dom: 11.18.1
+ motion-utils: 11.18.1
+ tslib: 2.8.1
+ optionalDependencies:
+ react: 19.2.1
+ react-dom: 19.2.1(react@19.2.1)
+
fs-extra@7.0.1:
dependencies:
graceful-fs: 4.2.11
@@ -8253,6 +8305,20 @@ snapshots:
minimist@1.2.8: {}
+ motion-dom@11.18.1:
+ dependencies:
+ motion-utils: 11.18.1
+
+ motion-utils@11.18.1: {}
+
+ motion@11.18.2(react-dom@19.2.1(react@19.2.1))(react@19.2.1):
+ dependencies:
+ framer-motion: 11.18.2(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
+ tslib: 2.8.1
+ optionalDependencies:
+ react: 19.2.1
+ react-dom: 19.2.1(react@19.2.1)
+
mri@1.2.0: {}
ms@2.1.3: {}