For AI Agents
This page is the canonical entry point for AI assistants (Claude Code, Cursor, Copilot, custom agents) that need to write code against @ngrok/mantle.
TL;DR for the agent
Machine-readable entry points
Suggested system-prompt snippet
Drop this near the top of an agent's system prompt when working in a mantle codebase:
Conventions agents should know
- Polymorphism via
asChild. Most components acceptasChild(powered bySlot). Use it to render the component as a different element (for example, render aButtonas areact-routerLink) without losing the component's styling or behavior. - Flat import paths, categorized docs URLs. Components import from
@ngrok/mantle/<name>(e.g.@ngrok/mantle/button). The docs URL's category segment (/components/actions/button) never appears in import paths. Hooks live at@ngrok/mantle/hooks(e.g.import { useBreakpoint } from "@ngrok/mantle/hooks"). Utilities live at their own subpaths:@ngrok/mantle/cx,@ngrok/mantle/color, etc. - Docs slug ≠ import subpath (sometimes). Most components import from a subpath that matches their docs slug (e.g.,
Buttonfrom@ngrok/mantle/button). A few are exported from a parent's subpath:IconButtonfrom@ngrok/mantle/button,PasswordInputfrom@ngrok/mantle/input,ProgressBarandProgressDonutfrom@ngrok/mantle/progress. The structured manifest at/api/components.jsonis the source of truth; itsimportPathfield is always correct. cxover string interpolation. Compose class names withcx. It merges Tailwind classes and de-duplicates conflicts.- Nullish checks use
== null/!= null. The codebase prefers these over=== undefined/!== undefined, since== nullcovers bothnullandundefined. - Theme + provider setup. Apps must mount
ThemeProviderandToasterat the root. AddTooltipProvideronce when using shared tooltip delay or hover settings. WithoutThemeProvider, theme tokens are unstyled and there's a flash of unstyled content. - Stylesheet setup. The app's CSS entry imports
@ngrok/mantle/mantle.cssand then@ngrok/mantle/source-all.css(see Stylesheet).mantle.cssdeclares no@source, and Tailwind 4 skipsnode_moduleswhen it scans for class names, so withoutsource-all.cssTailwind generates none of the classes mantle's components use. The components render without their styles. An openDialogshows its content in the page flow with no backdrop. Import both from the same CSS file: Tailwind compiles each CSS entry on its own, so an@sourcein a separate file adds nothing. To trim the production stylesheet, addmantleSourcesPlugin()from@ngrok/mantle-vite-pluginsnext totailwindcss()invite.config.ts. The CSS file does not change. - Phosphor icons by default. Import icons from
@phosphor-icons/react/<IconName>(e.g.@phosphor-icons/react/Fire). Custom ngrok icons live at@ngrok/mantle/icons. - No
ascasts in app code. Use proper null checks and type narrowing. Type assertions are restricted toas constand dedicated type guards (see Conventions). - Translation-safe markup. Google Translate reparents every text node, so React throws
NotFoundErrorand the page goes blank when it inserts an element before one. Never render a conditional element immediately before bare text children: wrap the text in a<span>, move the element after it, or mount it unconditionally. Pass aButtonorBadgeicon through theiconprop, never as a child. Mark code, keys, filenames, IDs, and passcodestranslate="no";Code,Kbd,CodeBlock.Code, andOtpInput.Slotalready do. The attribute classifies content and is never the fix for a crash: when a shape throws, fix the structure, and keep prose translatable. - Exact-pinned versions. When adding a dependency, pin it. No
^or~ranges. - Tailwind 4. Custom classes use the design tokens defined in
mantle.css. Prefer semantic tokens (text-strong,bg-form,border-accent-600) over raw color names where they exist. Color scales use Tailwind's50–950steps (text-accent-600,bg-neutral-100); there is no1–12scale, sotext-accent-11matches nothing. The text color tokens are on Typography and the palettes on Colors.
Composition patterns
A few high-leverage compositions agents commonly need:
Accessibility-first defaults
Every mantle component is built on semantic HTML and tested ARIA patterns. Agents should:
- Render real form controls (
Input,Checkbox,RadioGroup); never style a<div>with click handlers as a button. - Pair every form control with a
Label. - Use
SkipToMainLink+Mainat the top of the document tree. - Set accessible names for icon-only buttons via the required
labelprop onIconButton. - See the Accessibility page for a full keyboard / ARIA reference per component.
When to ask vs. when to act
- If the user asks for "a primary button," generate a
Buttonwithappearance="filled" intent="neutral", the system's default primary action.ButtonandIconButtonrequire explicitappearanceandintent; there are no defaults. The two take different tone unions:Buttontakes"accent","danger", or"neutral". Useintent="neutral"when no tone is implied. Reserveintent="accent"for deliberate brand emphasis: an action that should carry the accent color.IconButtontakes"neutral"alone. Reach forButtonwhere the tone carries meaning.
- If choosing between
Tooltip,Popover, andHoverCard, follow the JSDoc guidance:Tooltipfor short label hints,Popoverfor interactive content,HoverCardfor non-essential preview cards. - If choosing between
DataTableandTable:DataTablefor dynamic, sortable, filterable, paginated tabular data;Tablefor static layout. - When uncertain, prefer to fetch the relevant
/<page>.mdand read the full doc rather than guessing.
Versioning
@ngrok/mantle is 0.x, so stock semver does not apply. There is no major: a breaking change ships as a minor, and an additive or internal change ships as a patch. Read the Changelog before a minor bump, and run the matching migration guide when one exists. When working in a project, check package.json for the pinned version and prefer the docs at the matching tag on GitHub if you need to pin behavior to that version.