Skip to content

Commit 455191b

Browse files
BillLeoutsakosvl346Bill Leoutsakoscodexwaleedlatif1
authored
Consolidate EMCN controls and redundant styling options (#7998)
* improvement(emcn): share ordinary action geometry in Button * improvement(emcn): consolidate selection controls and styling options * test(emcn): read email geometry from the public export * improvement(emcn): share larger chip and field geometry (#8151) Co-authored-by: Bill Leoutsakos <billleoutsakos@Bills-MacBook-Pro.local> * refactor(ui): use EMCN fields in deployment and setup modals (#8009) * improvement(emcn): share larger chip and field geometry * refactor(ui): use EMCN fields in deployment and setup modals * improvement(ui): reuse chip for PII configuration action (#8173) Co-authored-by: Bill Leoutsakos <billleoutsakos@Bills-MacBook-Pro.local> * improvement(ui): compose horizontal and credential fields in EMCN (#8164) * improvement(ui): compose horizontal and credential fields in EMCN * Share deploy chat field error treatment (#8255) * refactor(ui): share deploy chat field error treatment * refactor(ui): name deploy error props --------- Co-authored-by: Bill Leoutsakos <billleoutsakos@Bills-MacBook-Pro.local> --------- Co-authored-by: Bill Leoutsakos <billleoutsakos@Bills-MacBook-Pro.local> --------- Co-authored-by: Bill Leoutsakos <billleoutsakos@Bills-MacBook-Pro.local> * improvement(ui): share knowledge filter headings (#8073) * improvement(ui): share knowledge filter headings * improvement(ui): share inline filter and pagination actions (#8163) Co-authored-by: Bill Leoutsakos <billleoutsakos@Bills-MacBook-Pro.local> --------- Co-authored-by: Bill Leoutsakos <billleoutsakos@Bills-MacBook-Pro.local> * improvement(ui): use chip fields in workflow search and replace (#8076) * improvement(emcn): share larger chip and field geometry * improvement(ui): use chip fields in workflow search and replace * fix(ui): use shared centered chip content on chat errors * improvement(ui): use chips for ordinary product actions (#8081) * improvement(ui): use chips for ordinary product actions * improvement(ui): use shared chip alignment * improvement(ui): use shared search action sizes (#8160) Co-authored-by: Bill Leoutsakos <billleoutsakos@Bills-MacBook-Pro.local> --------- Co-authored-by: Bill Leoutsakos <billleoutsakos@Bills-MacBook-Pro.local> --------- Co-authored-by: Bill Leoutsakos <billleoutsakos@Bills-MacBook-Pro.local> * improvement(ui): reuse chips for custom block upload actions (#8084) Co-authored-by: Bill Leoutsakos <billleoutsakos@Bills-MacBook-Pro.local> * fix(chat): use native attachment action (#8265) * fix(chat): use native attachment action * fix(chat): retain disabled attachment tooltip --------- Co-authored-by: Bill Leoutsakos <billleoutsakos@Bills-MacBook-Pro.local> * fix(chat): use EMCN icon button target for attachments * refactor(ui): centralize destructive ghost button colors in EMCN (#7988) * improvement(emcn): share larger chip and field geometry * refactor(ui): use EMCN fields in deployment and setup modals * refactor(ui): centralize destructive ghost button colors in EMCN * improvement(ui): use standard knowledge tag icon size * refactor(ui): consolidate product corner radii * improvement(ui): reuse chip for PII configuration action (#8173) Co-authored-by: Bill Leoutsakos <billleoutsakos@Bills-MacBook-Pro.local> * refactor(ui): share collapsible workflow field cards (#8017) Co-authored-by: Bill Leoutsakos <billleoutsakos@Bills-MacBook-Pro.local> * improvement(ui): compose horizontal and credential fields in EMCN (#8164) * improvement(ui): compose horizontal and credential fields in EMCN * Share deploy chat field error treatment (#8255) * refactor(ui): share deploy chat field error treatment * refactor(ui): name deploy error props --------- Co-authored-by: Bill Leoutsakos <billleoutsakos@Bills-MacBook-Pro.local> --------- Co-authored-by: Bill Leoutsakos <billleoutsakos@Bills-MacBook-Pro.local> --------- Co-authored-by: Bill Leoutsakos <billleoutsakos@Bills-MacBook-Pro.local> Co-authored-by: Waleed <walif6@gmail.com> * improvement(ui): share public headings and product typography (#8069) * improvement(emcn): share larger chip and field geometry * improvement(ui): share public authentication headings * improvement(ui): share workflow panel tab buttons (#8072) * improvement(ui): share workflow panel tab buttons * improvement(ui): reuse chips for panel selection actions (#8166) Co-authored-by: Bill Leoutsakos <billleoutsakos@Bills-MacBook-Pro.local> --------- Co-authored-by: Bill Leoutsakos <billleoutsakos@Bills-MacBook-Pro.local> * refactor(ui): share account headings and auth control sizing (#8074) * improvement(ui): share account status headings * improvement(ui): use shared sizing for auth controls * refactor(emcn): share invalid OTP slot treatment (#8246) * Add shared invalid state to EMCN OTP slots * fix(ui): distinguish OTP verification from resend errors * test(ui): type OTP input queries as inputs * test(ui): import OTP test button prop type * test(ui): use shared logger mock in OTP regression --------- Co-authored-by: Bill Leoutsakos <billleoutsakos@Bills-MacBook-Pro.local> --------- Co-authored-by: Bill Leoutsakos <billleoutsakos@Bills-MacBook-Pro.local> Co-authored-by: Waleed <walif6@gmail.com> * improvement(ui): share missing resource states (#8078) Co-authored-by: Bill Leoutsakos <billleoutsakos@Bills-MacBook-Pro.local> * improvement(ui): reuse avatars for owners and members (#8079) Co-authored-by: Bill Leoutsakos <billleoutsakos@Bills-MacBook-Pro.local> * fix(ui): adopt exact shared 18px typography token * fix(ui): preserve panel and auth control semantics --------- Co-authored-by: Bill Leoutsakos <billleoutsakos@Bills-MacBook-Pro.local> Co-authored-by: Waleed <walif6@gmail.com> * fix(ui): preserve shared tag actions and product styling fallbacks * fix(ui): keep logs filters on canonical chip height --------- Co-authored-by: Bill Leoutsakos <billleoutsakos@Bills-MacBook-Pro.local> Co-authored-by: Codex <codex@openai.com> Co-authored-by: Waleed <walif6@gmail.com>
1 parent 801329c commit 455191b

251 files changed

Lines changed: 3576 additions & 2682 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: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,14 @@ paths:
66

77
# EMCN Components
88

9-
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).
9+
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`, `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).
1010

1111
## Chip chrome — single source of truth
1212

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

1515
- **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`, `cellIconNodeClass` (non-chip surfaces that must visually match chip content, e.g. resource table cells), and the row-state pair `chipHoverSurfaceClass` / `chipActiveSurfaceClass` (hover vs. selected — mutually exclusive, so a selected row holds its surface through hover; every hand-rolled row imports these rather than restating the literals). All are re-exported from the `@sim/emcn` barrel — no subpath import needed.
16-
- **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.
16+
- **Pill geometry:** `chip/chip.tsx` — `chipVariants` (30px tall, `rounded-lg`, `px-2`, icon↔text `gap-1.5`). Every pill-shaped trigger (`ChipSelect`, `ChipDatePicker`) reuses it for visual parity.
1717

1818
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.
1919

@@ -24,15 +24,15 @@ The menu surface intentionally diverges from the pill: `dropdown-menu.tsx` items
2424
- **`Chip` / `ChipLink`** — the pill button (`<button>` / Next `<Link>`). Variants: `primary`, `destructive`, `border-shadow`, `border`, `outline` (a true `--border` border, no shadow or hover fill); the bare chip is implicit (omit `variant`). `filled` is deliberately NOT a `Chip` variant — it is reserved for chip fields/triggers. For a selected/toggle chip use the `active` prop, never a variant. `leftIcon`/`rightIcon`, `active`, `fullWidth`. Chips carry **no outer margin** — space between them is the parent's `gap`. The old `mx-0.5` default and its `flush` opt-out are gone; do not reintroduce either, and never add a margin to a chip through `className`.
2525
- **`ChipInput`** — single-line text field. `icon`, `endAdornment`, `error`, `inputClassName` (inner `<input>`); `className` styles the chrome wrapper.
2626
- **`ChipCopyInput`** — the canonical view-only field: a read-only `ChipInput` at full opacity with a trailing copy-to-clipboard button. View-only is a display mode, not a disabled state — reach for it (or `ChipModalField type='copy'`) over a `disabled` (greyed) input for values the user cannot edit.
27-
- **`ChipTextarea`** — multi-line sibling. `error`, `resizable` (off by default), `viewOnly` (read-only at full opacity with the default cursor — the multi-line counterpart of `ChipCopyInput`).
28-
- **`ChipDropdown`** — pill that opens a menu. Single OR multi-select via the discriminated `multiple` prop (one component, not two). Owns its trailing chevron — no `rightIcon`.
29-
- **`ChipSelect` / `ChipCombobox`** — `Combobox`-backed pickers with search, groups, multi-select; for richer lists than `ChipDropdown`.
30-
- **`ChipModal` + `ChipModalField`** — declarative compact modal. The field's `type` (`input` | `email` | `textarea` | `dropdown` | `copy` | `file` | `emails` | `custom`) picks the control and **owns all chrome** — consumers describe intent, never pass `variant`/`className`/`id` to the inner control. `custom` is the escape hatch. **Every body field MUST be a `ChipModalField`** — never hand-roll a field row (raw `<div>` + hand-rolled `<p>`/`<label>` title + bare `ChipInput`/`ChipTextarea`). `ChipModalBody` applies `px-2` + `gap-4`; `ChipModalField` adds another `px-2`, so each field lands at effective `px-4`, exactly matching the `px-4` header/footer — a hand-rolled row skips that gutter and sits misaligned at `px-2`. For controls the field doesn't cover (`ChipCombobox`, `ChipSelect`, `DatePicker`, `TimePicker`, `ButtonGroup`, arbitrary JSX), use `type='custom'` with a `title` — it still applies the gutter and renders the canonical `Label`.
31-
- **`ChipSwitch`** — segmented pill control (built from `chipVariants`).
27+
- **`ChipTextarea`** — multi-line sibling. `monospace` for structured text, `error`, `resizable` (off by default), `viewOnly` (read-only at full opacity with the default cursor — the multi-line counterpart of `ChipCopyInput`).
28+
- **`ChipSelect`** — canonical menu selection. Single selection uses `value`/`onChange`; `multiSelect` uses `multiSelectValues`/`onMultiSelectChange`, checkbox rows, and stays open. Supports groups, search aliases, rich labels/icons, disabled options, optional single-selection checks, and trigger refs. Rich labels require a nonempty `searchTerms` array containing their visible text, followed by any aliases; plain string/number labels are searchable automatically. `showAllOption` clears to an empty selection; without it, use `placeholder` for the empty state. Owns its chevron. `dropdownWidth` accepts `'trigger'`, `'content'`, or pixels; `modal={false}` keeps outside-dialog filters non-modal, while nested dialogs retain their focus lock.
29+
- **`ChipCombobox`** — shared chip styling over the separate `Combobox` engine for editable values, asynchronous results, and virtualized lists. Plain menu selection, including searchable menus, uses `ChipSelect`.
30+
- **`ChipModal` + `ChipModalField`** — declarative compact modal. `orientation='horizontal'` places a field title beside its control, wraps on narrow surfaces, and keeps error/hint text below the row; vertical is the default. The field's `type` (`input` | `email` | `textarea` | `dropdown` | `copy` | `file` | `emails` | `custom`) picks the control and **owns all chrome** — consumers describe intent, never pass `variant`/`className`/`id` to the inner control. `custom` is the escape hatch. **Every body field MUST be a `ChipModalField`** — never hand-roll a field row (raw `<div>` + hand-rolled `<p>`/`<label>` title + bare `ChipInput`/`ChipTextarea`). `ChipModalBody` applies `px-2` + `gap-4`; `ChipModalField` adds another `px-2`, so each field lands at effective `px-4`, exactly matching the `px-4` header/footer — a hand-rolled row skips that gutter and sits misaligned at `px-2`. For controls the field doesn't cover (`ChipCombobox`, `ChipSelect`, `DatePicker`, `TimePicker`, `ButtonGroup`, arbitrary JSX), use `type='custom'` with a `title` — it still applies the gutter and renders the canonical `Label`.
31+
- **`ChipButtonGroup` / `ChipSwitch`** — one Radix radio-group implementation with zero-gap segments and `default`/`compact` density. `ChipButtonGroup` accepts compound children; `ChipSwitch` adapts an options array.
3232
- **`ChipTag`** — 20px inline tag/badge (`mono`/`gray`/`invite`), not a pill trigger.
3333
- **`ChipDatePicker`** — chip-styled date field.
3434
- **`ChipTimePicker`** — minute-granular time sibling of `ChipDatePicker`, a `ChipInput` that leniently parses typed input (`9:47`, `947`, `2:05pm`, `14:30`), commits on Enter/blur, and re-renders the canonical `9:47 AM` label.
35-
- **`DropdownMenu`** — the canonical context/action menu (Radix-backed). Not a chip, but the standard menu for command/action lists; reach for it instead of a hand-rolled popover. Its surface intentionally diverges from the chip pill (`text-small`, `gap-2`) — keep them distinct. For a pill that opens a value picker, use `ChipDropdown`/`ChipSelect` instead.
35+
- **`DropdownMenu`** — the canonical context/action menu (Radix-backed). Not a chip, but the standard menu for command/action lists; reach for it instead of a hand-rolled popover. Its surface intentionally diverges from the chip pill (`text-small`, `gap-2`) — keep them distinct. For a pill that opens a value picker, use `ChipSelect` instead.
3636
- **`useScrollEdges` + `scrollFadeClass` / `scrollFadeAttributes`** — the canonical scroll-region edge treatment. The hook reports which edges hide content (tracking scroll and resizes; pass the element itself, held in state, when the region mounts after its owner, e.g. inside a Radix portal); the class and attributes fade a fixed 12px band at an active edge only, so a list that fits or sits at its top is never fogged. A floating control over the top edge sets `--scroll-fade-inset` to its height. A region that scrolls sideways (a tab row, a chip strip) uses `useScrollEdges(ref, { axis: 'x' })` with `scrollFadeXClass`; the attributes helper is shared. Any divider beside the region belongs to the neighboring block (`border-b` above, `border-t` below), never to the masked element, and shows only while that edge is active. Never hand-roll a `mask-image` gradient for a scroll region.
3737
- **`OverflowText`** — the canonical single-line overflow treatment for read-only human labels and titles. It owns `min-w-0`, fade-only clipping (never an ellipsis), the conditional 18px edge mask, and the full-value floating tooltip; consumers pass only layout/typography through `className`. `overflowTextClipClass` and `overflowTextFadeClass` are the complete base/faded treatments for the rare component that must own measurement itself; never pair either with `truncate`, `text-ellipsis`, or hover-time mask removal. Use `DropdownMenuItemLabel` for a menu label beside icons, checks, or actions. A non-editable `Combobox` passes the full visual value through `overlayLabel`; the combobox owns the visual overlay's fade and keeps its one accessible tooltip on the interactive layer. Keep ordinary `truncate` only for editable values, code/log/path content, dense or virtualized grids, and rich composite content that cannot supply a plain tooltip label. Multiline copy uses an intentional `line-clamp-*` treatment instead.
3838

@@ -50,7 +50,7 @@ Declare keyboard intent on the action-owning primitive; never add document-level
5050

5151
- **One source of truth for shared chrome.** Compose from `chip-chrome.ts` / `chipVariants`; never duplicate the chrome string.
5252
- **`cn()` for a single state toggle, CVA for genuine multiple variants.** A lone `error` boolean is `cn()`, not a CVA variant.
53-
- **Discriminated-union props for modes** (e.g. `multiple`, the modal field `type`) instead of near-duplicate components.
53+
- **Discriminated-union props for modes** (e.g. `multiSelect`, the modal field `type`) instead of near-duplicate components.
5454
- **Delete legacy variants after migration** — don't leave dead paths (this paradigm removed `Input variant='chip'` and `ChipMultiSelect`).
5555
- **Verify CSS vars exist.** An undefined var resolves to `currentColor` (caused a real black-border bug). Align to the canonical tokens: normal weight, `--text-body`, `--text-icon`.
5656
- Use Radix UI primitives for accessibility. Export the component and its `variants` (when using CVA). Document with TSDoc + a usage example.
@@ -70,3 +70,7 @@ geometry only; colour, radius and SVG stroke continue to come from the selected
7070
Use `shape='round'` for circular actions, or omit it to retain the current radius.
7171
`size='inline'` is a 20px-high action with caption typography and compact horizontal
7272
padding. Prefer these supported props to size, padding and radius overrides.
73+
74+
### Chip sizing and centered actions
75+
76+
`Chip`, `ChipLink`, and `ChipInput` retain their default 30px height. Use `size="lg"` for the existing auth-scale 36px controls; the larger size follows the root font size. `Chip` and `ChipLink` accept `align="center"` to center their icon/label group, including full-width form actions. Long labels still shrink and retain their overflow treatment. Avoid descendant flex overrides to center chip labels. The default geometry exports remain unchanged for static consumers.

‎.claude/rules/sim-settings-pages.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ Settings pages never use a literal `text-[Npx]` class — always the named Tailw
117117
scale token from the `@theme` block in `apps/sim/app/_styles/globals.css` (`text-micro`
118118
10px, `text-xs` 11px, `text-caption` 12px, `text-small` 13px, `text-sm` 14px
119119
[Tailwind default, unmodified], `text-base` 15px, `text-md` 16px, `text-lg` 18px
120-
[Tailwind default]). A literal size is either a straight rename to the equivalent
120+
[the exact Tailwind default adopted in globals.css]). A literal size is either a straight rename to the equivalent
121121
token (if the pixel value matches one exactly) or a sign the page never migrated —
122122
grep `text-\[1[0-8]px\]` under `apps/sim/app/workspace/*/settings/**` and
123123
`apps/sim/ee/**` to find stragglers.

‎.claude/rules/sim-styling.md‎

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,11 @@ setWidth: (width) => {
4747

4848
## Text Scale
4949

50-
Custom font sizes (the `@theme` block in `apps/sim/app/_styles/globals.css`): `text-micro`=10px, `text-xs`=11px, `text-caption`=12px, `text-small`=13px, `text-base`=15px. `text-sm` is Tailwind default 14px. Field titles use `text-small` (13px); hints/errors use `text-caption` (12px).
50+
Use the named scale in `apps/sim/app/_styles/globals.css`: `text-micro`=10px, `text-xs`=11px, `text-caption`=12px, `text-small`=13px, `text-base`=15px, `text-md`=16px, `text-title`=28px, `text-title-lg`=32px, and `text-display`=40px. The explicitly adopted stock entries retain their rem units and default leading: `text-sm`=0.875rem, `text-lg`=1.125rem, `text-xl`=1.25rem, and `text-2xl`=1.5rem (14/18/20/24px at the default root size). Field titles use `text-small`; hints/errors use `text-caption`; workflow titles use `text-md`.
51+
52+
Preserve explicit line heights when replacing arbitrary sizes. Where the old size inherited its line height, use `leading-[inherit]` if the named utility would otherwise add a different default. Keep document-relative heading/code recipes and the thinking loader's proportional label sizing distinct from ordinary UI text.
53+
54+
Use `font-inter` within the existing scoped Inter loading boundary; the utility does not load the font globally. Keep monospace for code, JSON, and credentials.
5155

5256
Icons default `size-[14px]`. Equal h/w → `size-*` (`size-[14px]`, `size-4`), never `h-N w-N`.
5357

@@ -67,7 +71,7 @@ A scroll region that can hide rows past an edge uses `useScrollEdges` with `scro
6771

6872
## Font Weight
6973

70-
Three steps, Tailwind's stock scale, nothing else: **`font-normal` (400)**, **`font-medium` (500)**, **`font-semibold` (600)**. 400 is the document default, so body text, chip labels, sidebar items, and headings carry **no weight class at all** — they inherit. Reach for a class only to step *up* from body.
74+
Three steps, explicitly adopted from Tailwind's stock scale in globals.css, nothing else: **`font-normal` (400)**, **`font-medium` (500)**, **`font-semibold` (600)**. 400 is the document default, so body text, chip labels, sidebar items, and headings carry **no weight class at all** — they inherit. Reach for a class to step *up* from body or to reset meaningful inherited weight. Raw CSS references the same `--font-weight-normal`, `--font-weight-medium`, and `--font-weight-semibold` definitions; their values remain 400/500/600.
7175

7276
Never write an arbitrary weight (`font-[380]`, `font-[430]`, `font-[450]`, …), and never set `fontWeight` in an inline `style`. There was previously a CSS-variable weight scale (`--font-weight-base/medium/semibold`, remapping `font-medium` to 440/480) plus seven ad-hoc values clustered between 380 and 500; it was deleted because nothing read as hierarchical. Off-scale values are only acceptable where the design system genuinely cannot reach — react-email templates and the static `apps/sim/emails/broadcasts/*.html`, which email clients render without CSS variables.
7377

0 commit comments

Comments
 (0)