com um clique
emcn-design-review
// Review UI code for alignment with the emcn design system — components, tokens, patterns, and conventions
// Review UI code for alignment with the emcn design system — components, tokens, patterns, and conventions
Review PRs and diffs for unbounded memory loading, concurrency explosions, oversized payload materialization, and missing pagination or byte caps. Use when reviewing cleanup jobs, background jobs, data imports/exports, file parsing, API fan-out, workflow execution payloads, large arrays/files, or any change that reads many rows, files, responses, logs, or external API pages into process memory.
Audit an existing Sim integration against the service API docs and repository conventions, then report and fix issues across tools, blocks, outputs, OAuth scopes, triggers, and registry entries. Use when validating or repairing a service integration under `apps/sim/tools`, `apps/sim/blocks`, or `apps/sim/triggers`.
Install, upgrade, and operate the Sim Helm chart on Kubernetes. Covers install path selection (inline / existingSecret / External Secrets Operator), required secret generation, the values.yaml mental model (env vs envDefaults vs Secret), and common failure triage. Invoke when a user asks about deploying Sim to a cluster, authoring a Sim values.yaml, debugging a Sim pod that won't start, upgrading a Sim release, or wiring Sim into a secret manager.
Commit, push, and open a PR to staging in one shot
Add a complete Sim integration from API docs, covering tools, block, icon, optional triggers, registrations, and integration conventions. Use when introducing a new service under `apps/sim/tools`, `apps/sim/blocks`, and `apps/sim/triggers`.
Run all code quality skills in sequence — effects, memo, callbacks, state, React Query, and emcn design review
| name | emcn-design-review |
| description | Review UI code for alignment with the emcn design system — components, tokens, patterns, and conventions |
Arguments:
User arguments: $ARGUMENTS
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.
apps/sim/components/emcn/components/index.ts to know what's availableapps/sim/app/_styles/globals.css for the full set of CSS variable tokens@/components/emcn, never from subpaths@/components/emcn/icons or lucide-reactcn from @/lib/core/utils/cn for conditional class merging@/components/ui// Good
import { Button, Modal, Badge } from '@/components/emcn'
// Bad
import { Button } from '@/components/emcn/components/button/button'
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.
| 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 |
| 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 |
| Token | Use |
|---|---|
border-[var(--border)] | Default borders |
border-[var(--border-1)] | Stronger borders (inputs, cards) |
border-[var(--border-muted)] | Subtle dividers |
| Token | Use |
|---|---|
--success | Success states |
--error | Error states |
--caution | Warning states |
| Token | Use |
|---|---|
--brand-secondary | Brand color |
--brand-accent | Accent/CTA color |
Use shadow tokens, never raw box-shadow values:
shadow-subtle, shadow-medium, shadow-overlayshadow-kbd, shadow-cardUse 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)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.
Use Modal + subcomponents. Never build custom dialog overlays.
<Modal open={open} onOpenChange={setOpen}>
<ModalContent size="sm">
<ModalHeader>Title</ModalHeader>
<ModalBody>Content</ModalBody>
<ModalFooter>
<Button variant="default" onClick={() => setOpen(false)}>Cancel</Button>
<Button variant="primary" onClick={handleSubmit}>Save</Button>
</ModalFooter>
</ModalContent>
</Modal>
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.
Always use Modal with size="sm". The established pattern:
<Modal open={open} onOpenChange={setOpen}>
<ModalContent size="sm">
<ModalHeader>Delete {itemType}</ModalHeader>
<ModalBody>
<p>Description of consequences</p>
<p className="text-[var(--text-error)]">Warning about irreversibility</p>
</ModalBody>
<ModalFooter>
<Button variant="default" onClick={() => setOpen(false)}>Cancel</Button>
<Button variant="destructive" onClick={handleDelete} disabled={isDeleting}>
Delete
</Button>
</ModalFooter>
</ModalContent>
</Modal>
Rules:
text-[var(--text-error)] for warning text when the action is irreversiblevariant="destructive" for the action button (100% compliance)variant="default" for cancel (100% compliance)Use the imperative toast API from @/components/emcn. Never build custom notification UI.
import { toast } from '@/components/emcn'
toast.success('Item saved')
toast.error('Something went wrong')
toast.success('Deleted', { action: { label: 'Undo', onClick: handleUndo } })
Variants: default, success, error. Auto-dismiss after 5s. Supports optional action buttons with callbacks.
Use semantic color variants for status:
| 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 |
Use dot prop for status indicators (19 instances in codebase). icon prop is available but rarely used.
Use Tooltip from emcn with namespace pattern:
<Tooltip.Root>
<Tooltip.Trigger asChild>
<Button variant="ghost">{icon}</Button>
</Tooltip.Trigger>
<Tooltip.Content>Helpful text</Tooltip.Content>
</Tooltip.Root>
Use tooltips for icon-only buttons and truncated text. Don't tooltip self-explanatory elements.
Use for filters, option menus, and nested navigation:
<Popover open={open} onOpenChange={setOpen} size="sm">
<PopoverTrigger asChild>
<Button variant="ghost">Trigger</Button>
</PopoverTrigger>
<PopoverContent side="bottom" align="end" minWidth={160}>
<PopoverSection>Section Title</PopoverSection>
<PopoverItem active={isActive} onClick={handleClick}>
Item Label
</PopoverItem>
<PopoverDivider />
</PopoverContent>
</Popover>
Use for context menus and action menus:
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="ghost">
<MoreHorizontal className="size-[14px]" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuItem onClick={handleEdit}>Edit</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem onClick={handleDelete} className="text-[var(--text-error)]">
Delete
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
Destructive items go last, after a separator, in error color.
Use FormField wrapper for labeled inputs:
<FormField label="Name" htmlFor="name" error={errors.name} optional>
<Input id="name" value={name} onChange={e => setName(e.target.value)} />
</FormField>
Rules:
Input from emcn, never raw <input> (exception: hidden file inputs)Textarea from emcn, never raw <textarea>FormField for label + input + error layoutoptional propCombobox for searchable selectsTagInput for multi-value inputsUse Skeleton for content placeholders:
<Skeleton className="h-5 w-[200px] rounded-md" />
Rules:
rounded-md to match component radiusStandard sizing — use the size-* shorthand. size-[14px] is the dominant pattern:
<Icon className="size-[14px] text-[var(--text-icon)]" />
Always prefer size-* over the legacy h-* w-* pair. size-[14px] is canonical; treat any h-[Npx] w-[Npx] or h-N w-N pair as a refactor target.
Size scale (most common first):
size-[14px] — default for inline iconssize-[16px] — slightly larger inline iconssize-3 (12px) — compact/tight spacessize-4 (16px) — Tailwind equivalentsize-3.5 (14px) — Tailwind equivalent of 14pxsize-5 (20px) — larger icons, section headersUse text-[var(--text-icon)] for icon color (113+ uses in codebase).
cn() for conditional classes: cn('base', condition && 'conditional') — never template literal concatenation like `base ${condition ? 'active' : ''}`style={{ width: dynamicVar }} or CSS variable references). Never use inline styles for colors or static values.text-gray-500, bg-red-100, #fff, or rgb(). Always text-[var(--text-*)], bg-[var(--surface-*)], etc.text-[var(--text-muted)] not text-muted-foreground. The CSS variable pattern is canonical.hover-hover: pseudo-class for hover-capable devicestransition-colors for color changes, transition-colors duration-100 for fast hoverrounded-lg (large cards), rounded-md (medium), rounded-sm (small), rounded-xs (tiny)text-small (13px), text-caption (12px), text-xs (11px), text-micro (10px)font-medium for emphasis, avoid font-bold unless for headingsgap-2, gap-3, px-4 py-2.5<button> instead of Button component (exception: inside Radix primitives)<input> instead of Input component (exception: hidden file inputs, read-only checkboxes in markdown)text-gray-*, bg-red-*, text-blue-*)bg-[#fff], text-[#333])text-muted-foreground) instead of CSS variables (text-[var(--text-muted)])Modaltoastcn()z-50, z-[9999]) instead of z-index tokenssize-[14px])h-* w-* pairs instead of the size-* shorthand