Skip to content

Commit c7d4e7f

Browse files
committed
improvement(emcn): extract design system into shared @sim/emcn package
Moves apps/sim/components/emcn into a shared @sim/emcn package consumed directly by apps/sim and apps/docs. cn/keyboard/use-copy-to-clipboard move into the package; all imports become direct @sim/emcn (icons via @sim/emcn/icons, CSS via file path). ChipModal email validation is now prop-driven (quickValidateEmail stays in apps/sim, injected via validate). Docs drops its local chip/chip-dropdown/dropdown-menu mirrors and consumes @sim/emcn.
1 parent c596316 commit c7d4e7f

692 files changed

Lines changed: 1349 additions & 1771 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.claude/rules/emcn-components.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,13 @@ paths:
55

66
# EMCN Components
77

8-
Import from `@/components/emcn`, never from subpaths (except CSS files). The **chip family** is the platform's primary chrome — always reach for it over the legacy primitives it is progressively replacing (`Input``ChipInput`, `Textarea``ChipTextarea`, `Modal``ChipModal`, `Select`/`Combobox``ChipSelect`/`ChipCombobox`/`ChipDropdown`, `Switch``ChipSwitch`, date field→`ChipDatePicker`). For context/action menus the canonical control is `DropdownMenu` — the standard menu (not a chip, and never a hand-rolled popover).
8+
Import components, `cn`, and tokens from the `@sim/emcn` barrel; icons come from the `@sim/emcn/icons` subpath, and CSS modules from their file path. Never deep-import other component subpaths. The **chip family** is the platform's primary chrome — always reach for it over the legacy primitives it is progressively replacing (`Input``ChipInput`, `Textarea``ChipTextarea`, `Modal``ChipModal`, `Select`/`Combobox``ChipSelect`/`ChipCombobox`/`ChipDropdown`, `Switch``ChipSwitch`, date field→`ChipDatePicker`). For context/action menus the canonical control is `DropdownMenu` — the standard menu (not a chip, and never a hand-rolled popover).
99

1010
## Chip chrome — single source of truth
1111

1212
Never hand-roll the chip pill from raw class strings (they go stale). Compose from the canonical sources:
1313

14-
- **Surface, typography + content tokens:** `chip/chip-chrome.ts``chipFilledSurfaceTokens`, `chipFieldSurfaceClass`, `chipFieldTextClass` (text fields and the dropdown search box build on these), plus the chip-content chrome `chipContentGap`, `chipGeometryClass`, `chipContentIconClass`, `chipContentLabelClass`, and `cellIconNodeClass` (non-chip surfaces that must visually match chip content, e.g. resource table cells). All are re-exported from the `@/components/emcn` barrel — no subpath import needed.
14+
- **Surface, typography + content tokens:** `chip/chip-chrome.ts``chipFilledSurfaceTokens`, `chipFieldSurfaceClass`, `chipFieldTextClass` (text fields and the dropdown search box build on these), plus the chip-content chrome `chipContentGap`, `chipGeometryClass`, `chipContentIconClass`, `chipContentLabelClass`, and `cellIconNodeClass` (non-chip surfaces that must visually match chip content, e.g. resource table cells). All are re-exported from the `@sim/emcn` barrel — no subpath import needed.
1515
- **Pill geometry:** `chip/chip.tsx``chipVariants` (30px tall, `rounded-lg`, `px-2`, icon↔text `gap-1.5`). Every pill-shaped trigger (`ChipDropdown`, `ChipSelect`, `ChipSwitch`) reuses it for visual parity.
1616

1717
Canonical look: normal font-weight (never `font-medium`/`font-semibold`), value text `--text-body`, icons `--text-icon` at `size-[14px]`, placeholder `--text-muted`, `transition-colors`, **no focus ring** (the caret marks focus). Filled surface is `--surface-5` light / `--surface-4` dark with a `--border-1` border.

.claude/rules/sim-imports.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ import { CORE_TRIGGER_TYPES } from '@/app/workspace/.../utils'
4747

4848
1. React/core libraries
4949
2. External libraries
50-
3. UI components (`@/components/emcn`, `@/components/ui`)
50+
3. UI components (`@sim/emcn`, `@/components/ui`)
5151
4. Utilities (`@/lib/...`)
5252
5. Stores (`@/stores/...`)
5353
6. Feature imports

.claude/rules/sim-styling.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ paths:
1616
## Conditional Classes
1717

1818
```typescript
19-
import { cn } from '@/lib/core/utils/cn'
19+
import { cn } from '@sim/emcn'
2020

2121
<div className={cn(
2222
'base-classes',
@@ -52,7 +52,7 @@ Value text `--text-body`; muted/placeholder/labels `--text-muted`; icons `--text
5252

5353
## Chip Components (consumer usage)
5454

55-
`ChipInput`, `ChipTextarea`, `ChipModal*` own their full chrome. Consumers describe intent through PROPS; they never re-style the chrome. The canonical chrome lives in `apps/sim/components/emcn/components/chip/chip-chrome.ts` (all tokens are re-exported from the `@/components/emcn` barrel — no subpath import needed) — never hand-roll `rounded-lg`/`border`/`bg-[var(--surface-5)]`/`h-[30px]`/`px-2`/`text-sm`/focus rings.
55+
`ChipInput`, `ChipTextarea`, `ChipModal*` own their full chrome. Consumers describe intent through PROPS; they never re-style the chrome. The canonical chrome lives in `apps/sim/components/emcn/components/chip/chip-chrome.ts` (all tokens are re-exported from the `@sim/emcn` barrel — no subpath import needed) — never hand-roll `rounded-lg`/`border`/`bg-[var(--surface-5)]`/`h-[30px]`/`px-2`/`text-sm`/focus rings.
5656

5757
### Props over className
5858

.cursor/rules/emcn-components.mdc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,13 @@ globs: ["apps/sim/components/emcn/**"]
44
---
55
# EMCN Components
66

7-
Import from `@/components/emcn`, never from subpaths (except CSS files). The **chip family** is the platform's primary chrome — always reach for it over the legacy primitives it is progressively replacing (`Input`→`ChipInput`, `Textarea`→`ChipTextarea`, `Modal`→`ChipModal`, `Select`/`Combobox`→`ChipSelect`/`ChipCombobox`/`ChipDropdown`, `Switch`→`ChipSwitch`, date field→`ChipDatePicker`). For context/action menus the canonical control is `DropdownMenu` — the standard menu (not a chip, and never a hand-rolled popover).
7+
Import from `@sim/emcn`, never from subpaths (except CSS files). The **chip family** is the platform's primary chrome — always reach for it over the legacy primitives it is progressively replacing (`Input`→`ChipInput`, `Textarea`→`ChipTextarea`, `Modal`→`ChipModal`, `Select`/`Combobox`→`ChipSelect`/`ChipCombobox`/`ChipDropdown`, `Switch`→`ChipSwitch`, date field→`ChipDatePicker`). For context/action menus the canonical control is `DropdownMenu` — the standard menu (not a chip, and never a hand-rolled popover).
88

99
## Chip chrome — single source of truth
1010

1111
Never hand-roll the chip pill from raw class strings (they go stale). Compose from the canonical sources:
1212

13-
- **Surface, typography + content tokens:** `chip/chip-chrome.ts` — `chipFilledSurfaceTokens`, `chipFieldSurfaceClass`, `chipFieldTextClass` (text fields and the dropdown search box build on these), plus the chip-content chrome `chipContentGap`, `chipGeometryClass`, `chipContentIconClass`, `chipContentLabelClass`, and `cellIconNodeClass` (non-chip surfaces that must visually match chip content, e.g. resource table cells). All are re-exported from the `@/components/emcn` barrel — no subpath import needed.
13+
- **Surface, typography + content tokens:** `chip/chip-chrome.ts` — `chipFilledSurfaceTokens`, `chipFieldSurfaceClass`, `chipFieldTextClass` (text fields and the dropdown search box build on these), plus the chip-content chrome `chipContentGap`, `chipGeometryClass`, `chipContentIconClass`, `chipContentLabelClass`, and `cellIconNodeClass` (non-chip surfaces that must visually match chip content, e.g. resource table cells). All are re-exported from the `@sim/emcn` barrel — no subpath import needed.
1414
- **Pill geometry:** `chip/chip.tsx` — `chipVariants` (30px tall, `rounded-lg`, `px-2`, icon↔text `gap-1.5`). Every pill-shaped trigger (`ChipDropdown`, `ChipSelect`, `ChipSwitch`) reuses it for visual parity.
1515

1616
Canonical look: normal font-weight (never `font-medium`/`font-semibold`), value text `--text-body`, icons `--text-icon` at `size-[14px]`, placeholder `--text-muted`, `transition-colors`, **no focus ring** (the caret marks focus). Filled surface is `--surface-5` light / `--surface-4` dark with a `--border-1` border.

.cursor/rules/sim-imports.mdc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ import { CORE_TRIGGER_TYPES } from '@/app/workspace/.../utils'
4646

4747
1. React/core libraries
4848
2. External libraries
49-
3. UI components (`@/components/emcn`, `@/components/ui`)
49+
3. UI components (`@sim/emcn`, `@/components/ui`)
5050
4. Utilities (`@/lib/...`)
5151
5. Stores (`@/stores/...`)
5252
6. Feature imports

.cursor/rules/sim-styling.mdc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ globs: ["apps/sim/**/*.tsx", "apps/sim/**/*.css"]
1414
## Conditional Classes
1515

1616
```typescript
17-
import { cn } from '@/lib/core/utils/cn'
17+
import { cn } from '@sim/emcn'
1818

1919
<div className={cn(
2020
'base-classes',
@@ -50,7 +50,7 @@ Value text `--text-body`; muted/placeholder/labels `--text-muted`; icons `--text
5050

5151
## Chip Components (consumer usage)
5252

53-
`ChipInput`, `ChipTextarea`, `ChipModal*` own their full chrome. Consumers describe intent through PROPS; they never re-style the chrome. The canonical chrome lives in `apps/sim/components/emcn/components/chip/chip-chrome.ts` (all tokens are re-exported from the `@/components/emcn` barrel — no subpath import needed) — never hand-roll `rounded-lg`/`border`/`bg-[var(--surface-5)]`/`h-[30px]`/`px-2`/`text-sm`/focus rings.
53+
`ChipInput`, `ChipTextarea`, `ChipModal*` own their full chrome. Consumers describe intent through PROPS; they never re-style the chrome. The canonical chrome lives in `apps/sim/components/emcn/components/chip/chip-chrome.ts` (all tokens are re-exported from the `@sim/emcn` barrel — no subpath import needed) — never hand-roll `rounded-lg`/`border`/`bg-[var(--surface-5)]`/`h-[30px]`/`px-2`/`text-sm`/focus rings.
5454

5555
### Props over className
5656

CLAUDE.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ Use barrel exports (`index.ts`) when a folder has 3+ exports. Do not re-export f
9393

9494
1. React/core libraries
9595
2. External libraries
96-
3. UI components (`@/components/emcn`, `@/components/ui`)
96+
3. UI components (`@sim/emcn`, `@/components/ui`)
9797
4. Utilities (`@/lib/...`)
9898
5. Stores (`@/stores/...`)
9999
6. Feature imports
@@ -375,7 +375,7 @@ Co-locate a `search-params.ts` per feature exporting the parser map (single sour
375375

376376
## Styling
377377

378-
Use Tailwind only, no inline styles. Use `cn()` from `@/lib/core/utils/cn` for conditional classes.
378+
Use Tailwind only, no inline styles. Use `cn()` from `@sim/emcn` for conditional classes.
379379

380380
```typescript
381381
<div className={cn('base-classes', isActive && 'active-classes')} />
@@ -391,7 +391,7 @@ On chip components (see "EMCN Components"), drive chrome through PROPS, not `cla
391391

392392
## EMCN Components
393393

394-
Import from `@/components/emcn`, never from subpaths (except CSS files). Use CVA only when 2+ genuine variants exist; otherwise plain `cn()`.
394+
Import components, `cn`, and tokens from the `@sim/emcn` barrel; icons come from the `@sim/emcn/icons` subpath, and CSS modules from their file path. Never deep-import other component subpaths. Use CVA only when 2+ genuine variants exist; otherwise plain `cn()`.
395395

396396
The chip family is the canonical UI chrome and is progressively replacing the legacy EMCN primitives — always reach for the chip equivalent: `ChipInput` over `Input`, `ChipTextarea` over `Textarea`, `ChipModal`/`ChipModalField` over `Modal`, `ChipSelect`/`ChipCombobox` (searchable) or `ChipDropdown` (simple menu-select) over `Select`/`Combobox`, `ChipSwitch` over `Switch`, `ChipDatePicker` over a raw date field, `Chip`/`ChipLink` for pill buttons/links, `ChipTag` for inline tags/badges. For context/action menus the canonical control is `DropdownMenu` (not a chip, but the standard menu — not a hand-rolled popover). Components OWN their chrome (single source of truth) — consumers pass props, not class overrides. Authoring rules in `.claude/rules/emcn-components.md`; consumer rules in `.claude/rules/sim-styling.md`.
397397

apps/docs/app/[lang]/not-found.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1+
import { ChipLink } from '@sim/emcn'
12
import { DocsPage } from 'fumadocs-ui/page'
2-
import { ChipLink } from '@/components/ui/chip'
33

44
export const metadata = {
55
title: 'Page Not Found',

apps/docs/app/global.css

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,14 @@ body {
5959
--text-icon: #525252;
6060
--text-inverse: #ffffff;
6161
--text-error: #ef4444;
62+
--text-muted-inverse: #a0a0a0;
6263
--brand-accent: #33c482;
6364
--brand-accent-hover: #2dac72;
65+
--brand-secondary: #33b4ff;
66+
--surface-inverted: #1b1b1b;
67+
--surface-inverted-hover: #363636;
68+
--border-inverted: #363636;
69+
--border-muted: #e4e4e4;
6470
--badge-success-bg: #bbf7d0;
6571
--badge-success-text: #15803d;
6672
--badge-blue-bg: #bfdbfe;
@@ -102,8 +108,14 @@ body {
102108
--text-icon: #a0a0a0;
103109
--text-inverse: #1b1b1b;
104110
--text-error: #ef4444;
111+
--text-muted-inverse: #b3b3b3;
105112
--brand-accent: #33c482;
106113
--brand-accent-hover: #2dac72;
114+
--brand-secondary: #33b4ff;
115+
--surface-inverted: #242424;
116+
--surface-inverted-hover: #363636;
117+
--border-inverted: #3d3d3d;
118+
--border-muted: #424242;
107119
--badge-success-bg: rgba(34, 197, 94, 0.2);
108120
--badge-success-text: #86efac;
109121
--badge-blue-bg: rgba(59, 130, 246, 0.2);

apps/docs/components/navbar/navbar.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
'use client'
22

3+
import { ChipLink } from '@sim/emcn'
34
import Link from 'next/link'
45
import { usePathname } from 'next/navigation'
5-
import { ChipLink } from '@/components/ui/chip'
66
import { LanguageDropdown } from '@/components/ui/language-dropdown'
77
import { SearchTrigger } from '@/components/ui/search-trigger'
88
import { SimLogoFull } from '@/components/ui/sim-logo'

0 commit comments

Comments
 (0)