App Catalog
A rounded, ruled app grid with pastel icons and a large editorial introduction.
App Store
The tools to run your business.
Manage jobs, customers, invoices and more in one place.
- JobsPlan the week and track work in progress.
- CustomersKeep contacts, history and details together.
- QuotesBuild an estimate and send it for approval.
- InvoicesSend invoices and track outstanding payments.
- BookkeepingSee income, expenses and account balances.
- BookingsManage appointments and team availability.
- TasksAssign work and see what’s next.
- InventoryTrack stock levels and reorder supplies.
- NotesCapture meeting notes and decisions.
'use client';
import { useId, type CSSProperties, type ReactNode } from 'react';
import './app-catalog.css';
export interface AppCatalogItem {
id: string;
title: string;
description: string;
icon: ReactNode;
color: string;
iconColor?: string;
href?: string;
}
export interface AppCatalogProps {
items: AppCatalogItem[];
eyebrow?: string;
title?: ReactNode;
description?: ReactNode;
onItemSelect?: (id: string) => void;
className?: string;
}
/** A ruled app directory. Supply icons as ordinary SVG, images, or React nodes. */
export function AppCatalog({ items, eyebrow = 'App Store', title = 'The tools to run your business.', description, onItemSelect, className = '' }: AppCatalogProps) {
const uid = useId();
return <section className={`ac-app-catalog ${className}`.trim()} aria-labelledby={`${uid}-title`}>
<div className="ac-app-catalog__layout">
<header className="ac-app-catalog__intro">
{eyebrow && <p className="ac-app-catalog__eyebrow">{eyebrow}</p>}
<h2 id={`${uid}-title`}>{title}</h2>
{description && <p className="ac-app-catalog__description">{description}</p>}
</header>
<ul className="ac-app-catalog__grid">
{items.map(item => {
const content = <><span className="ac-app-catalog__icon" style={{ background: item.color, color: item.iconColor ?? '#24251f' } as CSSProperties} aria-hidden="true">{item.icon}</span><span className="ac-app-catalog__copy"><span className="ac-app-catalog__name">{item.title}</span><span className="ac-app-catalog__detail">{item.description}</span></span></>;
return <li key={item.id}>{item.href ? <a className="ac-app-catalog__item" href={item.href} onClick={() => onItemSelect?.(item.id)}>{content}</a> : onItemSelect ? <button type="button" className="ac-app-catalog__item" onClick={() => onItemSelect(item.id)}>{content}</button> : <div className="ac-app-catalog__item">{content}</div>}</li>;
})}
</ul>
</div>
</section>;
}
Installation
Run this in your shadcn-configured React project. The component and stylesheet are added together.
npx shadcn@latest add http://localhost:5173/r/app-catalog.jsonThis command uses the current site address. For localhost, keep this preview running on the same computer. Files follow your configured components alias.
Usage
Import the component and make the details your own.
import { AppCatalog } from '@/components/anticraft/AppCatalog';
export function Example() {
return <AppCatalog
title="The tools to run your business."
description="Manage jobs, customers, invoices and more in one place."
items={[
{ id: 'jobs', title: 'Jobs', description: 'Plan the week and track the work.', color: '#87b376', iconColor: '#081f00', icon: <span>▥</span>, href: '/apps/jobs' },
{ id: 'customers', title: 'Customers', description: 'Keep customer details in one place.', color: '#f4a594', iconColor: '#4c1b11', icon: <span>▣</span>, href: '/apps/customers' },
{ id: 'quotes', title: 'Quotes', description: 'Prepare and share a quote.', color: '#e7d39e', iconColor: '#4e3c00', icon: <span>▤</span>, href: '/apps/quotes' },
]}
/>;
}Props
| Prop | Type | Default | Description |
|---|---|---|---|
items | AppCatalogItem[] | Required | Unique id, title, description, ReactNode icon and background color; optional iconColor and href. Supply your own icon assets. |
eyebrow | string | 'App Store' | Small label above the introduction. Empty text hides it. |
title | ReactNode | The tools to run your business. | Large heading beside the grid; wraps naturally with balanced text. |
description | ReactNode | undefined | Supporting copy below the heading. |
onItemSelect | (id: string) => void | undefined | Adds native buttons to items without href. Items with href remain normal links and also call the callback. |
className | string | '' | Additional classes. Customize --ac-app-surface, --ac-app-ink, --ac-app-muted, --ac-app-line, --ac-app-hover and --ac-app-font. |
Motion & accessibility
Hover gently lifts the icon and changes the cell background. Optional actions use native links or buttons. The introduction stacks below a 980px component width; the grid uses two columns below 600px. Reduced motion removes transitions. Neutral surfaces follow the page theme; icon colors stay fixed.
Use semantic content and meaningful labels in your composition. Check contrast and keyboard behavior with your own styles and content.
Inspiration
The ruled app grid, logo ribbons and opposing activity columns take their visual structure from this reference. Demo copy is independent; artwork credits are in the repository.
Visit PlyxlThe linked site is the design or content reference. See the repository for demo artwork credits.