NAF Quali — build anything with the design system
Storybook (source of truth): https://qa-nafdesignsystem.naftech.io/?path=/docs/documentation-introduction--docs
Packages: @naf/react-ui · @naf/design-tokens (Azure Artifacts — not npmjs.org)
Mandatory workflow (every UI task)
- Connect Storybook MCP (once per session if needed):
- Namespace:
user-storybook-mcp
connect({ url: "https://qa-nafdesignsystem.naftech.io/" })
- Find the right primitive —
search({ query: "<need>" }) or list({ full: true })
- Read live API —
get_docs({ path: "<story-id>--docs", format: "markdown" })
- Match a reference screen (when building a page):
- Login →
examples-loginform--playground
- Account/settings form →
examples-accountform--playground
- Generic form →
examples-simpleform--playground
- All inputs QA →
design-qa-allinputs--playground
- Screenshot when layout matters —
screenshot({ path: "<story-id>--playground" })
- Implement using imports below — props/variants must match Storybook docs exactly
- Verify checklist at bottom before finishing
Never invent components, props, variants, or hex colors when a Quali primitive exists.
Build-any-screen decision tree
| User need |
Quali component(s) |
Story prefix |
| Page shell / nav |
Top Bar, Sidebar, Breadcrumb |
components-navigation-* |
| Section grouping |
Card, Divider |
components-ui-card, components-ui-divider |
| Primary action |
Button variant="primary" |
components-ui-button |
| Secondary / cancel |
Button variant="secondary" or tertiary |
components-ui-button |
| Delete / irreversible |
Button variant="destructive" |
components-ui-button |
| Text link |
Hyperlink or Button hyperlink |
components-ui-hyperlink |
| Text field |
FormField + Input |
components-forms-formfield, components-forms-inputs-input |
| Password |
FormField + PasswordInput (+ PasswordHint) |
components-forms-inputs-passwordinput |
| Search box |
Search |
components-forms-inputs-search |
| Dropdown |
Select / MultiSelect |
components-forms-select, components-forms-multiselect |
| Date / range |
DatePicker / DateRangePicker |
components-forms-datepicker, components-forms-daterangepicker |
| File upload |
FileUpload |
components-forms-fileupload |
| Yes/no toggle |
Switch (Toggle) |
components-forms-switch-toggle |
| Checkbox / radio group |
Checkbox, CheckboxGroup, RadioGroup |
components-forms-* |
| Tabs |
TabList, TabButton, TabContext |
components-navigation-tabs-* |
| Accordion FAQ |
Accordion |
components-navigation-accordion |
| Status chip |
Badge, Chip, BadgeIcon |
components-data-display-* |
| Inline alert |
AlertCard |
components-ui-alertcard |
| Toast notification |
useToaster + Toast |
components-notifications-toast |
| Tooltip |
Tooltip |
components-data-display-tooltip |
| Modal dialog |
Modal |
components-ui-modal |
| Loading state |
Loader or Button isLoading |
components-ui-loader |
Full story ID list: components.md
App setup (required once per project)
.npmrc (commit registry line only; never commit tokens):
registry=https://registry.npmjs.org/
@naf:registry=https://pkgs.dev.azure.com/NAF-Tech/_packaging/NAF-Tech/npm/registry/
always-auth=true
Auth: vsts-npm-auth -config .npmrc (Windows) or PAT per Storybook Introduction docs.
Install:
npm install @naf/react-ui @naf/design-tokens react react-dom react-hook-form zod @hookform/resolvers
Entry (main.tsx) — order matters:
import '@naf/design-tokens/css/light';
import '@naf/react-ui/styles.css';
Provider:
import { DSProvider } from '@naf/react-ui';
<DSProvider theme="light">
<App />
</DSProvider>
- Light theme only until official dark guidance ships.
- Peers: React 18+, react-hook-form 7+.
Details: reference.md
Import paths
| Category |
Import |
Examples |
| UI |
@naf/react-ui/ui |
Button, Card, Modal, AlertCard, Loader, Hyperlink, Divider |
| Forms |
@naf/react-ui/forms |
FormField, Input, Select, DatePicker, PasswordInput, FileUpload |
| Data display |
@naf/react-ui/ui |
Badge, Chip, Tooltip |
| Navigation |
@naf/react-ui/ui |
TopBar, Sidebar, Breadcrumb, Accordion, tab primitives |
| Toaster |
@naf/react-ui |
useToaster |
Prefer subpath imports for tree-shaking.
Styling rules
- Custom CSS/SCSS uses tokens only:
var(--color-*), var(--spacing-*), var(--radius-*)
- Common:
var(--color-border-default), var(--color-bg-primary), var(--spacing-400) … var(--spacing-700)
- No raw hex in new code
- Custom
var(--…) must render under DSProvider or with global token import
- Do not replace Quali components with legacy app UI (
components/ui/*, bespoke SCSS buttons) in new work
Forms pattern
import { zodResolver } from '@hookform/resolvers/zod';
import { useForm } from 'react-hook-form';
import { z } from 'zod';
import { Button } from '@naf/react-ui/ui';
import { FormField, Input } from '@naf/react-ui/forms';
const schema = z.object({ email: z.string().email() });
function ExampleForm() {
const { register, handleSubmit, formState: { errors } } = useForm({
resolver: zodResolver(schema),
});
return (
<form
<FormField label="Email" error={errors.email?.message}>
<Input {...register('email')} />
</FormField>
<Button type="submit" variant="primary">Submit</Button>
</form>
);
}
Always get_docs on form components before assuming prop names.
Page composition pattern
import { Card } from '@naf/react-ui/ui';
import { Button } from '@naf/react-ui/ui';
export function ExamplePage() {
return (
<main style={{ padding: 'var(--spacing-600)' }}>
<Card>
<Card.Header title="Page title" />
<Card.Body>{/* FormField rows, AlertCard, etc. */}</Card.Body>
<Card.Footer>
<Button variant="secondary">Cancel</Button>
<Button variant="primary">Save</Button>
</Card.Footer>
</Card>
</main>
);
}
Check components-ui-card--docs for exact slot/prop names before coding.
Figma handoff (optional)
Storybook Ecosystem → Figma MCP (ecosystem-figma-mcp--docs) — use Figma MCP for mockups, then map frames to Quali components via this skill + Storybook docs. Do not copy hex from Figma when a token name exists.
Anti-patterns
| Do not |
Do instead |
<button className="btn"> |
<Button variant="primary"> |
<input type="text"> |
<FormField><Input /></FormField> |
#091644 in CSS |
var(--color-*) token |
| Guess Button props |
get_docs on components-ui-button--docs |
| Skip Storybook for “simple” UI |
Always fetch docs for components used |
Verify before done
Additional resources
- npm/registry/setup: reference.md
- Full component + story catalog: components.md
- Screen recipes: screen-patterns.md
- Share/install: README-SHARE.md
1---2name: naf-quali3description: Builds React UI with the NAF Quali design system (@naf/react-ui, @naf/design-tokens). Uses live Storybook at qa-nafdesignsystem.naftech.io via storybook-mcp for component APIs, variants, tokens, and screenshots. Use when building or refactoring any NAF UI, pages, forms, prototypes, dashboards, or when the user mentions Quali, NAF design system, or @naf/react-ui.4---56# NAF Quali — build anything with the design system78**Storybook (source of truth):** https://qa-nafdesignsystem.naftech.io/?path=/docs/documentation-introduction--docs910**Packages:** `@naf/react-ui` · `@naf/design-tokens` (Azure Artifacts — not npmjs.org)1112## Mandatory workflow (every UI task)13141. **Connect Storybook MCP** (once per session if needed):15 - Namespace: `user-storybook-mcp`16 - `connect({ url: "https://qa-nafdesignsystem.naftech.io/" })`172. **Find the right primitive** — `search({ query: "<need>" })` or `list({ full: true })`183. **Read live API** — `get_docs({ path: "<story-id>--docs", format: "markdown" })`194. **Match a reference screen** (when building a page):20 - Login → `examples-loginform--playground`21 - Account/settings form → `examples-accountform--playground`22 - Generic form → `examples-simpleform--playground`23 - All inputs QA → `design-qa-allinputs--playground`245. **Screenshot when layout matters** — `screenshot({ path: "<story-id>--playground" })`256. **Implement** using imports below — props/variants must match Storybook docs exactly267. **Verify** checklist at bottom before finishing2728**Never** invent components, props, variants, or hex colors when a Quali primitive exists.2930## Build-any-screen decision tree3132| User need | Quali component(s) | Story prefix |33|-----------|-------------------|--------------|34| Page shell / nav | `Top Bar`, `Sidebar`, `Breadcrumb` | `components-navigation-*` |35| Section grouping | `Card`, `Divider` | `components-ui-card`, `components-ui-divider` |36| Primary action | `Button variant="primary"` | `components-ui-button` |37| Secondary / cancel | `Button variant="secondary"` or `tertiary` | `components-ui-button` |38| Delete / irreversible | `Button variant="destructive"` | `components-ui-button` |39| Text link | `Hyperlink` or `Button hyperlink` | `components-ui-hyperlink` |40| Text field | `FormField` + `Input` | `components-forms-formfield`, `components-forms-inputs-input` |41| Password | `FormField` + `PasswordInput` (+ `PasswordHint`) | `components-forms-inputs-passwordinput` |42| Search box | `Search` | `components-forms-inputs-search` |43| Dropdown | `Select` / `MultiSelect` | `components-forms-select`, `components-forms-multiselect` |44| Date / range | `DatePicker` / `DateRangePicker` | `components-forms-datepicker`, `components-forms-daterangepicker` |45| File upload | `FileUpload` | `components-forms-fileupload` |46| Yes/no toggle | `Switch` (Toggle) | `components-forms-switch-toggle` |47| Checkbox / radio group | `Checkbox`, `CheckboxGroup`, `RadioGroup` | `components-forms-*` |48| Tabs | `TabList`, `TabButton`, `TabContext` | `components-navigation-tabs-*` |49| Accordion FAQ | `Accordion` | `components-navigation-accordion` |50| Status chip | `Badge`, `Chip`, `BadgeIcon` | `components-data-display-*` |51| Inline alert | `AlertCard` | `components-ui-alertcard` |52| Toast notification | `useToaster` + `Toast` | `components-notifications-toast` |53| Tooltip | `Tooltip` | `components-data-display-tooltip` |54| Modal dialog | `Modal` | `components-ui-modal` |55| Loading state | `Loader` or `Button isLoading` | `components-ui-loader` |5657Full story ID list: [components.md](components.md)5859## App setup (required once per project)6061**`.npmrc`** (commit registry line only; never commit tokens):6263```64registry=https://registry.npmjs.org/65@naf:registry=https://pkgs.dev.azure.com/NAF-Tech/_packaging/NAF-Tech/npm/registry/66always-auth=true67```6869Auth: `vsts-npm-auth -config .npmrc` (Windows) or PAT per Storybook Introduction docs.7071**Install:**7273```bash74npm install @naf/react-ui @naf/design-tokens react react-dom react-hook-form zod @hookform/resolvers75```7677**Entry (`main.tsx`) — order matters:**7879```tsx80import '@naf/design-tokens/css/light';81import '@naf/react-ui/styles.css';82```8384**Provider:**8586```tsx87import { DSProvider } from '@naf/react-ui';8889<DSProvider theme="light">90 <App />91</DSProvider>92```9394- **Light theme only** until official dark guidance ships.95- Peers: React 18+, react-hook-form 7+.9697Details: [reference.md](reference.md)9899## Import paths100101| Category | Import | Examples |102|----------|--------|----------|103| UI | `@naf/react-ui/ui` | `Button`, `Card`, `Modal`, `AlertCard`, `Loader`, `Hyperlink`, `Divider` |104| Forms | `@naf/react-ui/forms` | `FormField`, `Input`, `Select`, `DatePicker`, `PasswordInput`, `FileUpload` |105| Data display | `@naf/react-ui/ui` | `Badge`, `Chip`, `Tooltip` |106| Navigation | `@naf/react-ui/ui` | `TopBar`, `Sidebar`, `Breadcrumb`, `Accordion`, tab primitives |107| Toaster | `@naf/react-ui` | `useToaster` |108109Prefer subpath imports for tree-shaking.110111## Styling rules112113- Custom CSS/SCSS uses **tokens only**: `var(--color-*)`, `var(--spacing-*)`, `var(--radius-*)`114- Common: `var(--color-border-default)`, `var(--color-bg-primary)`, `var(--spacing-400)` … `var(--spacing-700)`115- No raw hex in new code116- Custom `var(--…)` must render under `DSProvider` or with global token import117- Do not replace Quali components with legacy app UI (`components/ui/*`, bespoke SCSS buttons) in **new** work118119## Forms pattern120121```tsx122import { zodResolver } from '@hookform/resolvers/zod';123import { useForm } from 'react-hook-form';124import { z } from 'zod';125import { Button } from '@naf/react-ui/ui';126import { FormField, Input } from '@naf/react-ui/forms';127128const schema = z.object({ email: z.string().email() });129130function ExampleForm() {131 const { register, handleSubmit, formState: { errors } } = useForm({132 resolver: zodResolver(schema),133 });134135 return (136 <form onSubmit={handleSubmit(console.log)}>137 <FormField label="Email" error={errors.email?.message}>138 <Input {...register('email')} />139 </FormField>140 <Button type="submit" variant="primary">Submit</Button>141 </form>142 );143}144```145146Always `get_docs` on form components before assuming prop names.147148## Page composition pattern149150```tsx151import { Card } from '@naf/react-ui/ui';152import { Button } from '@naf/react-ui/ui';153154export function ExamplePage() {155 return (156 <main style={{ padding: 'var(--spacing-600)' }}>157 <Card>158 <Card.Header title="Page title" />159 <Card.Body>{/* FormField rows, AlertCard, etc. */}</Card.Body>160 <Card.Footer>161 <Button variant="secondary">Cancel</Button>162 <Button variant="primary">Save</Button>163 </Card.Footer>164 </Card>165 </main>166 );167}168```169170Check `components-ui-card--docs` for exact slot/prop names before coding.171172## Figma handoff (optional)173174Storybook **Ecosystem → Figma MCP** (`ecosystem-figma-mcp--docs`) — use Figma MCP for mockups, then map frames to Quali components via this skill + Storybook docs. Do not copy hex from Figma when a token name exists.175176## Anti-patterns177178| Do not | Do instead |179|--------|------------|180| `<button className="btn">` | `<Button variant="primary">` |181| `<input type="text">` | `<FormField><Input /></FormField>` |182| `#091644` in CSS | `var(--color-*)` token |183| Guess Button props | `get_docs` on `components-ui-button--docs` |184| Skip Storybook for “simple” UI | Always fetch docs for components used |185186## Verify before done187188- [ ] Storybook MCP consulted for every Quali component used189- [ ] Token CSS before `@naf/react-ui/styles.css`190- [ ] `DSProvider theme="light"` wraps tree191- [ ] Imports from `@naf/react-ui/ui` or `/forms`192- [ ] No invented props — match live docs193- [ ] Forms use react-hook-form (+ Zod when validating)194195## Additional resources196197- npm/registry/setup: [reference.md](reference.md)198- Full component + story catalog: [components.md](components.md)199- Screen recipes: [screen-patterns.md](screen-patterns.md)200- Share/install: [README-SHARE.md](README-SHARE.md)