--- name: zard description: 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". user-invocable: false allowed-tools: Bash(npx zard-cli *), Bash(pnpm dlx zard-cli *), Bash(bunx --bun zard-cli *) --- # zard/ui 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`, or `bunx zard-cli` — pick the one matching `packageManager` in `components.json`. The examples below use `npx zard-cli`; substitute the right runner. ## Current Project Context **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. ```json { "$schema": "https://zardui.com/schema.json", "style": "css", "icons": "lucide", "rtl": false, "projectType": "angular", "appConfigFile": "src/app/app.config.ts", "packageManager": "npm", "tailwind": { "css": "src/styles.css", "baseColor": "neutral" }, "baseUrl": "src/app", "aliases": { "components": "@/shared/components", "utils": "@/shared/utils", "core": "@/shared/core", "services": "@/shared/services", "blocks": "@/shared/blocks" } } ``` 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](../zard-migration/SKILL.md), 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](./registry.md) or use the [MCP server](./mcp.md). Never reconstruct a component API from memory. ## Key Fields - **`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](./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](./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](./registry.md). ## Principles 1. **Install before importing.** A component only exists once `zard-cli add` has written it. Check the components directory first; do not import what is not there. 2. **Compose what exists.** A settings page is Card + Field + Input + Button. A dashboard is Sidebar + Card + Chart + Table. Reach for custom markup only when nothing covers it. 3. **Variants before classes.** `zType="outline"`, `zSize="sm"` — not a `class` that re-styles the component into the same thing. 4. **Semantic tokens only.** `bg-primary`, `text-muted-foreground`. Never `bg-blue-500`, never a `dark:` colour override. 5. **The library's own conventions apply to the code you write.** Standalone, `OnPush`, `input()`, `z`-prefixed inputs. See [rules/angular.md](./rules/angular.md). ## Critical Rules Always enforced. Each links to a file with Incorrect/Correct pairs. ### Angular API → [rules/angular.md](./rules/angular.md) - **Standalone with `imports`, `ChangeDetectionStrategy.OnPush`, `ViewEncapsulation.None`.** No NgModules, no `Default` change detection. - **Signal inputs: `input()`, `model()`, `output()`.** No `@Input()` / `@Output()` decorators in new code. - **Selectors are `z-` and/or `[z-]`.** Some components are element-only, some are attributes on a native tag (`input[z-input]`, `button[z-button]`). Use the one the component declares. - **Composite components import their `ZardImports` array**, not the individual classes one by one. - **Never edit generated output.** `apps/web/src/generated/**` and `public/r/*.json` are build artefacts. ### Styling → [rules/styling.md](./rules/styling.md) - **Semantic tokens, never raw colours.** `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. - **No `space-x-*` / `space-y-*`.** Use `flex` with `gap-*`. - **`size-*` when width and height match.** `size-4`, not `w-4 h-4`. - **No `dark:` colour overrides.** The tokens already switch. - **Tailwind v4 only.** There is no `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. ### Composition → [rules/composition.md](./rules/composition.md) - **Use the full composition.** `z-card` wants `z-card-header` / `z-card-title` / `z-card-content` / `z-card-footer`, not everything dumped into content. - **Items belong to their group.** `z-select-item` inside `z-select-group`. - **Dialogs, sheets, alert dialogs and drawers have two forms.** Compose `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`. - **Toasts go through `ZardSonnerService`** — `show`, `success`, `error`, `promise`. - **Use the component instead of styled markup.** `z-separator` not `
`, `z-skeleton` not an `animate-pulse` div, `z-badge` not a styled span, `z-empty` not a hand-built empty state. ### Forms → [rules/forms.md](./rules/forms.md) - **All three Angular form APIs are supported**: Signal Forms, Reactive Forms, Template-driven. Follow whichever the project already uses. - **Form layout is `z-field-group` + `z-field`**, never a `div` with `space-y-*`. - **Validation state is `data-invalid` on the field and `aria-invalid` on the control.** - **Errors render in `z-field-error`**, not a loose paragraph. ### Typeset → [rules/typeset.md](./rules/typeset.md) - **Rendered markdown gets a `typeset` container, never a class per tag.** `zard-cli add typeset` installs the stylesheet. - **Six variables govern it**, three of which are the rhythm: `--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. - **Utilities beat it with no `!important`** — every element selector sits inside `:where()`. ### Icons → [rules/icons.md](./rules/icons.md) - **Icons come from `@ng-icons/` and are registered with `provideIcons` in `viewProviders`.** An unregistered name renders nothing, silently. - **The family comes from `icons` in `components.json`.** Do not assume `lucide`. - **No sizing classes on icons inside components.** The component sizes them. ## Key Patterns ```angular-ts // Host classes: computed + mergeClasses. Never assembled in the template. protected readonly classes = computed(() => mergeClasses(cardVariants(), this.class())); // Variants, not classes. // correct // wrong // Spacing: gap-*, not space-y-*.
// correct
// wrong // Field: data-invalid on the field, aria-invalid on the control.
Enter a valid email.
// Loading button: the zLoading input, not a hand-rolled spinner. ``` ## Component Selection | 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](./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](../zard-migration/SKILL.md). ### Non-component items 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](./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](./rules/typeset.md) | ## Workflow 1. **Read `components.json`.** No file → run `init`. Note `aliases`, `baseUrl`, `icons`, `packageManager`, `projectType`. 2. **Check what is installed.** List the components directory resolved from `aliases.components`. Do not re-add what is there, and do not import what is not. 3. **Find what exists.** The registry index at `/registry.json` lists every item; the MCP server exposes the same thing as `list-components` and `search-components`. 4. **Read the real API before writing code.** `get-component-docs` (MCP) or `https://zardui.com/docs/components/.md`. Every component page is published as Markdown. Guessing at inputs is the single most common failure mode. 5. **Install.** `npx zard-cli add ` — dependencies of the component, both npm packages and other registry items, are resolved and installed with it. 6. **Review what was written.** Read the added files. Check the imports resolve under the project's real alias, and that the icon family matches `icons`. 7. **Never hand-fetch component source from GitHub.** Use the CLI or the registry; the registry is what the project actually installs from. ## Quick Reference ```bash # 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 ``` There is no `search`, `view`, `diff`, `info`, or `build` command — those are shadcn's. See [cli.md](./cli.md) for the full flag reference. ## Detailed References - [cli.md](./cli.md) — `init` and `add`, every flag, the five project types, headless behaviour - [registry.md](./registry.md) — the index, item and icon-catalog formats, JSON Schemas, pointing at your own registry - [mcp.md](./mcp.md) — the nine MCP tools, how to connect, `ZARD_REGISTRY_URL` / `ZARD_DOCS_URL` - [customization.md](./customization.md) — theme tokens, CVA variants, `mergeClasses`, extending a component - [rules/angular.md](./rules/angular.md) — standalone, `input()`, OnPush, `ViewEncapsulation.None`, selectors - [rules/styling.md](./rules/styling.md) — Tailwind v4, semantic tokens, `mergeClasses`, variants before raw classes, the `scroll-fade` / `shimmer` utilities - [rules/composition.md](./rules/composition.md) — composing with what exists before inventing markup - [rules/forms.md](./rules/forms.md) — Signal Forms, Reactive Forms, Template-driven - [rules/icons.md](./rules/icons.md) — ng-icons, `provideIcons`, the catalog, the configurable family - [rules/typeset.md](./rules/typeset.md) — styling rendered markdown with one container class instead of one per tag - [../zard-migration/SKILL.md](../zard-migration/SKILL.md) — a separate skill for a project whose installed component source predates the current registry (renamed selectors, `--overwrite`, the `core`/`utils`/`utilities` split)