@cmgfi/clear-ds 1.0.1 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +478 -0
- package/dist/llms.txt +1642 -0
- package/dist/tokens/tokens.css +102 -0
- package/package.json +1 -1
package/README.md
ADDED
|
@@ -0,0 +1,478 @@
|
|
|
1
|
+
# @cmgfi/clear-ds
|
|
2
|
+
|
|
3
|
+
**Clear Design System** — CMG Financial's official React component library.
|
|
4
|
+
|
|
5
|
+
The UI foundation for CMG's internal loan origination platform. Built from scratch for pixel-fidelity control, token ownership, and zero dead code. Every component was designed in Pencil before it was written in code.
|
|
6
|
+
|
|
7
|
+
- **npm:** [`@cmgfi/clear-ds`](https://www.npmjs.com/package/@cmgfi/clear-ds)
|
|
8
|
+
- **Storybook:** [clear-pimkxt5rp-cmgprojects.vercel.app](https://clear-pimkxt5rp-cmgprojects.vercel.app)
|
|
9
|
+
- **Version:** 1.0.1
|
|
10
|
+
- **License:** UNLICENSED (CMG Financial internal)
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## Table of Contents
|
|
15
|
+
|
|
16
|
+
1. [Installation](#installation)
|
|
17
|
+
2. [Consumer Setup](#consumer-setup)
|
|
18
|
+
3. [Component Catalog](#component-catalog)
|
|
19
|
+
4. [Token System](#token-system)
|
|
20
|
+
5. [Tech Stack](#tech-stack)
|
|
21
|
+
6. [Project Structure](#project-structure)
|
|
22
|
+
7. [Local Development](#local-development)
|
|
23
|
+
8. [Build Output](#build-output)
|
|
24
|
+
9. [Publishing](#publishing)
|
|
25
|
+
10. [Contributing](#contributing)
|
|
26
|
+
11. [Hard-won Rules](#hard-won-rules)
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## Installation
|
|
31
|
+
|
|
32
|
+
**Full install — with PrimeIcons (recommended):**
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
npm install @cmgfi/clear-ds primeicons
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
**Without PrimeIcons** — if your composition does not use any icon-bearing components:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
npm install @cmgfi/clear-ds
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
> PrimeIcons is a `peerDependency`. It must be installed in the consumer app — it is not bundled into the library.
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## Consumer Setup
|
|
49
|
+
|
|
50
|
+
Add these three imports once, in your application root (e.g. `main.tsx` or `App.tsx`):
|
|
51
|
+
|
|
52
|
+
```tsx
|
|
53
|
+
import '@cmgfi/clear-ds/tokens'; // CSS custom property definitions (:root)
|
|
54
|
+
import '@cmgfi/clear-ds/styles'; // compiled component styles
|
|
55
|
+
import 'primeicons/primeicons.css'; // omit if PrimeIcons not installed
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Set the base font size. The token system is built on **`1rem = 12px`**:
|
|
59
|
+
|
|
60
|
+
```css
|
|
61
|
+
/* global.css or index.css */
|
|
62
|
+
html {
|
|
63
|
+
font-size: 12px;
|
|
64
|
+
}
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Then use components anywhere in your app:
|
|
68
|
+
|
|
69
|
+
```tsx
|
|
70
|
+
import { Button, InputText, Modal, DataTable } from '@cmgfi/clear-ds';
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
---
|
|
74
|
+
|
|
75
|
+
## Component Catalog
|
|
76
|
+
|
|
77
|
+
43 components across 7 sections. All are fully typed with JSDoc-annotated props, `React.forwardRef`-wrapped, and documented in Storybook.
|
|
78
|
+
|
|
79
|
+
### Buttons
|
|
80
|
+
|
|
81
|
+
| Component | Description |
|
|
82
|
+
|---|---|
|
|
83
|
+
| `Button` | Primary action button. Variants: `primary`, `secondary`, `ghost`, `danger`. Sizes: `sm`, `md`, `lg`. |
|
|
84
|
+
| `IconButton` | Square button containing a single PrimeIcon. |
|
|
85
|
+
| `DropdownButton` | Button that opens a dropdown action menu. |
|
|
86
|
+
| `SplitButton` | Combined primary action + dropdown trigger in one control. |
|
|
87
|
+
| `LightningButton` | Quick-action button with a lightning icon affordance. |
|
|
88
|
+
| `CloseButton` | Compact `×` dismiss button used inside overlays and alerts. |
|
|
89
|
+
|
|
90
|
+
### Form Controls
|
|
91
|
+
|
|
92
|
+
| Component | Description |
|
|
93
|
+
|---|---|
|
|
94
|
+
| `InputText` | Single-line text input with label, helper text, and validation states. |
|
|
95
|
+
| `TextArea` | Multi-line text input. |
|
|
96
|
+
| `Checkbox` | Controlled checkbox with label. Supports indeterminate state. |
|
|
97
|
+
| `RadioButton` | Single radio option. Compose multiples in a group. |
|
|
98
|
+
| `Select` | Single-select dropdown. |
|
|
99
|
+
| `MultiSelect` | Multi-select dropdown with chip display and search. |
|
|
100
|
+
| `ListBox` | Inline scrollable single or multi-select list. |
|
|
101
|
+
| `SelectButton` | Segmented button group for mutually exclusive options. |
|
|
102
|
+
| `DatePicker` | Date input with calendar popover. |
|
|
103
|
+
| `ToggleSwitch` | On/off toggle. |
|
|
104
|
+
| `FileUpload` | Drag-and-drop file upload with type validation. |
|
|
105
|
+
|
|
106
|
+
### Data
|
|
107
|
+
|
|
108
|
+
| Component | Description |
|
|
109
|
+
|---|---|
|
|
110
|
+
| `DataTable` | Feature-rich table with sorting, filtering, row selection, grouping, inline editing, column resizing, and row expansion. |
|
|
111
|
+
| `Paginator` | Standalone pagination control for use with any data list. |
|
|
112
|
+
| `Picklist` | Dual-list transfer control for moving items between two sets. |
|
|
113
|
+
|
|
114
|
+
### Messages
|
|
115
|
+
|
|
116
|
+
| Component | Description |
|
|
117
|
+
|---|---|
|
|
118
|
+
| `BannerAlert` | Full-width page-level alert. Severities: `info`, `success`, `warning`, `error`. |
|
|
119
|
+
| `InlineAlert` | Compact inline alert for form-level feedback. |
|
|
120
|
+
| `InlineContainedAlert` | Bordered inline alert for use inside panels or cards. |
|
|
121
|
+
| `Toast` / `Toaster` / `toast` | Programmatic toast notifications. Mount `<Toaster />` once; call `toast(options)` anywhere. |
|
|
122
|
+
|
|
123
|
+
### Overlay
|
|
124
|
+
|
|
125
|
+
| Component | Description |
|
|
126
|
+
|---|---|
|
|
127
|
+
| `Modal` | Dialog overlay with header, body, and footer action slots. |
|
|
128
|
+
| `Drawer` | Slide-in panel overlay. |
|
|
129
|
+
| `SidePanel` / `SidePanelLayout` | Fixed side panel for persistent secondary content. |
|
|
130
|
+
| `Popup` | Anchored floating popup (e.g. contextual menus, micro-overlays). |
|
|
131
|
+
| `Tooltip` | Hover tooltip. Placements: `top`, `bottom`, `left`, `right`. |
|
|
132
|
+
|
|
133
|
+
### Panel
|
|
134
|
+
|
|
135
|
+
| Component | Description |
|
|
136
|
+
|---|---|
|
|
137
|
+
| `Card` | Surface container with optional header and footer. |
|
|
138
|
+
| `Accordion` | Collapsible section list. Variants: `default`, `flush`. |
|
|
139
|
+
| `Tabs` | Horizontal tab navigation with panel content. |
|
|
140
|
+
| `BannerTabs` | Loan-workflow tab bar with status chips and badge groups. |
|
|
141
|
+
|
|
142
|
+
### Status
|
|
143
|
+
|
|
144
|
+
| Component | Description |
|
|
145
|
+
|---|---|
|
|
146
|
+
| `SeverityChip` | Color-coded severity label. Variants: `info`, `success`, `warning`, `error`. |
|
|
147
|
+
| `MiscChip` | General-purpose label chip with configurable color. |
|
|
148
|
+
| `ProfileChip` | Avatar-style chip showing a person's initials or image. |
|
|
149
|
+
| `AUSChip` | Automated Underwriting System result chip. |
|
|
150
|
+
| `ProgressBar` | Horizontal progress indicator. |
|
|
151
|
+
| `ProgressSpinner` | Circular loading indicator. Large variant includes the Clear brand logo. |
|
|
152
|
+
|
|
153
|
+
### Navigation
|
|
154
|
+
|
|
155
|
+
| Component | Description |
|
|
156
|
+
|---|---|
|
|
157
|
+
| `TopBar` | App-level top navigation bar with logo, nav items, user menu, and notifications. |
|
|
158
|
+
| `TopBarMobile` | Mobile variant of `TopBar` with drawer-based navigation. |
|
|
159
|
+
| `URLATabsNav` | URLA section tab navigation with applicant selector. Responsive: desktop, tablet, mobile variants. |
|
|
160
|
+
| `LoanBannerNav` | Loan detail page banner with action toolbar, condition badges, and collapsible sections. |
|
|
161
|
+
| `FullNav` / `FullNavMobile` | Full application navigation shell with primary and secondary nav areas. |
|
|
162
|
+
|
|
163
|
+
---
|
|
164
|
+
|
|
165
|
+
## Token System
|
|
166
|
+
|
|
167
|
+
All tokens are CSS custom properties defined in `src/tokens/tokens.css` and exported via `@cmgfi/clear-ds/tokens`.
|
|
168
|
+
|
|
169
|
+
**Base scale:** `1rem = 12px` — the consumer app must set `html { font-size: 12px }`.
|
|
170
|
+
|
|
171
|
+
### Colors
|
|
172
|
+
|
|
173
|
+
Seven families, 10 shades each (`-50` lightest → `-900` darkest):
|
|
174
|
+
|
|
175
|
+
| Family | Prefix | Role |
|
|
176
|
+
|---|---|---|
|
|
177
|
+
| Teal | `--teal-*` | Brand color; primary actions, focus rings, active states |
|
|
178
|
+
| Surface | `--surface-*` | Neutral backgrounds; `50` = white, `900` = near-black |
|
|
179
|
+
| Navy | `--navy-*` | Text and borders |
|
|
180
|
+
| Yellow | `--yellow-*` | Warning severity |
|
|
181
|
+
| Blue | `--blue-*` | Informational severity |
|
|
182
|
+
| Green | `--green-*` | Success severity |
|
|
183
|
+
| Red | `--red-*` | Error severity |
|
|
184
|
+
|
|
185
|
+
Semantic aliases:
|
|
186
|
+
|
|
187
|
+
```css
|
|
188
|
+
--color-text: var(--navy-800); /* primary body text */
|
|
189
|
+
--color-text-secondary: var(--navy-500); /* helper / secondary text */
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
### Spacing
|
|
193
|
+
|
|
194
|
+
Two complementary scales:
|
|
195
|
+
|
|
196
|
+
```css
|
|
197
|
+
/* Numeric — direct pixel mapping */
|
|
198
|
+
--spacing-02: 2px; --spacing-04: 4px; --spacing-06: 6px;
|
|
199
|
+
--spacing-08: 8px; --spacing-12: 12px; --spacing-16: 16px;
|
|
200
|
+
--spacing-20: 20px; --spacing-24: 24px; --spacing-32: 32px;
|
|
201
|
+
--spacing-40: 40px;
|
|
202
|
+
|
|
203
|
+
/* Named aliases — semantic */
|
|
204
|
+
--spacing-xxxs: 2px; --spacing-xxs: 4px; --spacing-xs: 6px;
|
|
205
|
+
--spacing-sm: 8px; --spacing-md: 12px; --spacing-lg: 16px;
|
|
206
|
+
--spacing-xl: 24px; --spacing-xxl: 32px;
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
### Typography
|
|
210
|
+
|
|
211
|
+
Font family: **Open Sans** (`--font-family: 'Open Sans', sans-serif`)
|
|
212
|
+
|
|
213
|
+
Heading utility classes: `.text-h1` through `.text-h6`
|
|
214
|
+
|
|
215
|
+
Body utility classes — 4 sizes × 3 weights = 12 combinations:
|
|
216
|
+
```
|
|
217
|
+
.text-xl-bold .text-xl-semibold .text-xl-regular
|
|
218
|
+
.text-large-bold .text-large-semibold .text-large-regular
|
|
219
|
+
.text-normal-bold .text-normal-semibold .text-normal-regular
|
|
220
|
+
.text-small-bold .text-small-semibold .text-small-regular
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
### Elevation
|
|
224
|
+
|
|
225
|
+
```css
|
|
226
|
+
--shadow-depth-1: 0 1px 4px rgba(0,0,0,0.12);
|
|
227
|
+
--shadow-depth-2: 0 2px 8px rgba(0,0,0,0.16);
|
|
228
|
+
--shadow-depth-3: 0 4px 16px rgba(0,0,0,0.20);
|
|
229
|
+
--shadow-depth-4: 0 8px 32px rgba(0,0,0,0.24);
|
|
230
|
+
--scrim: /* navy-500 at 25% opacity */;
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
### Icons
|
|
234
|
+
|
|
235
|
+
```css
|
|
236
|
+
--icon-size-sm: 12px;
|
|
237
|
+
--icon-size-md: 14px;
|
|
238
|
+
--icon-size-lg: 18px;
|
|
239
|
+
--icon-size-xl: 24px;
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
Usage with PrimeIcons:
|
|
243
|
+
```tsx
|
|
244
|
+
<i className="pi pi-check" style={{ fontSize: 'var(--icon-size-lg)' }} />
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
### Grid
|
|
248
|
+
|
|
249
|
+
```css
|
|
250
|
+
/* Mobile — 4 column */
|
|
251
|
+
--grid-columns-mobile: 4; --grid-gutter-mobile: 16px;
|
|
252
|
+
--grid-margin-mobile: 24px; --grid-container-mobile: 576px;
|
|
253
|
+
|
|
254
|
+
/* Tablet — 6 column */
|
|
255
|
+
--grid-columns-tablet: 6; --grid-gutter-tablet: 16px;
|
|
256
|
+
--grid-margin-tablet: 48px; --grid-container-tablet: 768px;
|
|
257
|
+
|
|
258
|
+
/* Desktop — 12 column */
|
|
259
|
+
--grid-columns-desktop: 12; --grid-gutter-desktop: 18px;
|
|
260
|
+
--grid-margin-desktop: 128px; --grid-container-desktop: 1200px;
|
|
261
|
+
|
|
262
|
+
/* Breakpoints */
|
|
263
|
+
--breakpoint-sm: 576px; --breakpoint-md: 768px; --breakpoint-lg: 1200px;
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
---
|
|
267
|
+
|
|
268
|
+
## Tech Stack
|
|
269
|
+
|
|
270
|
+
| Concern | Choice | Notes |
|
|
271
|
+
|---|---|---|
|
|
272
|
+
| Framework | React `>=17` | Peer dep — never bundled; consumer's React is used |
|
|
273
|
+
| Language | TypeScript 5 strict | Exported types are part of the public API |
|
|
274
|
+
| Build | Vite 5 library mode | ESM + CJS dual output; CSS Modules built-in |
|
|
275
|
+
| Type declarations | `vite-plugin-dts` `rollupTypes: true` | Single rolled-up `index.d.ts` |
|
|
276
|
+
| Styles | CSS Modules | Scoped, zero runtime overhead; token vars pass through without JS |
|
|
277
|
+
| Docs / QA | Storybook v8 `@storybook/react-vite` | Shares the same Vite config; no duplicate setup |
|
|
278
|
+
| Icons | PrimeIcons `>=7` | CSS font glyphs; `pi pi-*` class API; must be peer dep |
|
|
279
|
+
| Node / npm | 22 / 11 (LTS) | |
|
|
280
|
+
|
|
281
|
+
**Not present (intentional):**
|
|
282
|
+
- No CSS-in-JS — runtime overhead is unacceptable in a library
|
|
283
|
+
- No Tailwind — custom token system conflicts with Tailwind's class API
|
|
284
|
+
- No third-party component library — pixel-fidelity requires full control
|
|
285
|
+
|
|
286
|
+
---
|
|
287
|
+
|
|
288
|
+
## Project Structure
|
|
289
|
+
|
|
290
|
+
```
|
|
291
|
+
clear-ds/
|
|
292
|
+
├── src/
|
|
293
|
+
│ ├── index.ts ← explicit public API; all component + type exports
|
|
294
|
+
│ ├── tokens/
|
|
295
|
+
│ │ ├── tokens.css ← all CSS custom properties (428 lines)
|
|
296
|
+
│ │ ├── TokenDocs.module.css ← shared layout for token Storybook pages
|
|
297
|
+
│ │ ├── Colors.stories.tsx
|
|
298
|
+
│ │ ├── Typography.stories.tsx
|
|
299
|
+
│ │ ├── Spacing.stories.tsx
|
|
300
|
+
│ │ ├── Elevation.stories.tsx
|
|
301
|
+
│ │ ├── Icons.stories.tsx
|
|
302
|
+
│ │ └── Grid.stories.tsx
|
|
303
|
+
│ └── components/
|
|
304
|
+
│ └── ComponentName/
|
|
305
|
+
│ ├── ComponentName.tsx ← React.forwardRef; JSDoc props; displayName
|
|
306
|
+
│ ├── ComponentName.module.css← token vars only; no hardcoded values
|
|
307
|
+
│ ├── ComponentName.stories.tsx← autodocs; named states; AllStates last
|
|
308
|
+
│ └── index.ts ← re-exports component + all types
|
|
309
|
+
├── .storybook/
|
|
310
|
+
│ ├── main.ts ← framework, addons, stories glob
|
|
311
|
+
│ └── preview.ts ← global CSS imports, backgrounds, storySort
|
|
312
|
+
├── dist/ ← build output (not committed)
|
|
313
|
+
├── vercel.json ← Storybook deployment config
|
|
314
|
+
├── vite.config.ts
|
|
315
|
+
├── tsconfig.json
|
|
316
|
+
└── package.json
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
---
|
|
320
|
+
|
|
321
|
+
## Local Development
|
|
322
|
+
|
|
323
|
+
**Prerequisites:** Node 22, npm 11
|
|
324
|
+
|
|
325
|
+
```bash
|
|
326
|
+
# Install dependencies
|
|
327
|
+
npm install
|
|
328
|
+
|
|
329
|
+
# Start Storybook dev server (localhost:6006)
|
|
330
|
+
npm run storybook
|
|
331
|
+
|
|
332
|
+
# Build the library (outputs to dist/)
|
|
333
|
+
npm run build
|
|
334
|
+
|
|
335
|
+
# Type-check without emitting
|
|
336
|
+
npm run type-check
|
|
337
|
+
|
|
338
|
+
# Watch mode for library build
|
|
339
|
+
npm run build:watch
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
### Adding a new component
|
|
343
|
+
|
|
344
|
+
Every component follows the same invariant structure — do not deviate:
|
|
345
|
+
|
|
346
|
+
1. Create `src/components/ComponentName/` with four files:
|
|
347
|
+
- `ComponentName.tsx` — always `React.forwardRef`; props extend `React.HTMLAttributes<HTMLElement>`; every prop has a JSDoc comment; set `displayName`; spread `...props`; merge `className`
|
|
348
|
+
- `ComponentName.module.css` — token vars exclusively; no hardcoded hex or pixel values (except sub-pixel precision: `border: 1px`, `outline-offset: 2px`); use `:focus-visible` not `:focus`
|
|
349
|
+
- `ComponentName.stories.tsx` — `tags: ['autodocs']` on meta; individual state stories; `AllStates` is always the last export
|
|
350
|
+
- `index.ts` — re-export component and all types
|
|
351
|
+
2. Add exports to `src/index.ts`
|
|
352
|
+
3. Add the component to `storySort.order` in `.storybook/preview.ts`
|
|
353
|
+
|
|
354
|
+
### Storybook conventions
|
|
355
|
+
|
|
356
|
+
- **Sidebar order:** `Best Practices → Tokens → Components (Buttons, Data, Form Controls, Messages, Overlay, Panel, Status) → Navigation`
|
|
357
|
+
- `tags: ['autodocs']` is mandatory on every `meta` — without it there is no Docs tab
|
|
358
|
+
- `AllStates` (or `AllSizes` / `AllSeverities`) must always be the last story export — Storybook renders in declaration order
|
|
359
|
+
- When a story `render:` function needs `useState`, define a named inner component and call the hook inside it (hooks cannot be called inside `render` directly)
|
|
360
|
+
|
|
361
|
+
---
|
|
362
|
+
|
|
363
|
+
## Build Output
|
|
364
|
+
|
|
365
|
+
```
|
|
366
|
+
dist/
|
|
367
|
+
├── index.mjs ← ESM bundle (tree-shakeable), 230 KB
|
|
368
|
+
├── index.cjs ← CommonJS bundle, 153 KB
|
|
369
|
+
├── index.css ← all compiled component styles, 126 KB
|
|
370
|
+
├── index.d.ts ← single rolled-up TypeScript declarations, 81 KB
|
|
371
|
+
├── index.mjs.map ← ESM source map
|
|
372
|
+
├── index.cjs.map ← CJS source map
|
|
373
|
+
└── tokens/
|
|
374
|
+
└── tokens.css ← design tokens (separate import path), 14 KB
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
**Vite config highlights:**
|
|
378
|
+
- `cssCodeSplit: false` — all CSS Modules compile into a single `index.css`
|
|
379
|
+
- `external: ['react', 'react/jsx-runtime', 'react-dom']` — React is never bundled
|
|
380
|
+
- `rollupTypes: true` — single `index.d.ts` instead of one file per source file
|
|
381
|
+
- `assetFileNames: 'index.css'` — predictable output filename
|
|
382
|
+
- `postbuild` npm script — copies `src/tokens/tokens.css` → `dist/tokens/tokens.css`
|
|
383
|
+
|
|
384
|
+
---
|
|
385
|
+
|
|
386
|
+
## Publishing
|
|
387
|
+
|
|
388
|
+
### npm
|
|
389
|
+
|
|
390
|
+
Auth token must be in `~/.npmrc` (never in the project `.npmrc`):
|
|
391
|
+
```
|
|
392
|
+
//registry.npmjs.org/:_authToken=<automation-token>
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
Use an **automation token** — it bypasses 2FA which is required for unattended publishes.
|
|
396
|
+
|
|
397
|
+
```bash
|
|
398
|
+
# Bump version in package.json, then:
|
|
399
|
+
npm publish
|
|
400
|
+
# prepublishOnly runs `npm run build` automatically
|
|
401
|
+
```
|
|
402
|
+
|
|
403
|
+
The package publishes to `@cmgfi/clear-ds` on public npm under the `andyg-cmg-xd` account.
|
|
404
|
+
|
|
405
|
+
### Storybook (Vercel)
|
|
406
|
+
|
|
407
|
+
The Storybook is hosted on Vercel under the `cmgprojects` team.
|
|
408
|
+
|
|
409
|
+
```bash
|
|
410
|
+
# First deploy (no .vercel/ present):
|
|
411
|
+
vercel --prod --yes --scope cmgprojects
|
|
412
|
+
|
|
413
|
+
# Subsequent deploys:
|
|
414
|
+
vercel --prod --yes
|
|
415
|
+
|
|
416
|
+
# Disable SSO protection after first deploy (new projects inherit team SSO):
|
|
417
|
+
TOKEN=$(cat ~/Library/Application\ Support/com.vercel.cli/auth.json | python3 -c "import sys,json; print(json.load(sys.stdin)['token'])")
|
|
418
|
+
curl -s -X PATCH "https://api.vercel.com/v9/projects/clear-ds?teamId=team_fm7yrQmYpugXC0N7tiglM4Bw" \
|
|
419
|
+
-H "Authorization: Bearer $TOKEN" \
|
|
420
|
+
-H "Content-Type: application/json" \
|
|
421
|
+
-d '{"ssoProtection": null}'
|
|
422
|
+
```
|
|
423
|
+
|
|
424
|
+
`vercel.json` configures the Storybook build:
|
|
425
|
+
```json
|
|
426
|
+
{
|
|
427
|
+
"buildCommand": "npm run build-storybook",
|
|
428
|
+
"outputDirectory": "storybook-static"
|
|
429
|
+
}
|
|
430
|
+
```
|
|
431
|
+
|
|
432
|
+
---
|
|
433
|
+
|
|
434
|
+
## Contributing
|
|
435
|
+
|
|
436
|
+
### Git workflow
|
|
437
|
+
|
|
438
|
+
- Remote: `https://cmg-financial.ghe.com/andyg/clear-ds.git`
|
|
439
|
+
- Branch from `main`, PR back to `main`
|
|
440
|
+
- Commit messages follow conventional commits (`feat:`, `fix:`, `chore:`, `docs:`)
|
|
441
|
+
|
|
442
|
+
### Before submitting a PR
|
|
443
|
+
|
|
444
|
+
- [ ] `npm run type-check` passes with zero errors
|
|
445
|
+
- [ ] `npm run build` completes successfully
|
|
446
|
+
- [ ] New component has all four required files
|
|
447
|
+
- [ ] `tags: ['autodocs']` is on the story meta
|
|
448
|
+
- [ ] `AllStates` is the last story export
|
|
449
|
+
- [ ] No hardcoded colors or spacing values — token vars only
|
|
450
|
+
- [ ] `src/index.ts` exports the new component and all its types
|
|
451
|
+
- [ ] Component added to `storySort.order` in `.storybook/preview.ts`
|
|
452
|
+
|
|
453
|
+
---
|
|
454
|
+
|
|
455
|
+
## Hard-won Rules
|
|
456
|
+
|
|
457
|
+
These are not guidelines — they are invariants derived from real failures during the build of this library:
|
|
458
|
+
|
|
459
|
+
| Rule | Why |
|
|
460
|
+
|---|---|
|
|
461
|
+
| `tags: ['autodocs']` on every story meta | Without it there is no Docs tab. Retroactively adding it across many files is painful. Add it when the file is created. |
|
|
462
|
+
| `AllStates` always last story export | Storybook renders in declaration order. A misplaced overview story at the top pollutes the component's story list. |
|
|
463
|
+
| `primeicons` in `peerDependencies`, not `devDependencies` | If it is only in `devDeps`, it will not be available in consumer bundles — icon glyphs silently disappear. |
|
|
464
|
+
| `publishConfig: { "access": "public" }` is required | Scoped packages are private by default on npm. Without this field, `npm publish` is rejected even with a valid token. |
|
|
465
|
+
| Never hardcode colors or spacing | Always use token vars. Hardcoded values break theming and make design drift invisible. |
|
|
466
|
+
| `React.forwardRef` on every component | Consumers need ref access for focus management, animations, and third-party integrations. |
|
|
467
|
+
| `:focus-visible` not `:focus` | `:focus` shows focus rings on mouse clicks. `:focus-visible` restricts them to keyboard navigation. |
|
|
468
|
+
| `min-width: 0` on shrinkable flex children | `flex: 1` alone does not allow an item to shrink below its content size. Without `min-width: 0`, the item overflows its container. |
|
|
469
|
+
| Merge consumer `className`, never replace | Spreading `...props` is not enough. Explicitly merge: `className={[styles.root, props.className].filter(Boolean).join(' ')}`. |
|
|
470
|
+
| The `postbuild` script must stay in `package.json` | It copies `src/tokens/tokens.css` → `dist/tokens/tokens.css`. Without it, the `@cmgfi/clear-ds/tokens` import path resolves to a file that does not exist. |
|
|
471
|
+
|
|
472
|
+
---
|
|
473
|
+
|
|
474
|
+
## Design Source
|
|
475
|
+
|
|
476
|
+
All visual decisions originate in **Pencil** — a design tool integrated as an MCP server. The `.pen` source files are in the project experiments directory alongside this package. The code is a direct translation of those designs: same structure, same hierarchy, same property names.
|
|
477
|
+
|
|
478
|
+
The token master copy lives at `../clear-ds-tokens.css` (one level above this package). When updating tokens, sync that file into `src/tokens/tokens.css`.
|