npx skills add ...
npx skills add zard-ui/zardui --skill zard
Manages zard/ui components and projects — adding, composing, styling, and debugging Angular UI built on TailwindCSS v4. Provides project context, component source, and the real API instead of a remembered one. Applies when working with zard/ui, zard-cli, the zard registry, or any project with a components.json that declares a zard projectType. Also triggers for "zard-cli init", "add a zard component", or "set up zard/ui in an Nx workspace".
npx skills add zard-ui/zardui --skill zard
An Angular component library. Components are installed as source code into the user's project by the CLI — there is no runtime package to import from, and no component to npm install.
IMPORTANT: Run every CLI command with the project's own package runner:
npx zard-cli,pnpm dlx zard-cli,yarn zard-cli, orbunx zard-cli— pick the one matchingpackageManagerincomponents.json. The examples below usenpx zard-cli; substitute the right runner.
Read components.json at the project root before doing anything else. It is written by zard-cli init and is the whole configuration — there is no zard-cli info command to call.
No components.json means the project has not been initialised — run init rather than writing component files by hand.
A components.json that exists but whose installed component source uses a renamed selector or an old file layout (core/css/tailwind.css instead of core/css/zard.css, a bare ::-webkit-scrollbar block in the global CSS, a button-group "divider" instead of "separator", a toast folder instead of sonner) belongs to a project predating the current registry. That is a job for the migration skill, which names every renamed selector precisely — not for guessing which shape is current.
For anything the file does not answer — what exists, what a component's API is, what it depends on — read the registry or use the MCP server. Never reconstruct a component API from memory.
aliases.components → the import prefix for every component. Use the actual value; never hardcode @/shared/components. The prefix can be anything (@app/..., ~/...) — it is mapped in tsconfig.json, or tsconfig.base.json in an Nx workspace.aliases.blocks → the import prefix a block (add dashboard-01, add login-01) is written under. Absent from a components.json written before blocks were installable — a re-run of init adds it.baseUrl → the source root the aliases resolve against. Components are written under it.projectType → angular, angular-library, nx, nx-library, or analog. Decides which tsconfig holds the aliases, where Tailwind is configured, and whether an app.config.ts exists at all. See cli.md.appConfigFile → where provideZard() is registered. Empty in a library — there the consuming app registers it.tailwind.css → the global CSS file holding the theme tokens. Always edit this file; never create a second one.tailwind.baseColor → the theme preset: neutral, stone, zinc, gray, or slate.icons → the icon family the components are written with (lucide today). Decides the @ng-icons/* package and the symbol names. See rules/icons.md.rtl → declares layout direction intent. It does not change what gets installed.packageManager → use it for every dependency install (pnpm add date-fns, not npm install date-fns) and to pick the CLI runner.registryUrl → optional. Present when the project installs from a registry other than https://zardui.com/r. See registry.md.zard-cli add has written it. Check the components directory first; do not import what is not there.zType="outline", zSize="sm" — not a class that re-styles the component into the same thing.bg-primary, text-muted-foreground. Never bg-blue-500, never a dark: colour override.OnPush, input(), z-prefixed inputs. See rules/angular.md.Always enforced. Each links to a file with Incorrect/Correct pairs.
imports, ChangeDetectionStrategy.OnPush, ViewEncapsulation.None. No NgModules, no Default change detection.input(), model(), output(). No @Input() / @Output() decorators in new code.z-<name> and/or [z-<name>]. Some components are element-only, some are attributes on a native tag (input[z-input], button[z-button]). Use the one the component declares.Zard<Name>Imports array, not the individual classes one by one.apps/web/src/generated/** and public/r/*.json are build artefacts.bg-primary not bg-blue-600.class is for layout, not for restyling. It is merged last and wins — which is exactly why it should not be used to override the component's own colours.mergeClasses(), not string concatenation. It is twMerge(clsx(...)); plain interpolation loses the conflict resolution.space-x-* / space-y-*. Use flex with gap-*.size-* when width and height match. size-4, not w-4 h-4.dark: colour overrides. The tokens already switch.tailwind.config.js; the theme lives in CSS.scroll-fade needs an overflow container; shimmer is text-only. Both are pure-CSS utilities from the core item.z-card wants z-card-header / z-card-title / z-card-content / z-card-footer, not everything dumped into content.z-select-item inside z-select-group.z-dialog / z-sheet / z-alert-dialog / z-drawer in the template with [(zVisible)] and the header/title/description/footer children, or open the same panel from code through ZardDialogService / ZardSheetService / ZardAlertDialogService / ZardDrawerService. Never toggle them with a hand-written open flag or @if.ZardSonnerService — show, success, error, promise.z-separator not <hr>, z-skeleton not an animate-pulse div, z-badge not a styled span, z-empty not a hand-built empty state.z-field-group + z-field, never a div with space-y-*.data-invalid on the field and aria-invalid on the control.z-field-error, not a loose paragraph.typeset container, never a class per tag. zard-cli add typeset installs the stylesheet.--typeset-size, --typeset-leading, --typeset-flow.not-typeset on any component embedded in prose. It brings its own sizing.typeset-scroll around a wide table, instead of a hand-rolled overflow wrapper.!important — every element selector sits inside :where().@ng-icons/<family> and are registered with provideIcons in viewProviders. An unregistered name renders nothing, silently.icons in components.json. Do not assume lucide.| Need | Use |
|---|---|
| Action | z-button (zType: default, destructive, outline, secondary, ghost, link) |
| Grouped actions | z-button-group, z-toggle, z-toggle-group |
| Text input | input[z-input], textarea[z-textarea], z-input-group, z-input-otp |
| Choice | z-select, z-combobox, z-radio-group, z-checkbox, z-switch, z-slider |
| Dates | z-calendar, z-date-picker |
| Form structure | z-field-group, z-field, z-field-label, z-field-description, z-field-error |
| Data display | z-table, z-card, z-item, z-badge, z-avatar, z-chart |
| Navigation | z-navigation-menu, z-breadcrumb, z-tab-group, z-pagination |
| Nested / tree list | z-collapsible, nested — a folder tree is a z-collapsible inside a z-collapsible-content, not a dedicated tree component. See the composition rule. |
| Layout | z-sidebar (with z-sidebar-provider and z-sidebar-inset), z-separator, z-resizable, z-accordion, z-carousel |
| Overlays | z-dialog (modal), z-drawer (bottom/side sheet), z-sheet (side panel), z-alert-dialog (confirmation) — each also from code via its service —, z-popover, z-tooltip, z-dropdown |
| Command palette | z-command |
| Feedback | ZardSonnerService (toast), z-alert, z-progress, z-skeleton, z-spinner |
| Empty states | z-empty |
| Chat / messages | z-bubble, z-bubble-group, z-bubble-content, z-bubble-reactions |
| Keyboard hints | z-kbd |
| Rendered prose | typeset + a preset class (a stylesheet, not a component) — see rules/typeset.md |
Names are the registry names — the same string zard-cli add takes. Two names from an older registry, tree and layout, no longer exist: layout's header/content/sidebar shape was replaced by the sidebar component above; tree has no replacement component — nest z-collapsible instead, per the row above. A project installed before the removal may still have them; see the migration skill.
Not every registry name is a component you pick for a UI need — five install infrastructure instead:
| Item | What it is | Installed |
|---|---|---|
core | Shared directives, the overlay stack, provideZard(), and the theme CSS | Automatic — init installs it; no component declares it as a dependency |
utils | mergeClasses() (twMerge(clsx(...))) | Automatic — same as core |
utilities | The scroll-fade / shimmer CSS utilities — see rules/styling.md | zard-cli add utilities, or installed with a component that uses them |
dark-mode | The dark-mode service and the toggle script injected into index.html | Opt-in — zard-cli add dark-mode (interactive; needs an index.html path) |
typeset | The prose stylesheet for rendered markdown | Opt-in — zard-cli add typeset — see rules/typeset.md |
components.json. No file → run init. Note aliases, baseUrl, icons, packageManager, projectType.aliases.components. Do not re-add what is there, and do not import what is not.<registryUrl>/registry.json lists every item; the MCP server exposes the same thing as list-components and search-components.get-component-docs (MCP) or https://zardui.com/docs/components/<name>.md. Every component page is published as Markdown. Guessing at inputs is the single most common failure mode.npx zard-cli add <name> — dependencies of the component, both npm packages and other registry items, are resolved and installed with it.icons.There is no search, view, diff, info, or build command — those are shadcn's. See cli.md for the full flag reference.
init and add, every flag, the five project types, headless behaviourZARD_REGISTRY_URL / ZARD_DOCS_URLmergeClasses, extending a componentinput(), OnPush, ViewEncapsulation.None, selectorsmergeClasses, variants before raw classes, the scroll-fade / shimmer utilitiesprovideIcons, the catalog, the configurable family--overwrite, the core/utils/utilities split)**// Host classes: computed + mergeClasses. Never assembled in the template.
protected readonly classes = computed(() => mergeClasses(cardVariants(), this.class()));
// Variants, not classes.
<button z-button zType="outline" zSize="sm">Save</button> // correct
<button z-button class="border bg-transparent px-2.5">Save</button> // wrong
// Spacing: gap-*, not space-y-*.
<div class="flex flex-col gap-4"> // correct
<div class="space-y-4"> // wrong
// Field: data-invalid on the field, aria-invalid on the control.
<div z-field [attr.data-invalid]="invalid || null">
<label z-field-label for="email">Email</label>
<input z-input id="email" [attr.aria-invalid]="invalid || null" />
<z-field-error>Enter a valid email.</z-field-error>
</div>
// Loading button: the zLoading input, not a hand-rolled spinner.
<button z-button [zLoading]="saving()" [zDisabled]="saving()">Save</button># Initialise a project (full-screen wizard).
npx zard-cli init
npx zard-cli init --type nx --project web # answer the wizard up front
npx zard-cli init --yes # accept the defaults; required without a TTY
# Add components.
npx zard-cli add button card dialog
npx zard-cli add # pick from the list
npx zard-cli add --all
npx zard-cli add button --overwrite # replace local changes — ask first
npx zard-cli add button --path src/app/ui # a directory other than the configured one
# Diagnose.
npx zard-cli add button --debug
npx zard-cli --version