Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 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
12 changes: 10 additions & 2 deletions apps/www/.source/source.config.mjs
Original file line number Diff line number Diff line change
@@ -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: {
Expand Down
184 changes: 179 additions & 5 deletions apps/www/app/(app)/(root)/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,24 +17,112 @@ 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,
images: [{ url: siteConfig.ogImage }],
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: "Trophy UI",
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 (
<div className="flex flex-1 flex-col">
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}
/>
<div className="relative overflow-hidden">
<div
className="pointer-events-none absolute inset-0"
Expand All @@ -47,10 +135,10 @@ export default function IndexPage() {
<PageHeaderHeading className="max-w-4xl">
<span className="font-montserrat flex items-baseline gap-2 sm:gap-3">
<span className="leading-[0.95] font-bold tracking-[-0.03em]">
Trophy
Gamification UI Kit
</span>
<span className="font-normal tracking-[-0.02em] opacity-90">
UI
<span className="animate-expand-in overflow-hidden whitespace-nowrap font-normal tracking-[-0.02em]">
by Trophy
</span>
</span>
</PageHeaderHeading>
Expand All @@ -68,6 +156,92 @@ export default function IndexPage() {
</PageHeader>
<HomeComponentMosaic />
</div>
<section className="container-wrapper">
<div className="container mx-auto max-w-3xl px-4 py-12 text-center">
<h2 className="text-2xl font-semibold tracking-tight sm:text-3xl">
Gamification UI Components for React
</h2>
<p className="text-muted-foreground mx-auto mt-4 max-w-2xl text-base sm:text-lg">
Trophy&apos;s Gamification UI Kit gives you production-ready
gamification components — streak trackers, achievement badges,
leaderboards, points displays, and more. Built on{" "}
<Link href="https://ui.shadcn.com" className="underline">
shadcn/ui
</Link>{" "}
and Tailwind CSS, every component is open source, fully
customizable, and installs with a single CLI command into any React
or Next.js project.
</p>
</div>
</section>
<section className="container-wrapper border-grid border-t">
<div className="container mx-auto max-w-5xl px-4 py-16">
<h2 className="text-center text-2xl font-semibold tracking-tight sm:text-3xl">
Everything You Need to Ship Gamification
</h2>
<p className="text-muted-foreground mx-auto mt-3 max-w-2xl text-center text-base sm:text-lg">
Four categories of components covering the most impactful
gamification patterns — all composable, accessible, and
theme-aware.
</p>
<div className="mt-12 grid gap-8 sm:grid-cols-2">
{categories.map((category) => (
<Link
key={category.title}
href={category.href}
className="border-border hover:border-foreground/20 group rounded-xl border p-6 transition-colors"
>
<h3 className="text-lg font-semibold tracking-tight">
{category.title}
</h3>
<p className="text-muted-foreground mt-2 text-sm leading-relaxed">
{category.description}
</p>
<div className="mt-4 flex flex-wrap gap-1.5">
{category.components.map((component) => (
<span
key={component}
className="bg-secondary text-secondary-foreground rounded-md px-2 py-0.5 text-xs"
>
{component}
</span>
))}
</div>
</Link>
))}
</div>
</div>
</section>
<section className="container-wrapper border-grid border-t">
<div className="container mx-auto max-w-3xl px-4 py-16 text-center">
<h2 className="text-2xl font-semibold tracking-tight sm:text-3xl">
Built for Developers
</h2>
<div className="mt-8 grid gap-6 text-left sm:grid-cols-3">
<div>
<h3 className="font-semibold">One-Command Install</h3>
<p className="text-muted-foreground mt-1 text-sm">
Add any component to your project with a single CLI command.
No extra dependencies to manage.
</p>
</div>
<div>
<h3 className="font-semibold">Fully Customizable</h3>
<p className="text-muted-foreground mt-1 text-sm">
Every component lives in your codebase. Tweak styles, layout,
and behavior to match your brand.
</p>
</div>
<div>
<h3 className="font-semibold">Open Source</h3>
<p className="text-muted-foreground mt-1 text-sm">
MIT licensed and community-driven. Use in personal projects or
production apps without restrictions.
</p>
</div>
</div>
</div>
</section>
</div>
)
}
25 changes: 21 additions & 4 deletions apps/www/app/(app)/docs/[[...slug]]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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,
},
}
}

Expand Down
7 changes: 7 additions & 0 deletions apps/www/app/(app)/layout.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<div className="bg-background relative z-10 flex min-h-svh flex-col">
Expand Down
15 changes: 15 additions & 0 deletions apps/www/app/robots.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import type { MetadataRoute } from "next"

import { siteConfig } from "@/lib/config"

export default function robots(): MetadataRoute.Robots {
return {
rules: [
{
userAgent: "*",
allow: "/",
},
],
sitemap: `${siteConfig.url}/sitemap.xml`,
}
}
25 changes: 25 additions & 0 deletions apps/www/app/sitemap.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import type { MetadataRoute } from "next"

import { siteConfig } from "@/lib/config"
import { source } from "@/lib/source"

export default function sitemap(): MetadataRoute.Sitemap {
const pages = source.getPages()

const docEntries: MetadataRoute.Sitemap = pages.map((page) => ({
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,
]
}
11 changes: 10 additions & 1 deletion apps/www/content/docs/components/achievement-badge.mdx
Original file line number Diff line number Diff line change
@@ -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 | Trophy UI"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the lowercase here is a bit ugly IMO, and we're not calling this Trophy UI yet I thought, instead Gamification UI Kit by Trophy? in this case could just be Gamification UI Kit in place of "Trophy UI" here.

We should probably dynamically append that | [name of site] rather than hardcoding it into every component

This comment applies to all component mdx files

keywords:
- react achievement component
- achievement badge component
- achievement UI design
- shadcn achievements
- gamification components
---

The Achievement Badge is the atomic unit of achievement UI - a compact tile users recognize from games and productivity apps, with room for displaying user progress and rarity. Compose with [Achievement Card](/docs/components/achievement-card) and [Achievement Grid](/docs/components/achievement-grid) when you need to display achievement UI at scale.

<ComponentPreview name="achievement-badge" />

## Installation
Expand Down
11 changes: 10 additions & 1 deletion apps/www/content/docs/components/achievement-card.mdx
Original file line number Diff line number Diff line change
@@ -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 | Trophy UI"
keywords:
- achievement UI examples
- react achievement component
- gamification interface design
- shadcn gamification
- react gamification library
---

The Achievement Card summarizes achievement UI for a user: headline stats, spotlighted badges, and room to browse more without leaving the card.

<ComponentPreview name="achievement-card" />

## Installation
Expand Down
Loading