From 84ed9dedad5494d2d7f2c077f1aeac81f7870b45 Mon Sep 17 00:00:00 2001 From: Andres Gonzalez Date: Fri, 30 Jan 2026 18:07:31 -0500 Subject: [PATCH] refactor(www): improve docs layout to match shadcn pattern - Update registry layout with proper container and grid structure - Simplify home page to cleaner introduction with installation example - Refactor sidebar for improved navigation - Update docs layout with consistent spacing - Refresh tokens page styling --- apps/www/public/r/registry.json | 830 +++++++++++++++++- apps/www/public/r/usage-meter-base.json | 16 +- apps/www/public/r/usage-meter.json | 16 +- apps/www/src/app/(registry)/layout.tsx | 30 +- apps/www/src/app/(registry)/page.tsx | 258 ++---- apps/www/src/app/(registry)/tokens/page.tsx | 323 +++---- apps/www/src/app/docs/layout.tsx | 38 +- .../components/registry/registry-sidebar.tsx | 420 +++------ 8 files changed, 1270 insertions(+), 661 deletions(-) diff --git a/apps/www/public/r/registry.json b/apps/www/public/r/registry.json index ca02623..a8168f5 100644 --- a/apps/www/public/r/registry.json +++ b/apps/www/public/r/registry.json @@ -1,35 +1,837 @@ { "$schema": "https://ui.shadcn.com/schema/registry.json", - "name": "usage-ui", - "homepage": "https://usage-ui.vercel.app", + "name": "Registry Starter", + "homepage": "https://registry-starter.vercel.app", "items": [ { - "name": "usage-meter", + "name": "theme", + "type": "registry:theme", + "title": "Nature Theme", + "description": "A nature theme and styles with greens and browns using Tailwind and shadcn/ui variable names", + "cssVars": { + "light": { + "background": "oklch(0.97 0.01 80.72)", + "foreground": "oklch(0.3 0.04 30.2)", + "card": "oklch(0.97 0.01 80.72)", + "card-foreground": "oklch(0.3 0.04 30.2)", + "popover": "oklch(0.97 0.01 80.72)", + "popover-foreground": "oklch(0.3 0.04 30.2)", + "primary": "oklch(0.52 0.13 144.17)", + "primary-foreground": "oklch(1.0 0 0)", + "secondary": "oklch(0.96 0.02 147.64)", + "secondary-foreground": "oklch(0.43 0.12 144.31)", + "muted": "oklch(0.94 0.01 74.42)", + "muted-foreground": "oklch(0.45 0.05 39.21)", + "accent": "oklch(0.9 0.05 146.04)", + "accent-foreground": "oklch(0.43 0.12 144.31)", + "destructive": "oklch(0.54 0.19 26.72)", + "destructive-foreground": "oklch(1.0 0 0)", + "border": "oklch(0.88 0.02 74.64)", + "input": "oklch(0.88 0.02 74.64)", + "ring": "oklch(0.52 0.13 144.17)", + "chart-1": "oklch(0.67 0.16 144.21)", + "chart-2": "oklch(0.58 0.14 144.18)", + "chart-3": "oklch(0.52 0.13 144.17)", + "chart-4": "oklch(0.43 0.12 144.31)", + "chart-5": "oklch(0.22 0.05 145.73)", + "sidebar": "oklch(0.94 0.01 74.42)", + "sidebar-foreground": "oklch(0.3 0.04 30.2)", + "sidebar-primary": "oklch(0.52 0.13 144.17)", + "sidebar-primary-foreground": "oklch(1.0 0 0)", + "sidebar-accent": "oklch(0.9 0.05 146.04)", + "sidebar-accent-foreground": "oklch(0.43 0.12 144.31)", + "sidebar-border": "oklch(0.88 0.02 74.64)", + "sidebar-ring": "oklch(0.52 0.13 144.17)", + "radius": "0.5rem" + }, + "dark": { + "background": "oklch(0.15 0.01 74.42)", + "foreground": "oklch(0.95 0.02 80.72)", + "card": "oklch(0.12 0.01 80.72)", + "card-foreground": "oklch(0.95 0.02 80.72)", + "popover": "oklch(0.12 0.01 80.72)", + "popover-foreground": "oklch(0.95 0.02 80.72)", + "primary": "oklch(0.58 0.15 144.17)", + "primary-foreground": "oklch(0.09 0.01 80.72)", + "secondary": "oklch(0.15 0.02 147.64)", + "secondary-foreground": "oklch(0.65 0.12 144.31)", + "muted": "oklch(0.18 0.01 74.42)", + "muted-foreground": "oklch(0.65 0.03 74.42)", + "accent": "oklch(0.22 0.04 146.04)", + "accent-foreground": "oklch(0.65 0.12 144.31)", + "destructive": "oklch(0.62 0.22 26.72)", + "destructive-foreground": "oklch(0.95 0.02 80.72)", + "border": "oklch(0.22 0.02 74.64)", + "input": "oklch(0.22 0.02 74.64)", + "ring": "oklch(0.58 0.15 144.17)", + "chart-1": "oklch(0.72 0.16 144.21)", + "chart-2": "oklch(0.63 0.14 144.18)", + "chart-3": "oklch(0.58 0.15 144.17)", + "chart-4": "oklch(0.48 0.12 144.31)", + "chart-5": "oklch(0.35 0.08 145.73)", + "sidebar": "oklch(0.15 0.01 74.42)", + "sidebar-foreground": "oklch(0.95 0.02 80.72)", + "sidebar-primary": "oklch(0.58 0.15 144.17)", + "sidebar-primary-foreground": "oklch(0.09 0.01 80.72)", + "sidebar-accent": "oklch(0.22 0.04 146.04)", + "sidebar-accent-foreground": "oklch(0.65 0.12 144.31)", + "sidebar-border": "oklch(0.22 0.02 74.64)", + "sidebar-ring": "oklch(0.58 0.15 144.17)", + "radius": "0.5rem" + } + }, + "dependencies": ["radix-ui"], + "files": [ + { + "path": "src/app/globals.css", + "type": "registry:style", + "target": "app/globals.css" + }, + { + "path": "postcss.config.mjs", + "type": "registry:file", + "target": "postcss.config.mjs" + } + ] + }, + { + "name": "blank", + "type": "registry:block", + "title": "Blank", + "description": "A blank application with all brand components and code", + "registryDependencies": [ + "https://registry-starter.vercel.app/r/brand-header.json", + "https://registry-starter.vercel.app/r/brand-sidebar.json", + "https://registry-starter.vercel.app/r/product-grid.json", + "https://registry-starter.vercel.app/r/theme.json" + ], + "files": [ + { + "path": "src/layouts/minimal-layout.tsx", + "type": "registry:file", + "target": "app/layout.tsx" + }, + { + "path": "src/app/demo/[name]/blocks/blank-page.tsx", + "type": "registry:page", + "target": "app/page.tsx" + } + ] + }, + { + "name": "dashboard", + "type": "registry:block", + "title": "Dashboard", + "description": "A dashboard application with your brand themed components", + "registryDependencies": [ + "https://registry-starter.vercel.app/r/sonner.json", + "https://registry-starter.vercel.app/r/brand-header.json", + "https://registry-starter.vercel.app/r/brand-sidebar.json", + "https://registry-starter.vercel.app/r/theme.json" + ], + "files": [ + { + "path": "src/layouts/shell-layout.tsx", + "type": "registry:file", + "target": "app/layout.tsx" + }, + { + "path": "src/app/demo/[name]/blocks/dashboard-page.tsx", + "type": "registry:page", + "target": "app/page.tsx" + } + ] + }, + { + "name": "store", + "type": "registry:block", + "title": "Store", + "description": "A store application with your brand themed components", + "registryDependencies": [ + "https://registry-starter.vercel.app/r/product-grid.json", + "https://registry-starter.vercel.app/r/theme.json" + ], + "files": [ + { + "path": "src/layouts/minimal-layout.tsx", + "type": "registry:file", + "target": "app/layout.tsx" + }, + { + "path": "src/app/demo/[name]/blocks/store-page.tsx", + "type": "registry:page", + "target": "app/page.tsx" + } + ] + }, + { + "name": "brand-header", + "type": "registry:component", + "title": "Brand Header", + "description": "A styled, simple, reusable header", + "registryDependencies": [ + "button", + "input", + "avatar", + "sidebar", + "https://registry-starter.vercel.app/r/sonner.json", + "https://registry-starter.vercel.app/r/logo.json", + "https://registry-starter.vercel.app/r/theme.json" + ], + "files": [ + { + "path": "src/components/brand-header.tsx", + "type": "registry:component" + }, + { + "path": "src/layouts/shell-layout.tsx", + "type": "registry:file", + "target": "app/layout.tsx" + }, + { + "path": "src/app/demo/[name]/blocks/blank-page.tsx", + "type": "registry:page", + "target": "app/page.tsx" + } + ] + }, + { + "name": "brand-sidebar", + "type": "registry:component", + "title": "Brand Sidebar", + "description": "A styled, simple, reusable sidebar", + "registryDependencies": [ + "badge", + "button", + "sidebar", + "https://registry-starter.vercel.app/r/sonner.json", + "https://registry-starter.vercel.app/r/theme.json" + ], + "files": [ + { + "path": "src/components/brand-sidebar.tsx", + "type": "registry:component" + }, + { + "path": "src/layouts/shell-layout.tsx", + "type": "registry:file", + "target": "app/layout.tsx" + }, + { + "path": "src/app/demo/[name]/blocks/blank-page.tsx", + "type": "registry:page", + "target": "app/page.tsx" + } + ] + }, + { + "name": "login", + "type": "registry:component", + "title": "Login", + "description": "Username & password login section with customer quote.", + "registryDependencies": [ + "badge", + "button", + "https://registry-starter.vercel.app/r/theme.json", + "https://registry-starter.vercel.app/r/logo.json" + ], + "files": [ + { + "path": "src/components/login.tsx", + "type": "registry:component" + }, + { + "path": "src/layouts/minimal-layout.tsx", + "type": "registry:file", + "target": "app/layout.tsx" + }, + { + "path": "src/app/demo/[name]/blocks/blank-page.tsx", + "type": "registry:page", + "target": "app/page.tsx" + } + ] + }, + { + "name": "logo", "type": "registry:component", + "title": "Brand Logo", + "description": "A styled, simple, reusable logo", + "registryDependencies": [ + "https://registry-starter.vercel.app/r/theme.json" + ], + "files": [ + { + "path": "src/components/logo.tsx", + "type": "registry:component" + }, + { + "path": "src/layouts/minimal-layout.tsx", + "type": "registry:file", + "target": "app/layout.tsx" + }, + { + "path": "src/app/demo/[name]/blocks/blank-page.tsx", + "type": "registry:page", + "target": "app/page.tsx" + } + ] + }, + { + "name": "hero", + "type": "registry:component", + "title": "Hero", + "description": "Attention-grabbing section for the top of your landing pages.", + "registryDependencies": [ + "badge", + "button", + "https://registry-starter.vercel.app/r/theme.json" + ], + "files": [ + { + "path": "src/components/hero.tsx", + "type": "registry:component" + }, + { + "path": "src/layouts/minimal-layout.tsx", + "type": "registry:file", + "target": "app/layout.tsx" + }, + { + "path": "src/app/demo/[name]/blocks/blank-page.tsx", + "type": "registry:page", + "target": "app/page.tsx" + } + ] + }, + { + "name": "promo", + "type": "registry:component", + "title": "Promo", + "description": "Attention-grabbing section to display the current promotional deal.", + "registryDependencies": [ + "button", + "https://registry-starter.vercel.app/r/theme.json" + ], + "files": [ + { + "path": "src/components/promo.tsx", + "type": "registry:component" + }, + { + "path": "src/layouts/minimal-layout.tsx", + "type": "registry:file", + "target": "app/layout.tsx" + }, + { + "path": "src/app/demo/[name]/blocks/blank-page.tsx", + "type": "registry:page", + "target": "app/page.tsx" + } + ] + }, + { + "name": "product-grid", + "type": "registry:component", + "title": "Product Grid", + "description": "Product grid displaying all products with API to fetch data", + "registryDependencies": [ + "https://registry-starter.vercel.app/r/theme.json" + ], + "files": [ + { + "path": "src/components/product-grid.tsx", + "type": "registry:component" + }, + { + "path": "src/lib/products.ts", + "type": "registry:lib" + }, + { + "path": "src/layouts/minimal-layout.tsx", + "type": "registry:file", + "target": "app/layout.tsx" + } + ] + }, + { + "name": "accordion", + "type": "registry:ui", + "title": "Accordion", + "description": "A vertically stacked set of interactive headings that each reveal a section of content.", + "registryDependencies": [ + "accordion", + "https://registry-starter.vercel.app/r/theme.json" + ], + "files": [ + { + "path": "src/layouts/minimal-layout.tsx", + "type": "registry:file", + "target": "app/layout.tsx" + } + ] + }, + { + "name": "alert", + "type": "registry:ui", + "title": "Alert", + "description": "Displays a callout for user attention.", + "registryDependencies": [ + "alert", + "https://registry-starter.vercel.app/r/theme.json" + ], + "files": [ + { + "path": "src/layouts/minimal-layout.tsx", + "type": "registry:file", + "target": "app/layout.tsx" + } + ] + }, + { + "name": "avatar", + "type": "registry:ui", + "title": "Avatar", + "description": "An image element with a fallback for representing the user.", + "registryDependencies": [ + "avatar", + "https://registry-starter.vercel.app/r/theme.json" + ], + "files": [ + { + "path": "src/layouts/minimal-layout.tsx", + "type": "registry:file", + "target": "app/layout.tsx" + } + ] + }, + { + "name": "badge", + "type": "registry:ui", + "title": "Badge", + "description": "Displays a small count or status indicator.", + "registryDependencies": [ + "badge", + "https://registry-starter.vercel.app/r/theme.json" + ], + "files": [ + { + "path": "src/layouts/minimal-layout.tsx", + "type": "registry:file", + "target": "app/layout.tsx" + } + ] + }, + { + "name": "breadcrumb", + "type": "registry:ui", + "title": "Breadcrumb", + "description": "Displays the path to the current resource using a hierarchy of links.", + "registryDependencies": [ + "breadcrumb", + "https://registry-starter.vercel.app/r/theme.json" + ], + "files": [ + { + "path": "src/layouts/minimal-layout.tsx", + "type": "registry:file", + "target": "app/layout.tsx" + } + ] + }, + { + "name": "button", + "type": "registry:ui", + "title": "Button", + "description": "Allows users to take actions with a single click or tap.", + "registryDependencies": [ + "button", + "https://registry-starter.vercel.app/r/theme.json" + ], + "files": [ + { + "path": "src/layouts/minimal-layout.tsx", + "type": "registry:file", + "target": "app/layout.tsx" + } + ] + }, + { + "name": "calendar", + "type": "registry:ui", + "title": "Calendar", + "description": "A date field component that allows users to enter and edit date.", + "registryDependencies": [ + "calendar", + "https://registry-starter.vercel.app/r/theme.json" + ], + "files": [ + { + "path": "src/layouts/minimal-layout.tsx", + "type": "registry:file", + "target": "app/layout.tsx" + } + ] + }, + { + "name": "card", + "type": "registry:ui", + "title": "Card", + "description": "Containers for displaying content and actions about a single subject.", + "registryDependencies": [ + "card", + "https://registry-starter.vercel.app/r/theme.json" + ], + "files": [ + { + "path": "src/layouts/minimal-layout.tsx", + "type": "registry:file", + "target": "app/layout.tsx" + } + ] + }, + { + "name": "chart", + "type": "registry:ui", + "title": "Chart", + "description": "Beautiful charts. Built using Recharts. Copy and paste into your apps.", + "registryDependencies": [ + "chart", + "https://registry-starter.vercel.app/r/theme.json" + ], + "files": [ + { + "path": "src/layouts/minimal-layout.tsx", + "type": "registry:file", + "target": "app/layout.tsx" + } + ] + }, + { + "name": "checkbox", + "type": "registry:ui", + "title": "Checkbox", + "description": "Allows users to select multiple items from a list of options.", + "registryDependencies": [ + "checkbox", + "https://registry-starter.vercel.app/r/theme.json" + ], + "files": [ + { + "path": "src/layouts/minimal-layout.tsx", + "type": "registry:file", + "target": "app/layout.tsx" + } + ] + }, + { + "name": "data-table", + "type": "registry:ui", + "title": "Data Table", + "description": "Powerful table and datagrids built using TanStack Table.", + "registryDependencies": [ + "data-table", + "https://registry-starter.vercel.app/r/theme.json" + ], + "files": [ + { + "path": "src/layouts/minimal-layout.tsx", + "type": "registry:file", + "target": "app/layout.tsx" + } + ] + }, + { + "name": "date-picker", + "type": "registry:ui", + "title": "Date Picker", + "description": "Displays the path to the current resource using a hierarchy of links.", + "registryDependencies": [ + "date-picker", + "https://registry-starter.vercel.app/r/theme.json" + ], + "files": [ + { + "path": "src/layouts/minimal-layout.tsx", + "type": "registry:file", + "target": "app/layout.tsx" + } + ] + }, + { + "name": "dialog", + "type": "registry:ui", + "title": "Dialog", + "description": "A modal dialog that interrupts the user with important content.", + "registryDependencies": [ + "dialog", + "https://registry-starter.vercel.app/r/theme.json" + ], + "files": [ + { + "path": "src/layouts/minimal-layout.tsx", + "type": "registry:file", + "target": "app/layout.tsx" + } + ] + }, + { + "name": "dropdown-menu", + "type": "registry:ui", + "title": "Dropdown", + "description": "Displays a menu to the user triggered by a button.", + "registryDependencies": [ + "dropdown-menu", + "https://registry-starter.vercel.app/r/theme.json" + ], + "files": [ + { + "path": "src/layouts/minimal-layout.tsx", + "type": "registry:file", + "target": "app/layout.tsx" + } + ] + }, + { + "name": "input", + "type": "registry:ui", + "title": "Input", + "description": "Displays a form input field or a component that looks like an input field.", + "registryDependencies": [ + "input", + "https://registry-starter.vercel.app/r/theme.json" + ], + "files": [ + { + "path": "src/layouts/minimal-layout.tsx", + "type": "registry:file", + "target": "app/layout.tsx" + } + ] + }, + { + "name": "menu-bar", + "type": "registry:ui", + "title": "Menu Bar", + "description": "A visually persistent menu common in desktop applications that provides quick access to a consistent set of commands.", + "registryDependencies": [ + "menu-bar", + "https://registry-starter.vercel.app/r/theme.json" + ], + "files": [ + { + "path": "src/layouts/minimal-layout.tsx", + "type": "registry:file", + "target": "app/layout.tsx" + } + ] + }, + { + "name": "select", + "type": "registry:ui", + "title": "Select", + "description": "Displays a list of options for the user to pick from—triggered by a button.", + "registryDependencies": [ + "select", + "https://registry-starter.vercel.app/r/theme.json" + ], + "files": [ + { + "path": "src/layouts/minimal-layout.tsx", + "type": "registry:file", + "target": "app/layout.tsx" + } + ] + }, + { + "name": "separator", + "type": "registry:ui", + "title": "Separator", + "description": "Visually or semantically separates content.", + "registryDependencies": [ + "separator", + "https://registry-starter.vercel.app/r/theme.json" + ], + "files": [ + { + "path": "src/layouts/minimal-layout.tsx", + "type": "registry:file", + "target": "app/layout.tsx" + } + ] + }, + { + "name": "skeleton", + "type": "registry:ui", + "title": "Skeleton", + "description": "Use to show a placeholder while content is loading.", + "registryDependencies": [ + "skeleton", + "https://registry-starter.vercel.app/r/theme.json" + ], + "files": [ + { + "path": "src/layouts/minimal-layout.tsx", + "type": "registry:file", + "target": "app/layout.tsx" + } + ] + }, + { + "name": "slider", + "type": "registry:ui", + "title": "Slider", + "description": "An input where the user selects a value from within a given range.", + "registryDependencies": [ + "slider", + "https://registry-starter.vercel.app/r/theme.json" + ], + "files": [ + { + "path": "src/layouts/minimal-layout.tsx", + "type": "registry:file", + "target": "app/layout.tsx" + } + ] + }, + { + "name": "sonner", + "type": "registry:ui", + "title": "Sonner", + "description": "An opinionated toast component for React.", + "registryDependencies": [ + "sonner", + "https://registry-starter.vercel.app/r/theme.json" + ], + "files": [ + { + "path": "src/layouts/minimal-layout.tsx", + "type": "registry:file", + "target": "app/layout.tsx" + }, + { + "path": "src/components/ui/sonner.tsx", + "type": "registry:file", + "target": "components/ui/sonner.tsx" + } + ] + }, + { + "name": "switch", + "type": "registry:ui", + "title": "Switch", + "description": "A control that allows the user to toggle between checked and not checked.", + "registryDependencies": [ + "switch", + "https://registry-starter.vercel.app/r/theme.json" + ], + "files": [ + { + "path": "src/layouts/minimal-layout.tsx", + "type": "registry:file", + "target": "app/layout.tsx" + } + ] + }, + { + "name": "table", + "type": "registry:ui", + "title": "Table", + "description": "A responsive table component.", + "registryDependencies": [ + "table", + "https://registry-starter.vercel.app/r/theme.json" + ], + "files": [ + { + "path": "src/layouts/minimal-layout.tsx", + "type": "registry:file", + "target": "app/layout.tsx" + } + ] + }, + { + "name": "tabs", + "type": "registry:ui", + "title": "Tabs", + "description": "A set of layered sections of content—known as tab panels—that are displayed one at a time.", + "registryDependencies": [ + "tabs", + "https://registry-starter.vercel.app/r/theme.json" + ], + "files": [ + { + "path": "src/layouts/minimal-layout.tsx", + "type": "registry:file", + "target": "app/layout.tsx" + } + ] + }, + { + "name": "toggle-group", + "type": "registry:ui", + "title": "Toggle Group", + "description": "A popup that displays information related to an element when the element receives keyboard focus or the mouse hovers over it.", + "registryDependencies": [ + "toggle-group", + "https://registry-starter.vercel.app/r/theme.json" + ], + "files": [ + { + "path": "src/layouts/minimal-layout.tsx", + "type": "registry:file", + "target": "app/layout.tsx" + } + ] + }, + { + "name": "tooltip", + "type": "registry:ui", + "title": "Tooltip", + "description": "A popup that displays information related to an element when the element receives keyboard focus or the mouse hovers over it.", + "registryDependencies": [ + "tooltip", + "https://registry-starter.vercel.app/r/theme.json" + ], + "files": [ + { + "path": "src/layouts/minimal-layout.tsx", + "type": "registry:file", + "target": "app/layout.tsx" + } + ] + }, + { + "name": "usage-meter", + "type": "registry:ui", "title": "Usage Meter", "description": "A linear progress meter for displaying usage/quota information with full Radix accessibility support.", - "dependencies": ["radix-ui"], - "registryDependencies": [], + "registryDependencies": [ + "https://usage-ui.vercel.app/r/usage-meter.json", + "https://registry-starter.vercel.app/r/theme.json" + ], "files": [ { - "path": "src/components/registry/usage-meter/usage-meter.tsx", - "type": "registry:component", - "target": "components/ui/usage-meter.tsx" + "path": "src/layouts/minimal-layout.tsx", + "type": "registry:file", + "target": "app/layout.tsx" } ] }, { "name": "usage-meter-base", - "type": "registry:component", + "type": "registry:ui", "title": "Usage Meter (Base)", "description": "A lightweight linear progress meter for displaying usage/quota information without Radix dependency.", - "dependencies": [], - "registryDependencies": [], + "registryDependencies": [ + "https://usage-ui.vercel.app/r/usage-meter-base.json", + "https://registry-starter.vercel.app/r/theme.json" + ], "files": [ { - "path": "src/components/registry/usage-meter/usage-meter-base.tsx", - "type": "registry:component", - "target": "components/ui/usage-meter-base.tsx" + "path": "src/layouts/minimal-layout.tsx", + "type": "registry:file", + "target": "app/layout.tsx" } ] } diff --git a/apps/www/public/r/usage-meter-base.json b/apps/www/public/r/usage-meter-base.json index 994ba2f..cc8dc4c 100644 --- a/apps/www/public/r/usage-meter-base.json +++ b/apps/www/public/r/usage-meter-base.json @@ -3,15 +3,17 @@ "name": "usage-meter-base", "title": "Usage Meter (Base)", "description": "A lightweight linear progress meter for displaying usage/quota information without Radix dependency.", - "dependencies": [], - "registryDependencies": [], + "registryDependencies": [ + "https://usage-ui.vercel.app/r/usage-meter-base.json", + "https://registry-starter.vercel.app/r/theme.json" + ], "files": [ { - "path": "src/components/registry/usage-meter/usage-meter-base.tsx", - "content": "\"use client\";\n\nimport * as React from \"react\";\n\nimport { cn } from \"@/lib/utils\";\n\nconst meterVariants = {\n default: \"bg-primary\",\n success: \"bg-chart-2\",\n warning: \"bg-chart-4\",\n danger: \"bg-destructive\",\n} as const;\n\nconst meterSizes = {\n sm: \"h-2\",\n default: \"h-3\",\n lg: \"h-4\",\n} as const;\n\ninterface UsageMeterBaseProps extends React.HTMLAttributes {\n /** Current value (required) */\n value: number;\n /** Maximum value (default: 100) */\n max?: number;\n /** Visual variant */\n variant?: keyof typeof meterVariants;\n /** Size variant */\n size?: keyof typeof meterSizes;\n /** Optional label text */\n label?: string;\n /** Show percentage (default: true) */\n showPercentage?: boolean;\n}\n\nconst UsageMeterBase = React.forwardRef(\n (\n {\n className,\n value,\n max = 100,\n variant = \"default\",\n size = \"default\",\n label,\n showPercentage = true,\n ...props\n },\n ref,\n ) => {\n // Guard against max being 0 or negative to avoid NaN/Infinity\n const percentage = max > 0 ? Math.round((value / max) * 100) : 0;\n const clampedPercentage = Math.min(100, Math.max(0, percentage));\n\n return (\n
\n {(label || showPercentage) && (\n \n {label && (\n \n {label}\n \n )}\n {showPercentage && (\n \n {clampedPercentage}%\n \n )}\n
\n )}\n \n \n \n \n );\n },\n);\nUsageMeterBase.displayName = \"UsageMeterBase\";\n\nexport {\n UsageMeterBase,\n meterVariants as meterBaseVariants,\n meterSizes as meterBaseSizes,\n};\nexport type { UsageMeterBaseProps };\n", - "type": "registry:component", - "target": "components/ui/usage-meter-base.tsx" + "path": "src/layouts/minimal-layout.tsx", + "content": "import { Geist, Geist_Mono, Montserrat } from \"next/font/google\";\nimport React, { type ReactNode } from \"react\";\n\nimport { cn } from \"@/lib/utils\";\n\nimport \"@/app/globals.css\";\n\nconst GeistSans = Geist({\n subsets: [\"latin\"],\n variable: \"--font-sans\",\n});\n\nconst GeistMono = Geist_Mono({\n subsets: [\"latin\"],\n variable: \"--font-mono\",\n});\n\nconst MontserratSerif = Montserrat({\n subsets: [\"latin\"],\n variable: \"--font-serif\",\n});\n\nexport default function RootLayout({\n children,\n}: Readonly<{\n children: ReactNode;\n}>) {\n return (\n \n \n
\n
{children}
\n
\n \n \n );\n}\n", + "type": "registry:file", + "target": "app/layout.tsx" } ], - "type": "registry:component" + "type": "registry:ui" } \ No newline at end of file diff --git a/apps/www/public/r/usage-meter.json b/apps/www/public/r/usage-meter.json index ef1dfd6..975bda5 100644 --- a/apps/www/public/r/usage-meter.json +++ b/apps/www/public/r/usage-meter.json @@ -3,17 +3,17 @@ "name": "usage-meter", "title": "Usage Meter", "description": "A linear progress meter for displaying usage/quota information with full Radix accessibility support.", - "dependencies": [ - "radix-ui" + "registryDependencies": [ + "https://usage-ui.vercel.app/r/usage-meter.json", + "https://registry-starter.vercel.app/r/theme.json" ], - "registryDependencies": [], "files": [ { - "path": "src/components/registry/usage-meter/usage-meter.tsx", - "content": "\"use client\";\n\nimport { Progress as ProgressPrimitive } from \"radix-ui\";\nimport * as React from \"react\";\n\nimport { cn } from \"@/lib/utils\";\n\nconst meterVariants = {\n default: \"bg-primary\",\n success: \"bg-chart-2\",\n warning: \"bg-chart-4\",\n danger: \"bg-destructive\",\n} as const;\n\nconst meterSizes = {\n sm: \"h-2\",\n default: \"h-3\",\n lg: \"h-4\",\n} as const;\n\ninterface UsageMeterProps\n extends Omit<\n React.ComponentProps,\n \"value\" | \"max\"\n > {\n /** Current value (required) */\n value: number;\n /** Maximum value (default: 100) */\n max?: number;\n /** Visual variant */\n variant?: keyof typeof meterVariants;\n /** Size variant */\n size?: keyof typeof meterSizes;\n /** Optional label text */\n label?: string;\n /** Show percentage (default: true) */\n showPercentage?: boolean;\n}\n\nconst UsageMeter = React.forwardRef<\n React.ComponentRef,\n UsageMeterProps\n>(\n (\n {\n className,\n value,\n max = 100,\n variant = \"default\",\n size = \"default\",\n label,\n showPercentage = true,\n ...props\n },\n ref,\n ) => {\n // Guard against max <= 0 to prevent division by zero and invalid progress state\n const safeMax = Math.max(1, max);\n const percentage = Math.round((value / safeMax) * 100);\n const clampedPercentage = Math.min(100, Math.max(0, percentage));\n\n return (\n
\n {(label || showPercentage) && (\n \n {label && (\n \n {label}\n \n )}\n {showPercentage && (\n \n {clampedPercentage}%\n \n )}\n
\n )}\n \n \n \n \n );\n },\n);\nUsageMeter.displayName = \"UsageMeter\";\n\nexport { UsageMeter, meterVariants, meterSizes };\nexport type { UsageMeterProps };\n", - "type": "registry:component", - "target": "components/ui/usage-meter.tsx" + "path": "src/layouts/minimal-layout.tsx", + "content": "import { Geist, Geist_Mono, Montserrat } from \"next/font/google\";\nimport React, { type ReactNode } from \"react\";\n\nimport { cn } from \"@/lib/utils\";\n\nimport \"@/app/globals.css\";\n\nconst GeistSans = Geist({\n subsets: [\"latin\"],\n variable: \"--font-sans\",\n});\n\nconst GeistMono = Geist_Mono({\n subsets: [\"latin\"],\n variable: \"--font-mono\",\n});\n\nconst MontserratSerif = Montserrat({\n subsets: [\"latin\"],\n variable: \"--font-serif\",\n});\n\nexport default function RootLayout({\n children,\n}: Readonly<{\n children: ReactNode;\n}>) {\n return (\n \n \n
\n
{children}
\n
\n \n \n );\n}\n", + "type": "registry:file", + "target": "app/layout.tsx" } ], - "type": "registry:component" + "type": "registry:ui" } \ No newline at end of file diff --git a/apps/www/src/app/(registry)/layout.tsx b/apps/www/src/app/(registry)/layout.tsx index a7479d5..0c5bd0c 100644 --- a/apps/www/src/app/(registry)/layout.tsx +++ b/apps/www/src/app/(registry)/layout.tsx @@ -13,11 +13,29 @@ export default function RegistryLayout({ children: ReactNode; }>) { return ( - - - -
{children}
- -
+
+ + + + {/* Content wrapper - matches shadcn docs pattern */} +
+
+
{children}
+
+
+ +
+
); } diff --git a/apps/www/src/app/(registry)/page.tsx b/apps/www/src/app/(registry)/page.tsx index 210cfbd..734d0ab 100644 --- a/apps/www/src/app/(registry)/page.tsx +++ b/apps/www/src/app/(registry)/page.tsx @@ -1,203 +1,99 @@ -import { ArrowRight, Blocks, Component, ToyBrick } from "lucide-react"; import Link from "next/link"; -import { MCPTabs } from "@/components/registry/mcp-tabs"; -import { - Card, - CardContent, - CardDescription, - CardHeader, - CardTitle, -} from "@/components/ui/card"; -import { getBlocks, getComponents, getUIPrimitives } from "@/lib/registry"; - -const uiItems = getUIPrimitives().slice(0, 5); -const componentItems = getComponents().slice(0, 5); -const blockItems = getBlocks().slice(0, 5); - export default function Home() { return ( -
-
-
-

- Registry + <> + {/* Top spacing matching shadcn pattern */} +
+ +
+
+

+ Introduction

-

- Distribute your design system tokens, custom components, hooks, - pages, and other files to any React project. +

+ A shadcn/ui component registry for usage meters, quota indicators, + and resource consumption visualizations.

-
- - - -
- Blocks -
- -
-
- - Pre-built blocks kits for consistent, repeatable generations - -
- -
- {blockItems.map((item) => ( -
- - {item.title} - - -
- ))} -
-
-
+
+

+ + This is not a component library. + {" "} + It's a collection of copy-paste components you can use to build your + own usage and quota visualization system. +

-
- - -
- UI Primitives -
- -
-
- - Reusable UI primitives to build your components - -
+

+ Components are distributed via the shadcn CLI. You install them into + your project and own the code completely. Customize, extend, or + modify them however you need. +

- -
- {uiItems.map((item) => ( -
- - {item.title} - - -
- ))} -
-
-
+
+ + npx shadcn add https://usage-ui.vercel.app/r/usage-meter.json + +
- - -
- Components -
- -
-
- - Compound components using common patterns - -
+

+ Open Code +

+

+ Every component's source code is open for modification. You have + full control to customize and extend components to fit your design + system. No wrapper components, no style overrides—just edit the code + directly. +

- -
- {componentItems.map((item) => ( -
- - {item.title} - - -
- ))} -
-
-
-
+

+ AI-Ready +

+

+ Integrate this registry with AI IDEs using{" "} + + Model Context Protocol (MCP) + + . The open code and consistent API make it easy for AI models to + read, understand, and generate new components. +

-
-
-

MCP

-

- Integrate this registry with AI IDEs using Model Context Protocol - (MCP) using the following configuration. This utilizes this - Registry's theme tokens and CSS variables with the Shadcn CLI. To - ensure this works, double check that the{" "} - - - style:theme - +

+ Getting Started +

+

+ Browse the sidebar to explore available components. Start with a{" "} + + blank block {" "} - contains the same colors as your{" "} - tokens.css + for a clean starting point, or pick individual components like the{" "} + + usage meter + {" "} + to add specific functionality.

- -
-
- -
-
-

About

-

- This registry serves as a central repository for all UI components - and blocks used in your applications. It helps maintain consistency - across your products and speeds up development by providing - ready-to-use components. Each component and block is documented with - examples. You can browse components by category, search for specific - components, and view examples of how they are used in different - contexts. -

-

- To get begin, start with a block{" "} - - (like the{" "} - - blank block - - ) - {" "} - and click the Open in v0 button. - You can also open individual UI primitives or components in v0 if - you want a smaller or more specific starting point. +

+ Each component page includes a live preview, installation + instructions, and API documentation.

+
-

- - - - - GitHub Repository - -

+
+ Built with shadcn/ui + + Get Started → +
-

+ ); } diff --git a/apps/www/src/app/(registry)/tokens/page.tsx b/apps/www/src/app/(registry)/tokens/page.tsx index 1a08796..c5be3f5 100644 --- a/apps/www/src/app/(registry)/tokens/page.tsx +++ b/apps/www/src/app/(registry)/tokens/page.tsx @@ -10,165 +10,188 @@ import { ColorBlock } from "./color-block"; export default function TokensPage() { return ( -
-
- + <> + {/* Top spacing */} +
-

- Design Tokens -

-

- A comprehensive overview of all color tokens used in the design system -

-
+
+
+ - {/* Fonts */} -
-

Fonts

-

- The core font families that define the primary typography. -

-
- - - +

+ Design Tokens +

+

+ A comprehensive overview of all color tokens used in the design + system +

-
- {/* Primary Theme */} -
-

Primary Theme

-

- The core colors that define the primary theme and overall look of the - interface. -

-
- - - - -
-
+ {/* Fonts */} +
+

Fonts

+

+ The core font families that define the primary typography. +

+
+ + + +
+
- {/* Secondary & Accent */} -
-

Secondary & Accent

-

- Colors used for secondary elements and accents throughout the - interface. -

-
- - - - -
-
+ {/* Primary Theme */} +
+

+ Primary Theme +

+

+ The core colors that define the primary theme and overall look of + the interface. +

+
+ + + + +
+
- {/* UI Component */} -
-

UI Component

-

- Colors used for specific UI components like cards, popovers, and muted - elements. -

-
- - - - - - -
-
+ {/* Secondary & Accent */} +
+

+ Secondary & Accent +

+

+ Colors used for secondary elements and accents throughout the + interface. +

+
+ + + + +
+
- {/* Utility & Form */} -
-

Utility & Form

-

- Colors used for borders, inputs, and other utility elements. -

-
- - - -
-
+ {/* UI Component */} +
+

+ UI Component +

+

+ Colors used for specific UI components like cards, popovers, and + muted elements. +

+
+ + + + + + +
+
- {/* Status & Feedback */} -
-

Status & Feedback

-

- Colors used to indicate different states and provide feedback to - users. -

-
- - -
-
+ {/* Utility & Form */} +
+

+ Utility & Form +

+

+ Colors used for borders, inputs, and other utility elements. +

+
+ + + +
+
- {/* Chart & Visualization */} -
-

Chart & Visualization

-

- Colors used for data visualization and charts. -

-
- - - - - -
-
+ {/* Status & Feedback */} +
+

+ Status & Feedback +

+

+ Colors used to indicate different states and provide feedback to + users. +

+
+ + +
+
- {/* Sidebar & Navigation */} -
-

Sidebar & Navigation

-

- Colors specific to the sidebar and navigation components. -

-
- - - - - - - - -
-
-
+ {/* Chart & Visualization */} +
+

+ Chart & Visualization +

+

+ Colors used for data visualization and charts. +

+
+ + + + + +
+
+ + {/* Sidebar & Navigation */} +
+

+ Sidebar & Navigation +

+

+ Colors specific to the sidebar and navigation components. +

+
+ + + + + + + + +
+
+
+ ); } diff --git a/apps/www/src/app/docs/layout.tsx b/apps/www/src/app/docs/layout.tsx index caf3b6b..949d6c6 100644 --- a/apps/www/src/app/docs/layout.tsx +++ b/apps/www/src/app/docs/layout.tsx @@ -13,13 +13,35 @@ export default function DocsLayout({ children: ReactNode; }>) { return ( - - - -
-
{children}
-
- -
+
+ + + + {/* Content wrapper - matches shadcn docs pattern */} +
+
+
+ {/* Top spacing */} +
+
+ {children} +
+
+
+
+ +
+
); } diff --git a/apps/www/src/components/registry/registry-sidebar.tsx b/apps/www/src/components/registry/registry-sidebar.tsx index eac9ca9..456ed86 100644 --- a/apps/www/src/components/registry/registry-sidebar.tsx +++ b/apps/www/src/components/registry/registry-sidebar.tsx @@ -1,29 +1,12 @@ "use client"; -import { - Blocks, - ChevronDown, - Component, - Home, - Menu, - Search, - ToyBrick, - X, -} from "lucide-react"; +import { Menu, X } from "lucide-react"; import Link from "next/link"; import { usePathname } from "next/navigation"; -import * as React from "react"; import { RegistryLogo } from "@/components/registry/registry-logo"; import { ModeToggle } from "@/components/registry/theme-toggle"; import { Button } from "@/components/ui/button"; -import { - Collapsible, - CollapsibleContent, - CollapsibleTrigger, -} from "@/components/ui/collapsible"; -import { Input } from "@/components/ui/input"; -import { ScrollArea } from "@/components/ui/scroll-area"; import { Sidebar, SidebarContent, @@ -37,73 +20,18 @@ import { SidebarMenuItem, useSidebar, } from "@/components/ui/sidebar"; -import { Skeleton } from "@/components/ui/skeleton"; import { getBlocks, getComponents, getUIPrimitives } from "@/lib/registry"; +import { cn } from "@/lib/utils"; const uiItems = getUIPrimitives(); const componentItems = getComponents(); const blockItems = getBlocks(); export const gettingStartedItems = [ - { title: "Home", path: "/" }, + { title: "Introduction", path: "/" }, { title: "Design Tokens", path: "/tokens" }, ]; -// Skeleton fallback for SSR to prevent hydration mismatch with Radix components -function SidebarSkeleton() { - return ( -
- {/* Getting Started */} -
-
- - -
-
- - -
-
- {/* Blocks */} -
-
- - -
-
- - - -
-
- {/* Components */} -
-
- - -
-
- - - -
-
- {/* UI Primitives */} -
-
- - -
-
- - - -
-
-
- ); -} - export function MobileSidebarTrigger() { const { setOpenMobile } = useSidebar(); @@ -116,51 +44,27 @@ export function MobileSidebarTrigger() { ); } +// Gradient fade component for top/bottom of sidebar +function SidebarFade({ position }: { position: "top" | "bottom" }) { + return ( +
+ ); +} + export function RegistrySidebar() { const pathname = usePathname(); - const { setOpenMobile } = useSidebar(); - // Track mount state to prevent hydration mismatch. - // Radix Collapsible components use useId for accessibility attributes, - // which can generate different IDs on server vs client due to streaming. - const [mounted, setMounted] = React.useState(false); - const [searchTerm, setSearchTerm] = React.useState(""); - const [filteredUiItems, setFilteredUiItems] = React.useState(uiItems); - const [filteredComponents, setFilteredComponents] = - React.useState(componentItems); - const [filteredBlocks, setFilteredBlocks] = React.useState(blockItems); - - React.useEffect(() => { - setMounted(true); - }, []); - - React.useEffect(() => { - if (searchTerm) { - setFilteredUiItems( - uiItems.filter((item) => - item.title.toLowerCase().includes(searchTerm.toLowerCase()), - ), - ); - setFilteredComponents( - componentItems.filter((item) => - item.title.toLowerCase().includes(searchTerm.toLowerCase()), - ), - ); - setFilteredBlocks( - blockItems.filter((item) => - item.title.toLowerCase().includes(searchTerm.toLowerCase()), - ), - ); - } else { - setFilteredUiItems(uiItems); - setFilteredComponents(componentItems); - setFilteredBlocks(blockItems); - } - }, [searchTerm]); - return ( - +
@@ -175,180 +79,122 @@ export function RegistrySidebar() {
-
-
- - setSearchTerm(e.target.value)} - /> -
-
- - - {!mounted ? ( - // Show skeleton during SSR to prevent hydration mismatch - - ) : ( - <> - - - - -
- - - Getting Started - -
- -
-
- - - - - {gettingStartedItems.map((item) => ( - - - setOpenMobile(false)} - href={item.path} - > - {item.title} - - - - ))} - - - -
-
- - - - - -
- - - Blocks - -
- -
-
- - - - - {filteredBlocks.map((item) => ( - - - setOpenMobile(false)} - href={`/docs/${item.name}`} - > - {item.title} - - - - ))} - - - -
-
- - - - - -
- - - Components - -
- -
-
- - - - - {filteredComponents.map((item) => ( - - - setOpenMobile(false)} - href={`/docs/${item.name}`} - > - {item.title} - - - - ))} - - - -
-
- - - - - -
- - - UI Primitives - -
- -
-
- - - - {filteredUiItems.map((item) => ( - - - setOpenMobile(false)} - href={`/docs/${item.name}`} - > - {item.title} - - - - ))} - - - -
-
- - )} -
+ + {/* Top gradient fade */} + + + {/* Subtle vertical border line */} +
+ + + + Getting Started + + + + {gettingStartedItems.map((item) => ( + + + setOpenMobile(false)} href={item.path}> + {item.title} + + + + ))} + + + + + + + Blocks + + + + {blockItems.map((item) => ( + + + setOpenMobile(false)} + href={`/docs/${item.name}`} + > + {item.title} + + + + ))} + + + + + + + Components + + + + {componentItems.map((item) => ( + + + setOpenMobile(false)} + href={`/docs/${item.name}`} + > + {item.title} + + + + ))} + + + + + + + UI Primitives + + + + {uiItems.map((item) => ( + + + setOpenMobile(false)} + href={`/docs/${item.name}`} + > + {item.title} + + + + ))} + + + + + {/* Bottom gradient fade */} +