npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@cmgfi/clear-ds

v1.3.6

Published

CMG Financial — Clear Design System React component library

Readme

@cmgfi/clear-ds

Clear Design System — CMG Financial's official React component library.

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.


Table of Contents

  1. Installation
  2. Consumer Setup
  3. Component Catalog
  4. Token System
  5. Tech Stack
  6. Project Structure
  7. Local Development
  8. Build Output
  9. Publishing
  10. Contributing
  11. Hard-won Rules

Installation

Full install — with PrimeIcons (recommended):

npm install @cmgfi/clear-ds primeicons

Without PrimeIcons — if your composition does not use any icon-bearing components:

npm install @cmgfi/clear-ds

PrimeIcons is a peerDependency. It must be installed in the consumer app — it is not bundled into the library.


Consumer Setup

Add these three imports once, in your application root (e.g. main.tsx or App.tsx):

import '@cmgfi/clear-ds/tokens';        // CSS custom property definitions (:root)
import '@cmgfi/clear-ds/styles';        // compiled component styles
import 'primeicons/primeicons.css';     // omit if PrimeIcons not installed

Set the base font size. The token system is built on 1rem = 12px:

/* global.css or index.css */
html {
  font-size: 12px;
}

Then use components anywhere in your app:

import { Button, InputText, Modal, DataTable } from '@cmgfi/clear-ds';

Component Catalog

44 components across 7 sections. All are fully typed with JSDoc-annotated props, React.forwardRef-wrapped, and documented in Storybook.

Buttons

All button components share a canonical three-size ramp:

| Size | Height | Font | Padding | |------|--------|------|---------| | sm | 24px | 10px | 4px 12px | | md | 36px | 12px | 8px 14px (default) | | lg | 40px | 15px | 10px 16px |

Icon sizes are explicitly defined per button size using design tokens:

| Button size | Icon token | Value | |-------------|------------|-------| | sm | --icon-size-md | 14px | | md | --icon-size-lg | 18px | | lg | --icon-size-lg | 18px |

Pass any React node to icon props — PrimeIcons (<i className="pi pi-*" />) are the standard.

| Component | Props | Description | |---|---|---| | Button | variant, size, leadingIcon, trailingIcon, badge | Primary action button. Variants: primary (default), secondary, ghost, danger, link. Sizes: sm, md (default), lg. Optional leading/trailing PrimeIcon slots and a notification badge. | | IconButton | icon, size, variant | Circular icon-only button. Sizes: sm (24×24px), md (36×36px, default), lg (40×40px). Tooltip showing aria-label rendered automatically on hover. | | DropdownButton | label, items, size, variant, leadingIcon, disabled | Button that opens a dropdown action menu. Chevron uses pi pi-chevron-down. Sizes: sm, md (default), lg. | | SplitButton | label, items, size, variant, leadingIcon, disabled | Two joined pill buttons: main action (left) + dropdown trigger (right). primary — both halves dark teal. secondary — both halves light teal. Sizes: sm, md (default), lg. Chevron trigger shows a tooltip with triggerAriaLabel on hover. | | LightningButton | items, size, variant, disabled | Icon-only quick-action button with pi pi-bolt + pi pi-chevron-down. Variants: basic, filled. Sizes: sm, md (default), lg. Tooltip showing aria-label rendered automatically on hover. | | CloseButton | size, disabled | Circular pi pi-times dismiss button. Default size is sm (24×24px). Sizes: sm (default), md (36×36px), lg (40×40px). Tooltip showing aria-label rendered automatically on hover. |

Form Controls

| Component | Description | |---|---| | InputText | Single-line text input with label, helper text, and validation states. | | TextArea | Multi-line text input. Props: showLabel (boolean), counterPlacement (inside | outside). | | RichTextEditor | Tiptap-based rich text editor. Every toolbar button (showBold, showItalic, showUnderline, showStrike, showCode, showHighlight, showSubscript, showSuperscript, showHeading, showBlockquote, showCodeBlock, showBulletList, showOrderedList, showTaskList, showHorizontalRule, showTextAlign, showLink, showImage, showMention, showUndo, showRedo) is an individual boolean prop. toolbarMode: always (default) | on-focus. mentionSuggestions accepts a static array or async function for @ mention data. | | Checkbox | Controlled checkbox with label. Supports indeterminate state. | | RadioButton | Single radio option. Compose multiples in a group. | | Select | Single-select dropdown. | | MultiSelect | Multi-select dropdown with chip display and search. | | ListBox | Inline scrollable single or multi-select list. | | SelectButton | Segmented button group for mutually exclusive options. Variants: group (joined toggles) and options (split button + dropdown). size prop (sm/md/lg). Each option accepts an optional icon?: React.ReactNode rendered before its label. | | DatePicker | Date input with calendar popover. | | ToggleSwitch | On/off toggle. | | FileUpload | Drag-and-drop file upload with type validation. |

Data

| Component | Description | |---|---| | DataTable | Feature-rich table with sorting, filtering, row selection, grouping, inline editing, column resizing, and row expansion. | | Paginator | Standalone pagination control for use with any data list. | | Picklist | Dual-list transfer control for moving items between two sets. |

Messages

| Component | Description | |---|---| | BannerAlert | Full-width page-level alert. Severities: info, success, warning, error. | | InlineAlert | Compact inline alert for form-level feedback. | | InlineContainedAlert | Bordered inline alert for use inside panels or cards. | | Toast / Toaster / toast | Programmatic toast notifications. Mount <Toaster /> once; call toast(options) anywhere. |

Overlay

| Component | Description | |---|---| | Modal | Dialog overlay with header, body, and footer action slots. | | Drawer | Slide-in panel overlay. | | SidePanel / SidePanelLayout | Fixed side panel for persistent secondary content. | | Popup | Anchored floating popup (e.g. contextual menus, micro-overlays). | | Tooltip | Hover tooltip. Placements: top, bottom, left, right. |

Panel

| Component | Description | |---|---| | Card | Surface container with optional header and footer. | | Accordion | Collapsible section list. Variants: default, flush. | | Tabs | Horizontal tab navigation with panel content. | | BannerTabs | Loan-workflow tab bar with status chips and badge groups. |

Status

| Component | Description | |---|---| | SeverityChip | Color-coded severity label. Variants: info, success, warning, error. | | MiscChip | General-purpose label chip with configurable color. | | ProfileChip | Avatar-style chip showing a person's initials or image. | | AUSChip | Automated Underwriting System result chip. | | ProgressBar | Horizontal progress indicator. | | ProgressSpinner | Circular loading indicator. Large variant includes the Clear brand logo. |

Navigation

| Component | Description | |---|---| | TopBar | App-level top navigation bar with logo, nav items, user menu, and notifications. | | TopBarMobile | Mobile variant of TopBar with drawer-based navigation. | | URLATabsNav | URLA section tab navigation with applicant selector. Responsive: desktop, tablet, mobile variants. | | LoanBannerNav | Loan detail page banner with action toolbar, condition badges, and collapsible sections. | | FullNav / FullNavMobile | Full application navigation shell with primary and secondary nav areas. |


Token System

All tokens are CSS custom properties defined in src/tokens/tokens.css and exported via @cmgfi/clear-ds/tokens.

Base scale: 1rem = 12px — the consumer app must set html { font-size: 12px }.

Colors

Seven families, 10 shades each (-50 lightest → -900 darkest):

| Family | Prefix | Role | |---|---|---| | Teal | --teal-* | Brand color; primary actions, focus rings, active states | | Surface | --surface-* | Neutral backgrounds; 50 = white, 900 = near-black | | Navy | --navy-* | Text and borders | | Yellow | --yellow-* | Warning severity | | Blue | --blue-* | Informational severity | | Green | --green-* | Success severity | | Red | --red-* | Error severity |

Semantic aliases:

--color-text:           var(--navy-800);   /* primary body text */
--color-text-secondary: var(--navy-500);   /* helper / secondary text */

Spacing

Two complementary scales:

/* Numeric — direct pixel mapping */
--spacing-02: 2px;   --spacing-04: 4px;   --spacing-06: 6px;
--spacing-08: 8px;   --spacing-12: 12px;  --spacing-16: 16px;
--spacing-20: 20px;  --spacing-24: 24px;  --spacing-32: 32px;
--spacing-40: 40px;

/* Named aliases — semantic */
--spacing-xxxs: 2px;   --spacing-xxs: 4px;   --spacing-xs: 6px;
--spacing-sm:   8px;   --spacing-md:  12px;  --spacing-lg: 16px;
--spacing-xl:   24px;  --spacing-xxl: 32px;

Typography

Font family: Open Sans (--font-family: 'Open Sans', sans-serif)

Heading utility classes: .text-h1 through .text-h6

Body utility classes — 4 sizes × 3 weights = 12 combinations:

.text-xl-bold       .text-xl-semibold       .text-xl-regular
.text-large-bold    .text-large-semibold     .text-large-regular
.text-normal-bold   .text-normal-semibold    .text-normal-regular
.text-small-bold    .text-small-semibold     .text-small-regular

Elevation

--shadow-depth-1: 0 1px 4px rgba(0,0,0,0.12);
--shadow-depth-2: 0 2px 8px rgba(0,0,0,0.16);
--shadow-depth-3: 0 4px 16px rgba(0,0,0,0.20);
--shadow-depth-4: 0 8px 32px rgba(0,0,0,0.24);
--scrim: /* navy-500 at 25% opacity */;

Icons

--icon-size-sm: 12px;
--icon-size-md: 14px;
--icon-size-lg: 18px;
--icon-size-xl: 24px;

Usage with PrimeIcons:

<i className="pi pi-check" style={{ fontSize: 'var(--icon-size-lg)' }} />

Grid

/* Mobile — 4 column */
--grid-columns-mobile: 4;   --grid-gutter-mobile: 16px;
--grid-margin-mobile: 24px; --grid-container-mobile: 576px;

/* Tablet — 6 column */
--grid-columns-tablet: 6;   --grid-gutter-tablet: 16px;
--grid-margin-tablet: 48px; --grid-container-tablet: 768px;

/* Desktop — 12 column */
--grid-columns-desktop: 12; --grid-gutter-desktop: 18px;
--grid-margin-desktop: 128px; --grid-container-desktop: 1200px;

/* Breakpoints */
--breakpoint-sm: 576px;   --breakpoint-md: 768px;   --breakpoint-lg: 1200px;

Tech Stack

| Concern | Choice | Notes | |---|---|---| | Framework | React >=17 | Peer dep — never bundled; consumer's React is used | | Language | TypeScript 5 strict | Exported types are part of the public API | | Build | Vite 5 library mode | ESM + CJS dual output; CSS Modules built-in | | Type declarations | vite-plugin-dts rollupTypes: true | Single rolled-up index.d.ts | | Styles | CSS Modules | Scoped, zero runtime overhead; token vars pass through without JS | | Docs / QA | Storybook v8 @storybook/react-vite | Shares the same Vite config; no duplicate setup | | Icons | PrimeIcons >=7 | CSS font glyphs; pi pi-* class API; must be peer dep | | Node / npm | 22 / 11 (LTS) | |

Not present (intentional):

  • No CSS-in-JS — runtime overhead is unacceptable in a library
  • No Tailwind — custom token system conflicts with Tailwind's class API
  • No third-party component library — pixel-fidelity requires full control

Project Structure

clear-ds/
├── src/
│   ├── index.ts                        ← explicit public API; all component + type exports
│   ├── tokens/
│   │   ├── tokens.css                  ← all CSS custom properties (428 lines)
│   │   ├── TokenDocs.module.css        ← shared layout for token Storybook pages
│   │   ├── Colors.stories.tsx
│   │   ├── Typography.stories.tsx
│   │   ├── Spacing.stories.tsx
│   │   ├── Elevation.stories.tsx
│   │   ├── Icons.stories.tsx
│   │   └── Grid.stories.tsx
│   └── components/
│       └── ComponentName/
│           ├── ComponentName.tsx       ← React.forwardRef; JSDoc props; displayName
│           ├── ComponentName.module.css← token vars only; no hardcoded values
│           ├── ComponentName.stories.tsx← autodocs; named states; AllStates last
│           └── index.ts               ← re-exports component + all types
├── .storybook/
│   ├── main.ts                         ← framework, addons, stories glob
│   └── preview.ts                      ← global CSS imports, backgrounds, storySort
├── dist/                               ← build output (not committed)
├── vercel.json                         ← Storybook deployment config
├── vite.config.ts
├── tsconfig.json
└── package.json

Local Development

Prerequisites: Node 22, npm 11

# Install dependencies
npm install

# Start Storybook dev server (localhost:6006)
npm run storybook

# Build the library (outputs to dist/)
npm run build

# Type-check without emitting
npm run type-check

# Watch mode for library build
npm run build:watch

Adding a new component

Every component follows the same invariant structure — do not deviate:

  1. Create src/components/ComponentName/ with four files:
    • ComponentName.tsx — always React.forwardRef; props extend React.HTMLAttributes<HTMLElement>; every prop has a JSDoc comment; set displayName; spread ...props; merge className
    • 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
    • ComponentName.stories.tsxtags: ['autodocs'] on meta; individual state stories; AllStates is always the last export
    • index.ts — re-export component and all types
  2. Add exports to src/index.ts
  3. Add the component to storySort.order in .storybook/preview.ts

Storybook conventions

  • Sidebar order: Best Practices → Tokens → Components (Buttons, Data, Form Controls, Messages, Overlay, Panel, Status) → Navigation
  • tags: ['autodocs'] is mandatory on every meta — without it there is no Docs tab
  • AllStates (or AllSizes / AllSeverities) must always be the last story export — Storybook renders in declaration order
  • 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)

Build Output

dist/
├── index.mjs          ← ESM bundle (tree-shakeable), 230 KB
├── index.cjs          ← CommonJS bundle, 153 KB
├── index.css          ← all compiled component styles, 126 KB
├── index.d.ts         ← single rolled-up TypeScript declarations, 81 KB
├── index.mjs.map      ← ESM source map
├── index.cjs.map      ← CJS source map
└── tokens/
    └── tokens.css     ← design tokens (separate import path), 14 KB

Vite config highlights:

  • cssCodeSplit: false — all CSS Modules compile into a single index.css
  • external: ['react', 'react/jsx-runtime', 'react-dom'] — React is never bundled
  • rollupTypes: true — single index.d.ts instead of one file per source file
  • assetFileNames: 'index.css' — predictable output filename
  • postbuild npm script — copies src/tokens/tokens.cssdist/tokens/tokens.css

Publishing

Releases are fully automated by GitHub Actions — do not run npm publish or vercel deploy from a laptop. Teammates need only write access to the repo (via XD Team membership); CI uses org-owned NPM_TOKEN and VERCEL_TOKEN secrets.

npm — publish a new version

git checkout main && git pull
npm version patch        # or: minor / major
git push origin main && git push origin --tags
gh run watch             # confirm the publish workflow goes green

The v* tag push triggers .github/workflows/publish-npm.yml, which builds and publishes @cmgfi/clear-ds to public npm.

Storybook (Vercel)

Storybook deploys automatically on every push to main via .github/workflows/deploy-vercel.yml, hosted at clear-ds.cmgfinancial.ai under the cmgprojects team. No manual vercel deploy needed.

vercel.json configures the build:

{
  "buildCommand": "npm run build-storybook",
  "outputDirectory": "storybook-static"
}

Full release instructions and the emergency manual fallback live in RELEASING.md.


Contributing

Git workflow

  • Remote: https://github.com/cmg-pilot-program/clear-ds.git
  • Branch from main, PR back to main
  • Commit messages follow conventional commits (feat:, fix:, chore:, docs:)
  • No approval required to merge — open a PR, wait for the build check (.github/workflows/ci.yml) to pass, then squash-merge your own PR. Admins can --admin-bypass in a true emergency. See RELEASING.md for the full release flow.

Before submitting a PR

  • [ ] npm run type-check passes with zero errors
  • [ ] npm run build completes successfully
  • [ ] New component has all four required files
  • [ ] tags: ['autodocs'] is on the story meta
  • [ ] AllStates is the last story export
  • [ ] No hardcoded colors or spacing values — token vars only
  • [ ] src/index.ts exports the new component and all its types
  • [ ] Component added to storySort.order in .storybook/preview.ts

CI runs npm run build + npm run build-storybook on every PR (the required build check). A PR cannot merge until it passes — but no human approval is needed.


Hard-won Rules

These are not guidelines — they are invariants derived from real failures during the build of this library:

| Rule | Why | |---|---| | 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. | | AllStates always last story export | Storybook renders in declaration order. A misplaced overview story at the top pollutes the component's story list. | | primeicons in peerDependencies, not devDependencies | If it is only in devDeps, it will not be available in consumer bundles — icon glyphs silently disappear. | | 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. | | Never hardcode colors or spacing | Always use token vars. Hardcoded values break theming and make design drift invisible. | | React.forwardRef on every component | Consumers need ref access for focus management, animations, and third-party integrations. | | :focus-visible not :focus | :focus shows focus rings on mouse clicks. :focus-visible restricts them to keyboard navigation. | | 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. | | Merge consumer className, never replace | Spreading ...props is not enough. Explicitly merge: className={[styles.root, props.className].filter(Boolean).join(' ')}. | | The postbuild script must stay in package.json | It copies src/tokens/tokens.cssdist/tokens/tokens.css. Without it, the @cmgfi/clear-ds/tokens import path resolves to a file that does not exist. |


Design Source

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.

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.