spartan/ui
spartan/ui is an Angular UI library. It uses a two-layer architecture:
- Brain (
@spartan-ng/brain) - accessible, unstyled primitives (Angular directives/components),
installed from npm. This is the behavior and accessibility layer.
- Helm (
@spartan-ng/helm) - the styled layer (Tailwind + class-variance-authority). Helm code
is copied into the user's project by the CLI so they own and can customize it.
You compose Helm directives/components onto host elements; Helm wires up the matching Brain
primitive under the hood. Always prefer existing components over hand-written markup.
All CLI commands run through the workspace's runner. Detect it from the project:
- Nx workspace (has
nx.json): npx nx g @spartan-ng/cli:<generator> (or pnpm nx g ...).
- Angular CLI workspace (has
angular.json, no nx.json): ng g @spartan-ng/cli:<generator>.
Current project context
Before generating any code, gather the project context:
npx nx g @spartan-ng/cli:info --json # Nx
ng g @spartan-ng/cli:info --json # Angular CLI
This is read-only and prints JSON with:
workspaceType - nx | angular-cli (decides which runner to use above).
config.componentsPath - where Helm components are copied (e.g. libs/ui).
config.importAlias - the import prefix for Helm, default @spartan-ng/helm.
config.generateAs - library | entrypoint (Nx layout choice).
versions - Angular, Angular CDK, Tailwind, @spartan-ng/brain, @spartan-ng/cli.
iconLibrary - @ng-icons when present.
tailwindCssFile - the global stylesheet that imports the preset.
installedComponents - components already present (do not re-add these).
availableComponents - everything the CLI can generate.
If components.json does not exist, the project is not set up yet - run @spartan-ng/cli:init
first (it installs dependencies and the theme). components.json itself is created when you add the
first component with ui (see cli.md).
Principles
- Use existing components first. Check
installedComponents, then availableComponents. Find
docs via the MCP server (spartan_components_list / spartan_components_get) or the live docs at
https://www.spartan.ng/components/<name>. See mcp.md.
- Compose, do not reinvent. Build dashboards, forms, and dialogs from existing Helm + Brain
pieces rather than custom markup.
- Use built-in variants before custom styles. Buttons, badges, alerts, etc. ship
variant and
size inputs - use them instead of overriding classes.
- Use semantic colors, never raw values.
bg-primary text-primary-foreground, not
bg-blue-500. See rules/styling.md.
Critical rules
Read the rule file before doing the related work:
rules/styling.md - the hlm() util, semantic color tokens, layout-only classes,
gap-* over space-*, size-*, dark mode, no manual z-index on overlays.
rules/forms.md - compose forms with hlmField (label, control, error, description) and
hlmFieldSet / hlmFieldLegend on native <fieldset>/<legend>; option sets (2-7 choices) use
hlm-toggle-group.
rules/composition.md - items belong inside their group; dialogs/sheets need a title;
full Card composition; tabs triggers inside hlm-tabs-list; avatar always has a fallback;
use Alert/Empty/Skeleton/Badge/Separator/Spinner instead of custom markup.
rules/icons.md - icons are <ng-icon name="lucide...">; register with provideIcons;
no manual sizing classes inside components - use the size input.
rules/brain-vs-helm.md - the two-layer model (one headless library, Brain, plus the styled
Helm layer); when to reach for Brain directly vs Helm, and how composition works via directives.
cli.md - every generator (init, ui, ui-theme, healthcheck, info, migrate-*),
with Nx and Angular CLI invocations.
registry.md - the Brain-npm + Helm-copy-in distribution model, components.json, and the
fixed component catalog (the shipped CLI uses no remote or custom registry).
customization.md - theming via the hlm-tailwind-preset.css, CSS variables, the ui-theme
generator, and extending copied Helm components.
mcp.md - using the @spartan-ng/mcp tools, resources, and prompts for discovery.
Key patterns
<!-- Buttons: use variant/size inputs, not custom classes -->
<button hlmBtn variant="destructive" size="lg">Delete</button>
<!-- Icon in a button: ng-icon -->
<button hlmBtn size="icon" variant="ghost">
<ng-icon name="lucideTrash" />
</button>
<!-- Loading state: compose a spinner, there is no isLoading input -->
<button hlmBtn [disabled]="loading()">
@if (loading()) {
<hlm-spinner />
} Save
</button>
<!-- Layout: gap, not space-* ; size-* when width == height -->
<div class="flex items-center gap-2">
<span hlmBadge variant="secondary">beta</span>
</div>
Component selection
| Need |
Component(s) |
| Action / button |
button (hlmBtn), button-group |
| Text/number input |
input, textarea, input-otp, input-group, native-select |
| Choice input |
select, combobox, autocomplete, radio-group, checkbox, switch, slider |
| Toggle 2-7 options |
toggle-group |
| Form layout/validation |
field, label |
| Data display |
chart, table, card, badge, avatar, kbd, item |
| Navigation |
sidebar, navigation-menu, breadcrumb, tabs, pagination |
| Overlays |
dialog, sheet, alert-dialog, popover, hover-card, tooltip |
| Menus |
dropdown-menu, context-menu, menubar, command |
| Feedback |
sonner (toasts), alert, progress, skeleton, spinner |
| Layout/containers |
card, separator, resizable, scroll-area, accordion, collapsible, aspect-ratio |
| Empty states |
empty |
| Dates |
calendar, date-picker |
| Icons |
icon (@ng-icons) |
| Typography |
typography |
Workflow
- Get context. Run
@spartan-ng/cli:info --json. If the project is not set up, run :init,
then add components with :ui (the first :ui run creates components.json).
- Check what is installed. Do not re-add anything in
installedComponents.
- Find the component. Use the MCP tools or
https://www.spartan.ng/components/<name> for the
API and examples (mcp.md). Never guess selectors - confirm them.
- Add it.
npx nx g @spartan-ng/cli:ui --name=<component> (Nx) or
ng g @spartan-ng/cli:ui --name=<component> (Angular CLI). This installs the Brain dependency and
copies the Helm code. Omit --name to get an interactive multiselect.
- Compose correctly. Import the
*Imports const (e.g. HlmDialogImports) or the individual
classes from the import alias, add them to the standalone component's imports, and follow the
composition rules.
- Register icons. Any
<ng-icon> you use must be passed to provideIcons(...) (see
rules/icons.md).
- Verify. After bigger changes or upgrades, run
@spartan-ng/cli:healthcheck to catch
deprecated patterns and fix imports.
- Theme/customize. Edit the copied Helm files and the CSS variables; do not fork Brain.
Quick reference
# Initialize (creates components.json, wires Tailwind + preset)
npx nx g @spartan-ng/cli:init
ng g @spartan-ng/cli:init
# Project context as JSON
npx nx g @spartan-ng/cli:info --json
# Add components (interactive, or pass --name)
npx nx g @spartan-ng/cli:ui
npx nx g @spartan-ng/cli:ui --name=dialog
# Generate theme variables
npx nx g @spartan-ng/cli:ui-theme
# Scan + auto-fix deprecated APIs/imports after an upgrade
npx nx g @spartan-ng/cli:healthcheck --autoFix
1---2name: spartan3description: Manages spartan/ui, the Angular UI library - adding, composing, fixing, debugging, and styling UI with the Brain (headless primitives) and Helm (styled) layers. Provides project context, component APIs, and usage examples. Applies when working with spartan/ui, @spartan-ng/brain, @spartan-ng/helm, the @spartan-ng/cli generators, or any Angular project with a components.json file. Also triggers for "spartan init", "add a spartan component", or "set up spartan/ui".4---5
6# spartan/ui
7
8spartan/ui is an Angular UI library. It uses a **two-layer architecture**:
9
10- **Brain (`@spartan-ng/brain`)** - accessible, unstyled primitives (Angular directives/components),
11 installed from npm. This is the behavior and accessibility layer.
12- **Helm (`@spartan-ng/helm`)** - the styled layer (Tailwind + class-variance-authority). Helm code
13 is **copied into the user's project** by the CLI so they own and can customize it.
14
15You compose Helm directives/components onto host elements; Helm wires up the matching Brain
16primitive under the hood. Always prefer existing components over hand-written markup.
17
18All CLI commands run through the workspace's runner. Detect it from the project:
19
20- **Nx workspace** (has `nx.json`): `npx nx g @spartan-ng/cli:<generator>` (or `pnpm nx g ...`).
21- **Angular CLI workspace** (has `angular.json`, no `nx.json`): `ng g @spartan-ng/cli:<generator>`.
22
23## Current project context
24
25Before generating any code, gather the project context:
26
27```bash
28npx nx g @spartan-ng/cli:info --json # Nx
29ng g @spartan-ng/cli:info --json # Angular CLI
30```
31
32This is read-only and prints JSON with:
33
34- `workspaceType` - `nx` | `angular-cli` (decides which runner to use above).
35- `config.componentsPath` - where Helm components are copied (e.g. `libs/ui`).
36- `config.importAlias` - the import prefix for Helm, default `@spartan-ng/helm`.
37- `config.generateAs` - `library` | `entrypoint` (Nx layout choice).
38- `versions` - Angular, Angular CDK, Tailwind, `@spartan-ng/brain`, `@spartan-ng/cli`.
39- `iconLibrary` - `@ng-icons` when present.
40- `tailwindCssFile` - the global stylesheet that imports the preset.
41- `installedComponents` - components already present (do not re-add these).
42- `availableComponents` - everything the CLI can generate.
43
44If `components.json` does not exist, the project is not set up yet - run `@spartan-ng/cli:init`
45first (it installs dependencies and the theme). `components.json` itself is created when you add the
46first component with `ui` (see `cli.md`).
47
48## Principles
49
501. **Use existing components first.** Check `installedComponents`, then `availableComponents`. Find
51 docs via the MCP server (`spartan_components_list` / `spartan_components_get`) or the live docs at
52 `https://www.spartan.ng/components/<name>`. See `mcp.md`.
532. **Compose, do not reinvent.** Build dashboards, forms, and dialogs from existing Helm + Brain
54 pieces rather than custom markup.
553. **Use built-in variants before custom styles.** Buttons, badges, alerts, etc. ship `variant` and
56 `size` inputs - use them instead of overriding classes.
574. **Use semantic colors, never raw values.** `bg-primary text-primary-foreground`, not
58 `bg-blue-500`. See `rules/styling.md`.
59
60## Critical rules
61
62Read the rule file before doing the related work:
63
64- **`rules/styling.md`** - the `hlm()` util, semantic color tokens, layout-only classes,
65 `gap-*` over `space-*`, `size-*`, dark mode, no manual z-index on overlays.
66- **`rules/forms.md`** - compose forms with `hlmField` (label, control, error, description) and
67 `hlmFieldSet` / `hlmFieldLegend` on native `<fieldset>`/`<legend>`; option sets (2-7 choices) use
68 `hlm-toggle-group`.
69- **`rules/composition.md`** - items belong inside their group; dialogs/sheets need a title;
70 full Card composition; tabs triggers inside `hlm-tabs-list`; avatar always has a fallback;
71 use `Alert`/`Empty`/`Skeleton`/`Badge`/`Separator`/`Spinner` instead of custom markup.
72- **`rules/icons.md`** - icons are `<ng-icon name="lucide...">`; register with `provideIcons`;
73 no manual sizing classes inside components - use the `size` input.
74- **`rules/brain-vs-helm.md`** - the two-layer model (one headless library, Brain, plus the styled
75 Helm layer); when to reach for Brain directly vs Helm, and how composition works via directives.
76- **`cli.md`** - every generator (`init`, `ui`, `ui-theme`, `healthcheck`, `info`, `migrate-*`),
77 with Nx and Angular CLI invocations.
78- **`registry.md`** - the Brain-npm + Helm-copy-in distribution model, `components.json`, and the
79 fixed component catalog (the shipped CLI uses no remote or custom registry).
80- **`customization.md`** - theming via the `hlm-tailwind-preset.css`, CSS variables, the `ui-theme`
81 generator, and extending copied Helm components.
82- **`mcp.md`** - using the `@spartan-ng/mcp` tools, resources, and prompts for discovery.
83
84## Key patterns
85
86```html
87<!-- Buttons: use variant/size inputs, not custom classes -->
88<button hlmBtn variant="destructive" size="lg">Delete</button>
89
90<!-- Icon in a button: ng-icon -->
91<button hlmBtn size="icon" variant="ghost">
92 <ng-icon name="lucideTrash" />
93</button>
94
95<!-- Loading state: compose a spinner, there is no isLoading input -->
96<button hlmBtn [disabled]="loading()">
97 @if (loading()) {
98 <hlm-spinner />
99 } Save
100</button>
101
102<!-- Layout: gap, not space-* ; size-* when width == height -->
103<div class="flex items-center gap-2">
104 <span hlmBadge variant="secondary">beta</span>
105</div>
106```
107
108## Component selection
109
110| Need | Component(s) |
111| ---------------------- | ------------------------------------------------------------------------------------------- |
112| Action / button | `button` (`hlmBtn`), `button-group` |
113| Text/number input | `input`, `textarea`, `input-otp`, `input-group`, `native-select` |
114| Choice input | `select`, `combobox`, `autocomplete`, `radio-group`, `checkbox`, `switch`, `slider` |
115| Toggle 2-7 options | `toggle-group` |
116| Form layout/validation | `field`, `label` |
117| Data display | `chart`, `table`, `card`, `badge`, `avatar`, `kbd`, `item` |
118| Navigation | `sidebar`, `navigation-menu`, `breadcrumb`, `tabs`, `pagination` |
119| Overlays | `dialog`, `sheet`, `alert-dialog`, `popover`, `hover-card`, `tooltip` |
120| Menus | `dropdown-menu`, `context-menu`, `menubar`, `command` |
121| Feedback | `sonner` (toasts), `alert`, `progress`, `skeleton`, `spinner` |
122| Layout/containers | `card`, `separator`, `resizable`, `scroll-area`, `accordion`, `collapsible`, `aspect-ratio` |
123| Empty states | `empty` |
124| Dates | `calendar`, `date-picker` |
125| Icons | `icon` (`@ng-icons`) |
126| Typography | `typography` |
127
128## Workflow
129
1301. **Get context.** Run `@spartan-ng/cli:info --json`. If the project is not set up, run `:init`,
131 then add components with `:ui` (the first `:ui` run creates `components.json`).
1322. **Check what is installed.** Do not re-add anything in `installedComponents`.
1333. **Find the component.** Use the MCP tools or `https://www.spartan.ng/components/<name>` for the
134 API and examples (`mcp.md`). Never guess selectors - confirm them.
1354. **Add it.** `npx nx g @spartan-ng/cli:ui --name=<component>` (Nx) or
136 `ng g @spartan-ng/cli:ui --name=<component>` (Angular CLI). This installs the Brain dependency and
137 copies the Helm code. Omit `--name` to get an interactive multiselect.
1385. **Compose correctly.** Import the `*Imports` const (e.g. `HlmDialogImports`) or the individual
139 classes from the import alias, add them to the standalone component's `imports`, and follow the
140 composition rules.
1416. **Register icons.** Any `<ng-icon>` you use must be passed to `provideIcons(...)` (see
142 `rules/icons.md`).
1437. **Verify.** After bigger changes or upgrades, run `@spartan-ng/cli:healthcheck` to catch
144 deprecated patterns and fix imports.
1458. **Theme/customize.** Edit the copied Helm files and the CSS variables; do not fork Brain.
146
147## Quick reference
148
149```bash
150# Initialize (creates components.json, wires Tailwind + preset)
151npx nx g @spartan-ng/cli:init
152ng g @spartan-ng/cli:init
153
154# Project context as JSON
155npx nx g @spartan-ng/cli:info --json
156
157# Add components (interactive, or pass --name)
158npx nx g @spartan-ng/cli:ui
159npx nx g @spartan-ng/cli:ui --name=dialog
160
161# Generate theme variables
162npx nx g @spartan-ng/cli:ui-theme
163
164# Scan + auto-fix deprecated APIs/imports after an upgrade
165npx nx g @spartan-ng/cli:healthcheck --autoFix
166```