diff --git a/.agents/skills/emcn-design-review/SKILL.md b/.agents/skills/emcn-design-review/SKILL.md index 04e39da1f8e..b53d3e19582 100644 --- a/.agents/skills/emcn-design-review/SKILL.md +++ b/.agents/skills/emcn-design-review/SKILL.md @@ -13,12 +13,12 @@ User arguments: $ARGUMENTS ## Context -This codebase uses **emcn**, a custom component library built on Radix UI primitives with CVA (class-variance-authority) variants and CSS variable design tokens. All UI must use emcn components and tokens — never raw HTML elements or hardcoded colors. +This codebase uses **emcn**, a custom component library built on Radix UI primitives with CVA variants and CSS variable design tokens. All UI must use emcn components and tokens. ## Steps -1. Read the emcn barrel export at `apps/sim/components/emcn/components/index.ts` to know what's available -2. Read `apps/sim/app/_styles/globals.css` for the full set of CSS variable tokens +1. Read the emcn public barrel at `apps/sim/components/emcn/index.ts` (re-exports components, Calendar, Table*, and icons) to know what's available; for the full icon set read `apps/sim/components/emcn/icons/index.ts` +2. Read `apps/sim/app/_styles/globals.css` for CSS variable tokens 3. Analyze the specified scope against every rule below 4. If fix=true, apply the fixes. If fix=false, propose the fixes without applying. @@ -26,313 +26,59 @@ This codebase uses **emcn**, a custom component library built on Radix UI primit ## Imports -- Import components from `@/components/emcn`, never from subpaths -- Import icons from `@/components/emcn/icons` or `lucide-react` -- Import `cn` from `@/lib/core/utils/cn` for conditional class merging -- Import app-specific wrappers (Select, VerifiedBadge) from `@/components/ui` +- Import from `@/components/emcn` barrel, never subpaths +- Icons from `@/components/emcn/icons` or `lucide-react` +- Use `cn` from `@/lib/core/utils/cn` for conditional classes -```tsx -// Good -import { Button, Modal, Badge } from '@/components/emcn' -// Bad -import { Button } from '@/components/emcn/components/button/button' -``` +## Design Tokens ---- - -## Design Tokens (CSS Variables) - -Never use raw color values. Always use CSS variable tokens via Tailwind arbitrary values: `text-[var(--text-primary)]`, not `text-gray-500` or `#333`. The CSS variable pattern is canonical (1,700+ uses) — do not use Tailwind semantic classes like `text-muted-foreground`. - -### Text hierarchy -| Token | Use | -|-------|-----| -| `text-[var(--text-primary)]` | Main content text | -| `text-[var(--text-secondary)]` | Secondary/supporting text | -| `text-[var(--text-tertiary)]` | Tertiary text | -| `text-[var(--text-muted)]` | Disabled, placeholder text | -| `text-[var(--text-icon)]` | Icon tinting | -| `text-[var(--text-inverse)]` | Text on dark backgrounds | -| `text-[var(--text-error)]` | Error/warning messages | - -### Surfaces (elevation) -| Token | Use | -|-------|-----| -| `bg-[var(--bg)]` | Page background | -| `bg-[var(--surface-2)]` through `bg-[var(--surface-7)]` | Increasing elevation | -| `bg-[var(--surface-hover)]` | Hover state backgrounds | -| `bg-[var(--surface-active)]` | Active/selected backgrounds | - -### Borders -| Token | Use | -|-------|-----| -| `border-[var(--border)]` | Default borders | -| `border-[var(--border-1)]` | Stronger borders (inputs, cards) | -| `border-[var(--border-muted)]` | Subtle dividers | - -### Status -| Token | Use | -|-------|-----| -| `--success` | Success states | -| `--error` | Error states | -| `--caution` | Warning states | - -### Brand -| Token | Use | -|-------|-----| -| `--brand-secondary` | Brand color | -| `--brand-accent` | Accent/CTA color | - -### Shadows -Use shadow tokens, never raw box-shadow values: -- `shadow-subtle`, `shadow-medium`, `shadow-overlay` -- `shadow-kbd`, `shadow-card` - -### Z-Index -Use z-index tokens for layering: -- `z-[var(--z-dropdown)]` (100), `z-[var(--z-modal)]` (200), `z-[var(--z-popover)]` (300), `z-[var(--z-tooltip)]` (400), `z-[var(--z-toast)]` (500) - ---- - -## Component Usage Rules - -### Buttons -Available variants: `default`, `primary`, `destructive`, `ghost`, `outline`, `active`, `secondary`, `tertiary`, `subtle`, `ghost-secondary`, `3d` - -| Action type | Variant | Frequency | -|-------------|---------|-----------| -| Toolbar, icon-only, utility actions | `ghost` | Most common (28%) | -| Primary action (create, save, submit) | `primary` | Very common (24%) | -| Cancel, close, secondary action | `default` | Common | -| Delete, remove, destructive action | `destructive` | Targeted use only | -| Active/selected state | `active` | Targeted use only | -| Toggle, mode switch | `outline` | Moderate | - -Sizes: `sm` (compact, 32% of buttons) or `md` (default, used when no size specified). Never create custom button styles — use an existing variant. - -Buttons without an explicit variant prop get `default` styling. This is acceptable for cancel/secondary actions. - -### Modals (Dialogs) -Use `Modal` + subcomponents. Never build custom dialog overlays. - -```tsx - - - Title - Content - - - - - - -``` - -Modal sizes by frequency: `sm` (440px, most common — confirmations and simple dialogs), `md` (500px, forms), `lg` (600px, content-heavy), `xl` (800px, rare), `full` (1200px, rare). - -Footer buttons: Cancel on left (`variant="default"`), primary action on right. This pattern is followed 100% across the codebase. - -### Delete/Remove Confirmations -Always use Modal with `size="sm"`. The established pattern: +Use CSS variable pattern (`text-[var(--text-primary)]`), never Tailwind semantics (`text-muted-foreground`) or hardcoded colors (`text-gray-500`, `#333`). -```tsx - - - Delete {itemType} - -

Description of consequences

-

Warning about irreversibility

-
- - - - -
-
-``` +**Text**: `--text-primary`, `--text-secondary`, `--text-tertiary`, `--text-muted`, `--text-body` (canonical value text), `--text-icon`, `--text-placeholder`, `--text-subtle`, `--text-inverse`, `--text-error` +**Surfaces**: `--bg`, `--surface-1` through `--surface-7`, `--surface-hover`, `--surface-active` +**Borders**: `--border`, `--border-1`, `--border-muted` +**Brand/accent**: `--brand-secondary`, `--brand-accent` +**Z-Index**: `--z-dropdown` (100), `--z-modal` (200), `--z-popover` (300), `--z-tooltip` (400), `--z-toast` (500) +**Shadows**: `shadow-subtle`, `shadow-medium`, `shadow-overlay`, `shadow-card` +**Badges**: `--badge-*` semantic families (success/error/gray/blue/purple/orange/amber/teal/cyan/pink, each with `-bg`/`-text`) -Rules: -- Title: "Delete {ItemType}" or "Remove {ItemType}" (use "Remove" for membership/association changes) -- Include consequence description -- Use `text-[var(--text-error)]` for warning text when the action is irreversible -- `variant="destructive"` for the action button (100% compliance) -- `variant="default"` for cancel (100% compliance) -- Cancel left, destructive right (100% compliance) -- For high-risk deletes (workspaces), require typing the name to confirm -- Include recovery info if soft-delete: "You can restore it from Recently Deleted in Settings" +## Buttons -### Toast Notifications -Use the imperative `toast` API from `@/components/emcn`. Never build custom notification UI. +Intent-to-variant mapping (read the actual `buttonVariants` in `apps/sim/components/emcn/components/button/button.tsx` for the full variant set — it exposes more than listed here): -```tsx -import { toast } from '@/components/emcn' +| Action | Variant | +|--------|---------| +| Toolbar, icon-only | `ghost` | +| Create, save, submit | `primary` | +| Cancel, close | `default` | +| Delete, remove | `destructive` | +| Selected state | `active` | +| Toggle | `outline` | -toast.success('Item saved') -toast.error('Something went wrong') -toast.success('Deleted', { action: { label: 'Undo', onClick: handleUndo } }) -``` +## Delete/Remove Confirmations -Variants: `default`, `success`, `error`. Auto-dismiss after 5s. Supports optional action buttons with callbacks. +`ChipModal` `size='sm'`, title "Delete/Remove {ItemType}", destructive confirm button, plain Cancel (follow the chip footer layout in `.claude/rules/emcn-components.md`). Use `text-[var(--text-error)]` for irreversible warnings. -### Badges -Use semantic color variants for status: +## Toast -| Status | Variant | Usage | -|--------|---------|-------| -| Error, failed, disconnected | `red` | Most common (15 uses) | -| Metadata, roles, auth types, scopes | `gray-secondary` | Very common (12 uses) | -| Type annotations (TS types, field types) | `type` | Very common (12 uses) | -| Success, active, enabled, running | `green` | Common (7 uses) | -| Neutral, default, unknown | `gray` | Common (6 uses) | -| Outline, parameters, public | `outline` | Moderate (6 uses) | -| Warning, processing | `amber` | Moderate (5 uses) | -| Paused, warning | `orange` | Occasional | -| Info, queued | `blue` | Occasional | -| Data types (arrays) | `purple` | Occasional | -| Generic with border | `default` | Occasional | +`toast.success()`, `toast.error()`, `toast()` from `@/components/emcn`. Never custom notification UI. -Use `dot` prop for status indicators (19 instances in codebase). `icon` prop is available but rarely used. +## Badges -### Tooltips -Use `Tooltip` from emcn with namespace pattern: +`red`=error/failed, `gray-secondary`=metadata/roles, `type`=type annotations, `green`=success/active, `gray`=neutral, `amber`=processing, `orange`=paused, `blue`=info. Use `dot` prop for status indicators. -```tsx - - - - - Helpful text - -``` +## Icons -Use tooltips for icon-only buttons and truncated text. Don't tooltip self-explanatory elements. - -### Popovers -Use for filters, option menus, and nested navigation: - -```tsx - - - - - - Section Title - - Item Label - - - - -``` - -### Dropdown Menus -Use for context menus and action menus: - -```tsx - - - - - - Edit - - - Delete - - - -``` - -Destructive items go last, after a separator, in error color. - -### Forms -Use `FormField` wrapper for labeled inputs: - -```tsx - - setName(e.target.value)} /> - -``` - -Rules: -- Use `Input` from emcn, never raw `` (exception: hidden file inputs) -- Use `Textarea` from emcn, never raw `