Resources / Code snippets
Code snippets
The entry points the package publishes, and the shapes that come up across components rather than inside one. Reach for this when you know what you are building and want the import line or the composition rather than a component's own page.
| Import | Contents |
|---|---|
| @growth-protocol-ai/alpha | Every component, its prop types and its variants function, plus cn() — no icons |
| @growth-protocol-ai/alpha/accordion | Accordion, its parts, its variants function and its prop types |
| @growth-protocol-ai/alpha/button | Button, its variants function and its prop types |
| @growth-protocol-ai/alpha/canvas | Canvas and its prop types — the arrangement, composed and narrowed |
| @growth-protocol-ai/alpha/card | Card, its parts, its variants function and its prop types |
| @growth-protocol-ai/alpha/checkbox | Checkbox, its variants function and its prop types |
| @growth-protocol-ai/alpha/confirmation-modal | ConfirmationModal and its prop types — the arrangement, composed and narrowed |
| @growth-protocol-ai/alpha/dropdown | Dropdown, its variants function and its prop types |
| @growth-protocol-ai/alpha/empty-state | EmptyState, its parts, its variants function and its prop types |
| @growth-protocol-ai/alpha/error-view | ErrorView and its prop types — the arrangement, composed and narrowed |
| @growth-protocol-ai/alpha/icons | Every icon component, the IconName union and AlphaIcon |
| @growth-protocol-ai/alpha/llms-full.txt | The same documentation inline, for a tool that cannot follow links |
| @growth-protocol-ai/alpha/llms.txt | The agent-context index the LLM context page describes |
| @growth-protocol-ai/alpha/markdown | Markdown, its variants function and its prop types |
| @growth-protocol-ai/alpha/modal | Modal, its parts, its variants function and its prop types |
| @growth-protocol-ai/alpha/pagination | Pagination, its variants function and its prop types |
| @growth-protocol-ai/alpha/popover | Popover, its variants function and its prop types |
| @growth-protocol-ai/alpha/radio | Radio, its variants function and its prop types |
| @growth-protocol-ai/alpha/registry | COMPONENT_REGISTRY, MODULE_REGISTRY and iconMeta — runtime-pure, so it is safe on a server |
| @growth-protocol-ai/alpha/search | Search, its variants function and its prop types |
| @growth-protocol-ai/alpha/skeleton | Skeleton, its variants function and its prop types |
| @growth-protocol-ai/alpha/spinner | Spinner, its variants function and its prop types |
| @growth-protocol-ai/alpha/styles.css | The compiled stylesheet: tokens and every component |
| @growth-protocol-ai/alpha/tabs | Tabs, its parts, its variants function and its prop types |
| @growth-protocol-ai/alpha/tag | Tag, its variants function and its prop types |
| @growth-protocol-ai/alpha/text-area | TextArea, its variants function and its prop types |
| @growth-protocol-ai/alpha/text-input | TextInput, its variants function and its prop types |
| @growth-protocol-ai/alpha/toggle | Toggle, its variants function and its prop types |
| @growth-protocol-ai/alpha/tokens.css | The design tokens alone |
| @growth-protocol-ai/alpha/tooltip | Tooltip, its variants function and its prop types |
Read from the package's own exports map, so this is what the installed version actually resolves. There is no wildcard subpath, and nothing under @growth-protocol-ai/alpha/dist/… is public.
The compositions that come up across components rather than inside one. What a single component takes is on its own page; this is the rest — an element handed to a slot, a control that is really a link, a contextual override through data-slot.
// A button that is really a link
<Button asChild variant="outlined" emphasis="ghost">
<a href="/pricing">See pricing</a>
</Button>
// A quiet action beside a loud one
<Button variant="secondary">Cancel</Button>
<Button variant="primary" emphasis="depth" size="large">Continue</Button>
// An icon in a slot, and a button that is only an icon. Icons come from the
// /icons subpath, and a slot takes the element rather than the component.
import { DotsHorizontalIcon, PlusIcon } from '@growth-protocol-ai/alpha/icons';
<Button iconLeft={<PlusIcon />}>Add member</Button>
<Button iconOnly aria-label="More actions"><DotsHorizontalIcon /></Button>
// A pending action that cannot be double-submitted
<Button loading={isSaving} onClick={save}>Save</Button>
// Compose class names
import { cn } from '@growth-protocol-ai/alpha';
<Button className={cn('my-utility', isWide && 'is-wide')}>Go</Button>
// A decision. title names the dialog and shows in its head.
<Modal title="Delete this project?" open={open} onOpenChange={setOpen}>
<ModalBody>
<p>Everything in it goes too.</p>
</ModalBody>
<ModalFooter><Button variant="danger">Delete</Button></ModalFooter>
</Modal>
// A controlled selection. options carry the list; value is one of their values.
const MARKETS = [{ value: 'pt', label: 'Portugal' }, { value: 'se', label: 'Sweden' }];
<Dropdown
options={MARKETS}
value={market}
onValueChange={setMarket}
placeholder="Market"
/>
// Uncontrolled, no search. Narrower than the 240px default comes from the
// container's own rule, below — not from a style prop.
<Dropdown
options={PAGE_SIZES}
defaultValue="25"
emphasis="ghost"
searchable={false}
aria-label="Rows per page"
/>
// A search. onSearch is Enter; onChange is every keystroke.
<Search
label="Search portfolios"
placeholder="Search portfolios, stories, reports…"
value={query}
onChange={(e) => setQuery(e.target.value)}
onSearch={run}
/>
// Cmd/K is a convention, not a prop — Alpha ships no global shortcut,
// because a component cannot know which search on the page is the primary one.
const search = useRef<HTMLInputElement>(null);
useEffect(() => {
const focus = (e: KeyboardEvent) => {
if (e.key === 'k' && (e.metaKey || e.ctrlKey)) {
e.preventDefault();
search.current?.focus();
}
};
document.addEventListener('keydown', focus);
return () => document.removeEventListener('keydown', focus);
}, []);
<Search ref={search} rounding="pill" size="large" label="Site search" />
// In-content section switching. Hold the tabs outside the render so the row is
// not measuring a new array every time the page re-renders.
const SECTIONS = [
{ value: 'overview', label: 'Overview' },
{ value: 'activity', label: 'Activity' },
{ value: 'archive', label: 'Archive', disabled: true },
];
<Tabs
aria-label="Project views"
items={SECTIONS}
value={section}
onValueChange={setSection}
/>
// A card that is a link. href makes the root an <a>, which is what turns on
// the cursor, the hover and the focus ring. Slots go in one order.
<Card status="critical" href="/kpis/supplier-risk" data-card-id="supplier-risk">
<CardHead>Supplier risk</CardHead>
<CardValue delta="↑ from 6.4 last week">7.2</CardValue>
<CardBody>3rd APAC geopolitical event this month.</CardBody>
<CardFooter>Review 5 insights</CardFooter>
</Card>
// Under asChild the href is on your element, so say it is interactive.
<Card asChild interactive status="ok">
<Link href="/kpis/on-time-delivery">
<CardHead>On-time delivery</CardHead>
<CardValue delta="target 95%">91.4%</CardValue>
</Link>
</Card>
// Tiles go inside something that already has a surface — here, a card.
<Card>
<CardHead>Recommended action</CardHead>
{items.map((item) => (
<Card key={item.id} variant="tile" status={item.status}>
<CardHead>{item.name}</CardHead>
<CardBody>{item.body}</CardBody>
</Card>
))}
</Card>
// The card as a container for a table: no padding, clipped to the radius.
<Card variant="table">
<CardToolbar>
<Dropdown options={RANGES} defaultValue="30d" aria-label="Date range" />
</CardToolbar>
<table>…</table>
</Card>
// A tag's icon decides its element: none is a <span>, add and remove are
// <button>s. So this is the whole difference between a label and an affordance.
<Tag variant="outlined" size="xsmall">Draft</Tag>
<Tag icon="remove" variant="secondary" aria-label="Remove APAC" onClick={drop}>
APAC
</Tag>
// An empty container. The action is yours to pass; only title is required.
<EmptyState
icon={<LayoutIcon />}
title="No portfolios yet"
description="A portfolio groups the trends you want to track together."
filled
>
<Button emphasis="ghost">Create a portfolio</Button>
<EmptyStateHint href="/docs/portfolios">How portfolios work</EmptyStateHint>
</EmptyState>
// A coming-soon surface. placeholder, never vacant — vacant says the reader
// can add something now, and a feature that does not exist cannot be filled.
<EmptyState type="placeholder" title="Workflows will come here" />
// Filling a region needs a flex-column parent. Never a vh height: it cannot
// know the height of the chrome above it, so it clips or overflows.
.results { display: flex; flex-direction: column; flex: 1 1 auto; }
<EmptyState fill filled title="No portfolios match that search" />
// A failure. onRetry present draws the button; absent draws none.
<ErrorView
title="Error while loading portfolios"
description="Something isn't right. This is usually temporary."
detail={`request ${requestId}`}
onRetry={refetch}
/>
// A failure a retry cannot clear — so it is not offered.
<ErrorView
tone="unavailable"
title="Trend data is too sparse"
description="Try a more general search term."
/>
// A contextual override — data-slot is the one supported selector
.my-toolbar [data-slot='button'] { gap: 6px; }
.my-toolbar [data-slot='dropdown'] { width: 150px; }
.kpi-grid [data-slot='card'] { grid-column: span 2; }