feat(web): enhance style guide with left nav and all components

- Add sticky left-hand navigation with scroll-spy for active section
- Add 20+ missing UI components to style guide showcase
- Include Accordion, Breadcrumbs, Drawer, Modal, Pagination, etc.
- Add state components: EmptyState, ErrorState, LoadingState
- Remove Playground link from header and mobile menu
- Comment out Architecture Diagram on homepage temporarily
This commit is contained in:
Ajax Davis 2026-01-16 11:04:08 +10:00
parent 76955bde18
commit 7483e697fc
6 changed files with 569 additions and 131 deletions

View file

@ -4,6 +4,7 @@ import { Button } from '@tpmjs/ui/Button/Button';
import { Container } from '@tpmjs/ui/Container/Container';
import Link from 'next/link';
import { AppHeader } from '../components/AppHeader';
// import { ArchitectureDiagramWrapper } from '../components/home/ArchitectureDiagramWrapper';
import { HeroSection } from '../components/home/HeroSection';
export const dynamic = 'force-dynamic';
@ -83,6 +84,22 @@ export default async function HomePage(): Promise<React.ReactElement> {
{/* Hero Section - Dithered Design */}
<HeroSection stats={data.stats} />
{/* Architecture Diagram Section - temporarily disabled
<section className="py-16 bg-background border-b border-border">
<Container size="xl" padding="lg">
<div className="text-center mb-10">
<h2 className="text-3xl md:text-4xl font-bold mb-4 text-foreground">
How It Works
</h2>
<p className="text-lg text-foreground-secondary max-w-2xl mx-auto">
Publish once to npm, reach every AI agent. Your tool goes live in minutes.
</p>
</div>
<ArchitectureDiagramWrapper />
</Container>
</section>
*/}
{/* Featured Tools Section */}
<section className="py-16 bg-background">
<Container size="xl" padding="lg">

View file

@ -2,11 +2,10 @@
import { Badge } from '@tpmjs/ui/Badge/Badge';
import { Container } from '@tpmjs/ui/Container/Container';
import { useState } from 'react';
import { useEffect, useState } from 'react';
import { AppHeader } from '~/components/AppHeader';
import {
FieldsetSection,
NavItem,
SectionA11yChecklists,
SectionAccessibility,
SectionColors,
@ -30,74 +29,182 @@ import {
SectionTypography,
} from '~/components/style-guide';
interface NavSection {
title: string;
items: { id: string; label: string }[];
}
const navSections: NavSection[] = [
{
title: 'Foundations',
items: [
{ id: 'principles', label: 'Design Principles' },
{ id: 'colors', label: 'Color System' },
{ id: 'typography', label: 'Typography' },
{ id: 'spacing', label: 'Spacing' },
{ id: 'motion', label: 'Motion' },
],
},
{
title: 'Systems',
items: [
{ id: 'accessibility', label: 'Accessibility' },
{ id: 'layout', label: 'Layout' },
{ id: 'content', label: 'Content' },
{ id: 'data-viz', label: 'Data Visualization' },
{ id: 'icons', label: 'Iconography' },
],
},
{
title: 'Implementation',
items: [
{ id: 'theming', label: 'Theming' },
{ id: 'components', label: 'Components' },
{ id: 'api', label: 'Component APIs' },
],
},
{
title: 'Patterns',
items: [
{ id: 'nav-patterns', label: 'Navigation' },
{ id: 'form-patterns', label: 'Forms' },
{ id: 'feedback-patterns', label: 'Feedback' },
{ id: 'table-patterns', label: 'Tables' },
{ id: 'search-patterns', label: 'Search & Filtering' },
],
},
{
title: 'Governance',
items: [
{ id: 'a11y-checklists', label: 'A11y Checklists' },
{ id: 'content-guidelines', label: 'Content & Writing' },
{ id: 'icon-system', label: 'Icon System' },
],
},
];
export default function StyleGuidePage(): React.ReactElement {
const [activeTab, setActiveTab] = useState('all');
const [radioValue, setRadioValue] = useState('option1');
const [counterValue, setCounterValue] = useState(1234);
const [density, setDensity] = useState<'compact' | 'comfortable' | 'spacious'>('comfortable');
const [activeSection, setActiveSection] = useState('principles');
// Track active section on scroll
useEffect(() => {
const handleScroll = () => {
const sections = navSections.flatMap((s) => s.items.map((i) => i.id));
for (const id of sections) {
const element = document.getElementById(id);
if (element) {
const rect = element.getBoundingClientRect();
if (rect.top <= 120 && rect.bottom > 120) {
setActiveSection(id);
break;
}
}
}
};
window.addEventListener('scroll', handleScroll, { passive: true });
return () => window.removeEventListener('scroll', handleScroll);
}, []);
const scrollToSection = (id: string) => {
const element = document.getElementById(id);
if (element) {
const offset = 80;
const elementPosition = element.getBoundingClientRect().top + window.scrollY;
window.scrollTo({ top: elementPosition - offset, behavior: 'smooth' });
}
};
return (
<div className="min-h-screen flex flex-col bg-background">
<AppHeader />
<main className="flex-1 py-24">
<div className="flex-1 flex">
{/* Left Sidebar Navigation */}
<aside className="hidden lg:block w-64 flex-shrink-0">
<nav className="sticky top-16 h-[calc(100vh-4rem)] overflow-y-auto py-8 px-4 border-r border-border">
<div className="mb-6">
<h2 className="font-mono text-xs text-foreground-tertiary uppercase tracking-wider mb-2">
Design System
</h2>
<div className="flex gap-2">
<Badge variant="default" size="sm">v2.0</Badge>
<Badge variant="outline" size="sm">wcag aa</Badge>
</div>
</div>
{navSections.map((section) => (
<div key={section.title} className="mb-6">
<h3 className="font-mono text-xs text-foreground-tertiary uppercase tracking-wider mb-2">
{section.title}
</h3>
<ul className="space-y-1">
{section.items.map((item) => (
<li key={item.id}>
<button
onClick={() => scrollToSection(item.id)}
className={`block w-full text-left px-3 py-1.5 text-sm rounded-md transition-colors ${
activeSection === item.id
? 'bg-primary/10 text-primary font-medium'
: 'text-foreground-secondary hover:text-foreground hover:bg-surface'
}`}
>
{item.label}
</button>
</li>
))}
</ul>
</div>
))}
</nav>
</aside>
{/* Main Content */}
<main className="flex-1 min-w-0 py-12">
<Container size="lg" padding="lg">
{/* Hero Section */}
<div className="mb-24">
<div className="mb-16">
<h1 className="font-mono text-4xl md:text-5xl lg:text-6xl font-semibold mb-6 text-foreground lowercase tracking-tight">
tpmjs design system
</h1>
<p className="font-sans text-lg text-foreground-secondary max-w-2xl leading-relaxed mb-8">
<p className="font-sans text-lg text-foreground-secondary max-w-2xl leading-relaxed mb-6">
The comprehensive design system for TPMJS. This guide covers design principles,
foundations, components, and governance rules that scale across teams, AI agents,
and external contributors.
</p>
<div className="flex flex-wrap gap-4">
<div className="flex flex-wrap gap-3 lg:hidden">
<Badge variant="default">v2.0</Badge>
<Badge variant="outline">wcag aa</Badge>
<Badge variant="outline">dark mode</Badge>
</div>
</div>
{/* Table of Contents */}
<FieldsetSection title="table of contents" id="toc">
<div className="grid grid-cols-1 md:grid-cols-4 gap-8">
<div className="space-y-2">
<p className="font-mono text-xs text-foreground-tertiary uppercase tracking-wide mb-3">foundations</p>
<NavItem href="#principles">1. design principles</NavItem>
<NavItem href="#colors">2. color system</NavItem>
<NavItem href="#typography">3. typography</NavItem>
<NavItem href="#spacing">4. spacing</NavItem>
<NavItem href="#motion">5. motion</NavItem>
</div>
<div className="space-y-2">
<p className="font-mono text-xs text-foreground-tertiary uppercase tracking-wide mb-3">systems</p>
<NavItem href="#accessibility">6. accessibility</NavItem>
<NavItem href="#layout">7. layout &amp; responsiveness</NavItem>
<NavItem href="#content">8. content guidelines</NavItem>
<NavItem href="#data-viz">9. data visualization</NavItem>
<NavItem href="#icons">10. iconography</NavItem>
</div>
<div className="space-y-2">
<p className="font-mono text-xs text-foreground-tertiary uppercase tracking-wide mb-3">implementation</p>
<NavItem href="#theming">11. theming</NavItem>
<NavItem href="#components">12. components</NavItem>
<NavItem href="#api">13. component apis</NavItem>
</div>
<div className="space-y-2">
<p className="font-mono text-xs text-foreground-tertiary uppercase tracking-wide mb-3">patterns</p>
<NavItem href="#nav-patterns">14. navigation</NavItem>
<NavItem href="#form-patterns">15. forms</NavItem>
<NavItem href="#feedback-patterns">16. feedback</NavItem>
<NavItem href="#table-patterns">17. tables</NavItem>
<NavItem href="#search-patterns">18. search &amp; filtering</NavItem>
</div>
<div className="space-y-2">
<p className="font-mono text-xs text-foreground-tertiary uppercase tracking-wide mb-3">governance</p>
<NavItem href="#a11y-checklists">19. a11y checklists</NavItem>
<NavItem href="#content-guidelines">20. content &amp; writing</NavItem>
<NavItem href="#icon-system">21. icon system</NavItem>
{/* Mobile Table of Contents */}
<FieldsetSection title="table of contents" id="toc" className="lg:hidden">
<div className="grid grid-cols-2 md:grid-cols-3 gap-4">
{navSections.map((section) => (
<div key={section.title}>
<p className="font-mono text-xs text-foreground-tertiary uppercase tracking-wide mb-2">
{section.title}
</p>
<ul className="space-y-1">
{section.items.map((item) => (
<li key={item.id}>
<button
onClick={() => scrollToSection(item.id)}
className="text-sm text-foreground-secondary hover:text-primary transition-colors"
>
{item.label}
</button>
</li>
))}
</ul>
</div>
))}
</div>
</FieldsetSection>
@ -149,5 +256,6 @@ export default function StyleGuidePage(): React.ReactElement {
</Container>
</main>
</div>
</div>
);
}

View file

@ -171,6 +171,7 @@ const resourceItems: DropdownItem[] = [
{ href: '/faq', label: 'FAQ', description: 'Common questions' },
{ href: '/changelog', label: 'Changelog', description: 'Latest updates' },
{ href: '/stats', label: 'Stats', description: 'Platform metrics' },
{ href: '/style-guide', label: 'Style Guide', description: 'UI components' },
];
/**
@ -217,11 +218,6 @@ export function AppHeader(): React.ReactElement {
Agents
</Button>
</Link>
<a href="https://playground.tpmjs.com" target="_blank" rel="noopener noreferrer">
<Button variant="ghost" size="sm" className="text-foreground hover:text-foreground">
Playground
</Button>
</a>
{/* Separator */}
<span className="text-foreground-tertiary mx-2">|</span>

View file

@ -24,12 +24,6 @@ const navSections: NavSection[] = [
{ href: '/tool/tool-search', label: 'Tools', description: 'Browse all tools' },
{ href: '/collections', label: 'Collections', description: 'Discover curated tool sets' },
{ href: '/agents', label: 'Agents', description: 'AI agents with tools' },
{
href: 'https://playground.tpmjs.com',
label: 'Playground',
description: 'Try tools live',
external: true,
},
],
},
{
@ -50,6 +44,7 @@ const navSections: NavSection[] = [
{ href: '/faq', label: 'FAQ', description: 'Common questions' },
{ href: '/changelog', label: 'Changelog', description: 'Latest updates' },
{ href: '/stats', label: 'Stats', description: 'Platform metrics' },
{ href: '/style-guide', label: 'Style Guide', description: 'UI components' },
],
},
];

View file

@ -1,6 +1,13 @@
'use client';
import {
Accordion,
AccordionContent,
AccordionItem,
AccordionTrigger,
} from '@tpmjs/ui/Accordion/Accordion';
import { Badge } from '@tpmjs/ui/Badge/Badge';
import { Breadcrumbs } from '@tpmjs/ui/Breadcrumbs/Breadcrumbs';
import { Button } from '@tpmjs/ui/Button/Button';
import {
Card,
@ -12,15 +19,35 @@ import {
} from '@tpmjs/ui/Card/Card';
import { Checkbox } from '@tpmjs/ui/Checkbox/Checkbox';
import { CodeBlock } from '@tpmjs/ui/CodeBlock/CodeBlock';
import { Drawer, DrawerContent, DrawerTrigger } from '@tpmjs/ui/Drawer/Drawer';
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from '@tpmjs/ui/DropdownMenu/DropdownMenu';
import { EmptyState } from '@tpmjs/ui/EmptyState/EmptyState';
import { ErrorState } from '@tpmjs/ui/ErrorState/ErrorState';
import { FormField } from '@tpmjs/ui/FormField/FormField';
import { Icon } from '@tpmjs/ui/Icon/Icon';
import { Input } from '@tpmjs/ui/Input/Input';
import { InstallSnippet } from '@tpmjs/ui/InstallSnippet/InstallSnippet';
import { LoadingState } from '@tpmjs/ui/LoadingState/LoadingState';
import { Modal, ModalContent, ModalTrigger } from '@tpmjs/ui/Modal/Modal';
import { PageHeader } from '@tpmjs/ui/PageHeader/PageHeader';
import { Pagination } from '@tpmjs/ui/Pagination/Pagination';
import { Popover, PopoverContent, PopoverTrigger } from '@tpmjs/ui/Popover/Popover';
import { ProgressBar } from '@tpmjs/ui/ProgressBar/ProgressBar';
import { QualityScore } from '@tpmjs/ui/QualityScore/QualityScore';
import { Radio } from '@tpmjs/ui/Radio/Radio';
import { RadioGroup } from '@tpmjs/ui/Radio/RadioGroup';
import { Select } from '@tpmjs/ui/Select/Select';
import { Skeleton } from '@tpmjs/ui/Skeleton/Skeleton';
import { Slider } from '@tpmjs/ui/Slider/Slider';
import { Spinner } from '@tpmjs/ui/Spinner/Spinner';
import { StatCard } from '@tpmjs/ui/StatCard/StatCard';
import { Switch } from '@tpmjs/ui/Switch/Switch';
import { Tabs } from '@tpmjs/ui/Tabs/Tabs';
import {
Table,
TableBody,
@ -30,7 +57,11 @@ import {
TableHeader,
TableRow,
} from '@tpmjs/ui/Table/Table';
import { Tabs } from '@tpmjs/ui/Tabs/Tabs';
import { Textarea } from '@tpmjs/ui/Textarea/Textarea';
import { Toast, ToastProvider } from '@tpmjs/ui/Toast/Toast';
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@tpmjs/ui/Tooltip/Tooltip';
import { useState } from 'react';
import { FieldsetSection, SubSection } from './shared';
interface SectionComponentsProps {
@ -44,8 +75,12 @@ export function SectionComponents({
activeTab,
onTabChange,
radioValue,
onRadioChange
onRadioChange,
}: SectionComponentsProps): React.ReactElement {
const [sliderValue, setSliderValue] = useState(50);
const [currentPage, setCurrentPage] = useState(1);
const [showToast, setShowToast] = useState(false);
return (
<FieldsetSection title="12. components" id="components">
<p className="text-foreground-secondary mb-8 font-sans prose-width">
@ -74,7 +109,9 @@ export function SectionComponents({
<Button size="sm">small</Button>
<Button size="md">medium</Button>
<Button size="lg">large</Button>
<Button size="icon"><Icon icon="plus" size="sm" /></Button>
<Button size="icon">
<Icon icon="plus" size="sm" />
</Button>
</div>
</div>
<div>
@ -117,7 +154,8 @@ export function SectionComponents({
{/* Card */}
<SubSection title="card">
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
{(['default', 'elevated', 'outline', 'blueprint', 'featured', 'brutalist'] as const).map((variant) => (
{(['default', 'elevated', 'outline', 'blueprint', 'featured', 'brutalist'] as const).map(
(variant) => (
<Card key={variant} variant={variant}>
<CardHeader>
<CardTitle>{variant} card</CardTitle>
@ -130,7 +168,49 @@ export function SectionComponents({
<Button size="sm">action</Button>
</CardFooter>
</Card>
))}
)
)}
</div>
</SubSection>
{/* Accordion */}
<SubSection title="accordion">
<Accordion type="single" collapsible className="w-full max-w-lg">
<AccordionItem value="item-1">
<AccordionTrigger>What is TPMJS?</AccordionTrigger>
<AccordionContent>
TPMJS is a tool package manager for JavaScript that helps you discover, publish, and
use AI tools.
</AccordionContent>
</AccordionItem>
<AccordionItem value="item-2">
<AccordionTrigger>How do I publish a tool?</AccordionTrigger>
<AccordionContent>
Add the tpmjs keyword to your package.json and publish to npm. Your tool will be
automatically discovered.
</AccordionContent>
</AccordionItem>
<AccordionItem value="item-3">
<AccordionTrigger>Is it free to use?</AccordionTrigger>
<AccordionContent>
Yes, TPMJS is free for open source tools. Enterprise features are available for
private registries.
</AccordionContent>
</AccordionItem>
</Accordion>
</SubSection>
{/* Breadcrumbs */}
<SubSection title="breadcrumbs">
<div className="space-y-4">
<Breadcrumbs
items={[
{ label: 'Home', href: '/' },
{ label: 'Tools', href: '/tools' },
{ label: 'Category', href: '/tools/category' },
{ label: 'Current Page' },
]}
/>
</div>
</SubSection>
@ -189,6 +269,36 @@ export function SectionComponents({
</div>
</SubSection>
{/* Slider */}
<SubSection title="slider">
<div className="max-w-md space-y-4">
<Slider value={sliderValue} onChange={setSliderValue} min={0} max={100} step={1} />
<p className="font-mono text-xs text-foreground-secondary">Value: {sliderValue}</p>
</div>
</SubSection>
{/* Progress Bar */}
<SubSection title="progress bar">
<div className="space-y-6 max-w-md">
<div>
<p className="font-mono text-xs text-foreground-secondary mb-2">25%</p>
<ProgressBar value={25} />
</div>
<div>
<p className="font-mono text-xs text-foreground-secondary mb-2">50%</p>
<ProgressBar value={50} />
</div>
<div>
<p className="font-mono text-xs text-foreground-secondary mb-2">75%</p>
<ProgressBar value={75} />
</div>
<div>
<p className="font-mono text-xs text-foreground-secondary mb-2">100%</p>
<ProgressBar value={100} />
</div>
</div>
</SubSection>
{/* Tabs */}
<SubSection title="tabs">
<div className="space-y-6">
@ -240,6 +350,100 @@ export function SectionComponents({
</Table>
</SubSection>
{/* Pagination */}
<SubSection title="pagination">
<Pagination
currentPage={currentPage}
totalPages={10}
onPageChange={setCurrentPage}
/>
</SubSection>
{/* Dropdown Menu */}
<SubSection title="dropdown menu">
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="outline">
Open Menu <Icon icon="chevronDown" size="sm" className="ml-2" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent>
<DropdownMenuItem>Profile</DropdownMenuItem>
<DropdownMenuItem>Settings</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem>Logout</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</SubSection>
{/* Popover */}
<SubSection title="popover">
<Popover>
<PopoverTrigger asChild>
<Button variant="outline">Open Popover</Button>
</PopoverTrigger>
<PopoverContent>
<div className="space-y-2">
<h4 className="font-medium text-foreground">Popover Title</h4>
<p className="text-sm text-foreground-secondary">
This is the popover content. It can contain any elements.
</p>
</div>
</PopoverContent>
</Popover>
</SubSection>
{/* Tooltip */}
<SubSection title="tooltip">
<TooltipProvider>
<div className="flex gap-4">
<Tooltip>
<TooltipTrigger asChild>
<Button variant="outline">Hover me</Button>
</TooltipTrigger>
<TooltipContent>
<p>This is a tooltip</p>
</TooltipContent>
</Tooltip>
</div>
</TooltipProvider>
</SubSection>
{/* Modal */}
<SubSection title="modal">
<Modal>
<ModalTrigger asChild>
<Button>Open Modal</Button>
</ModalTrigger>
<ModalContent title="Modal Title" description="This is a modal dialog.">
<div className="py-4">
<p className="text-foreground-secondary">Modal content goes here.</p>
</div>
<div className="flex justify-end gap-2">
<Button variant="outline">Cancel</Button>
<Button>Confirm</Button>
</div>
</ModalContent>
</Modal>
</SubSection>
{/* Drawer */}
<SubSection title="drawer">
<Drawer>
<DrawerTrigger asChild>
<Button variant="outline">Open Drawer</Button>
</DrawerTrigger>
<DrawerContent>
<div className="p-6">
<h3 className="text-lg font-semibold mb-4">Drawer Content</h3>
<p className="text-foreground-secondary">
This is the drawer content. It slides in from the bottom.
</p>
</div>
</DrawerContent>
</Drawer>
</SubSection>
{/* Spinner */}
<SubSection title="spinner">
<div className="flex flex-wrap gap-12 items-center">
@ -266,6 +470,122 @@ export function SectionComponents({
</div>
</SubSection>
{/* Skeleton */}
<SubSection title="skeleton">
<div className="space-y-4 max-w-md">
<Skeleton className="h-4 w-full" />
<Skeleton className="h-4 w-3/4" />
<Skeleton className="h-4 w-1/2" />
<div className="flex items-center gap-4 mt-6">
<Skeleton className="h-12 w-12 rounded-full" />
<div className="space-y-2 flex-1">
<Skeleton className="h-4 w-full" />
<Skeleton className="h-4 w-2/3" />
</div>
</div>
</div>
</SubSection>
{/* State Components */}
<SubSection title="state components">
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
<div className="border border-dashed border-border p-4">
<p className="font-mono text-xs text-foreground-secondary mb-4">empty state</p>
<EmptyState
icon="inbox"
title="No items yet"
description="Get started by creating your first item."
action={<Button size="sm">Create Item</Button>}
/>
</div>
<div className="border border-dashed border-border p-4">
<p className="font-mono text-xs text-foreground-secondary mb-4">error state</p>
<ErrorState
title="Something went wrong"
message="Failed to load data. Please try again."
onRetry={() => {}}
/>
</div>
<div className="border border-dashed border-border p-4">
<p className="font-mono text-xs text-foreground-secondary mb-4">loading state</p>
<LoadingState message="Loading data..." />
</div>
</div>
</SubSection>
{/* Page Header */}
<SubSection title="page header">
<div className="border border-dashed border-border p-4">
<PageHeader
title="Page Title"
description="This is a description of the page content."
actions={
<div className="flex gap-2">
<Button variant="outline" size="sm">
Secondary
</Button>
<Button size="sm">Primary</Button>
</div>
}
/>
</div>
</SubSection>
{/* Stat Card */}
<SubSection title="stat card">
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
<StatCard title="Total Tools" value="1,234" change="+12%" changeType="positive" />
<StatCard title="Downloads" value="45.2K" change="-3%" changeType="negative" />
<StatCard title="Users" value="892" change="0%" changeType="neutral" />
</div>
</SubSection>
{/* Quality Score */}
<SubSection title="quality score">
<div className="flex flex-wrap gap-8 items-center">
<div className="text-center">
<QualityScore score={0.95} size="lg" />
<p className="font-mono text-xs text-foreground-secondary mt-2">excellent</p>
</div>
<div className="text-center">
<QualityScore score={0.75} size="lg" />
<p className="font-mono text-xs text-foreground-secondary mt-2">good</p>
</div>
<div className="text-center">
<QualityScore score={0.5} size="lg" />
<p className="font-mono text-xs text-foreground-secondary mt-2">average</p>
</div>
<div className="text-center">
<QualityScore score={0.25} size="lg" />
<p className="font-mono text-xs text-foreground-secondary mt-2">poor</p>
</div>
</div>
</SubSection>
{/* Install Snippet */}
<SubSection title="install snippet">
<InstallSnippet packageName="@tpmjs/example-tool" />
</SubSection>
{/* Toast */}
<SubSection title="toast">
<ToastProvider>
<div className="space-y-4">
<Button onClick={() => setShowToast(true)}>Show Toast</Button>
<Toast
open={showToast}
onClose={() => setShowToast(false)}
title="Success!"
description="Your action was completed successfully."
variant="success"
/>
<p className="font-mono text-xs text-foreground-secondary">
Toast variants: default, success, error, warning, info
</p>
</div>
</ToastProvider>
</SubSection>
{/* Code Block */}
<SubSection title="code block">
<CodeBlock

View file

@ -9,13 +9,15 @@ export function FieldsetSection({
title,
children,
id,
className = '',
}: {
title: string;
children: React.ReactNode;
id?: string;
className?: string;
}): React.ReactElement {
return (
<fieldset id={id} className="border border-dashed border-border p-8 mb-16 scroll-mt-24">
<fieldset id={id} className={`border border-dashed border-border p-8 mb-16 scroll-mt-24 ${className}`}>
<legend className="font-mono text-sm text-foreground-secondary px-3 lowercase">
{title}
</legend>