npm package: @scalably/ui
Description: Scalably Design System - Shared UI Components
Author: Scalably
Latest version: 0.11.1

# Scalably UI Component Library

![license](https://img.shields.io/npm/l/@scalably/ui)
![npm version](https://img.shields.io/npm/v/@scalably/ui)
![npm downloads](https://img.shields.io/npm/dm/@scalably/ui)
![npm bundle size](https://img.shields.io/bundlephobia/minzip/@scalably/ui)
![TypeScript](https://img.shields.io/badge/TypeScript-5.9.3-blue)
![Node](https://img.shields.io/badge/Node-%3E%3D18-green)
![React](https://img.shields.io/badge/React-%3E%3D18-blue)
![Tailwind](https://img.shields.io/badge/Tailwind-3.4-38bdf8?logo=tailwindcss&logoColor=white)
![Coverage](https://img.shields.io/badge/coverage-vitest-blue)
![Maintained](https://img.shields.io/badge/maintained-yes-brightgreen)

A modern, accessible, and fully isolated React component library built with TypeScript and Tailwind CSS. Designed to work seamlessly across multiple projects without style conflicts.

## 🚀 Features

- **Style Isolation**: All components use prefixed Tailwind classes (`sui-*`) to prevent conflicts
- **TypeScript First**: Full type safety with comprehensive prop interfaces
- **Accessible**: Built with accessibility best practices and WCAG compliance
- **Modern**: Uses latest React patterns and modern build tools (tsup, Vite, Storybook)
- **Rich Text Editing**: TipTap-based editor with character counting, media embeds, and formatting toolbar
- **Customizable**: Flexible variant system with class-variance-authority
- **Comprehensive**: 20+ production-ready components with full documentation
- **Production Ready**: Complete build pipeline with CommonJS and ES modules
- **Icons & Assets**: First-class icon set, avatar placeholders, and logo assets
- **Utilities & Helpers**: Shared date helpers, form helpers, and utility functions (`cn`, `debounce`, `throttle`, `scopeClass`)

## 📦 Installation

```bash
npm install @scalably/ui
```

## 🎨 Usage

### 1. Use Components (Styles Auto-Injected)

The `ScalablyUIProvider` automatically injects styles into `document.head`, so you don't need to manually import CSS. This ensures styles work correctly in portals (modals, tooltips, etc.). No wrapper or scope class is required—just wrap your app once.

```tsx
import { ScalablyUIProvider, Form, FormField, Input, Button, ToastContainer, Toast } from "@scalably/ui";

export default function App() {
  return (
    <ScalablyUIProvider>
      <main>
        <Form onSubmit={(e) => e.preventDefault()}>
          <FormField label="Email" htmlFor="email">
            <Input id="email" type="email" placeholder="Enter your email" />
          </FormField>
          <Button className="sui-mt-4" type="submit">Submit</Button>
        </Form>

        {/* Toasts: render each Toast inside ToastContainer for fixed stacking */}
        <ToastContainer>
          <Toast status="success" title="Welcome" message="You're all set." />
        </ToastContainer>
      </main>
    </ScalablyUIProvider>
  );
}
```

### 2. Optional: Manual CSS Import

If you prefer to import CSS manually (e.g., for better control or SSR optimization), you can disable automatic injection:

```tsx
// In your main.tsx, App.tsx, or index.tsx
import "@scalably/ui/styles";
import { ScalablyUIProvider } from "@scalably/ui";

export default function App() {
  return (
    <ScalablyUIProvider injectStyles={false}>
      {/* your app */}
    </ScalablyUIProvider>
  );
}
```

### 3. Portal Support

Styles automatically work in portaled components (modals, tooltips, popovers, etc.) because:
- Styles are injected globally into `document.head`
- CSS variables propagate to all elements
- No parent selector dependencies

```tsx
// Tooltips, modals, and other portaled components work automatically
<Tooltip content="This works in portals!" portal>
  <Button>Hover me</Button>
</Tooltip>
```

Note: Toasts are declarative. Put each `Toast` **inside** `ToastContainer` so they stack in a fixed corner of the viewport (`Toast` is not positioned on its own). Toggle visibility with state; use `onClose` after auto-dismiss or when the user dismisses the toast to unmount or dequeue. `ToastContainer` accepts `position` (e.g. `top-right`, `bottom-center`), optional `width`, and `className`. `Toast` uses `message` for body text, `status` of `success` | `info` | `warn` | `error`, plus optional `actions`, `autoCloseMs`, and `showProgress`.

### 4. React import guidance

If you reference the React namespace (e.g., `React.useState`, `React.forwardRef`, `React.SVGProps`) add an explicit import to avoid UMD global errors:

```ts
import * as React from "react";
```

Alternatively, import only what you use with the automatic JSX runtime:

```ts
import { useState, forwardRef } from "react";
import type { SVGProps } from "react";
```

### Helper Functions
- **Date helpers**: `addMonths`, `clampDate`, `daysGrid`, `endOfMonth`, `formatDateLocalized`, `isSameDay`, `monthsForLocale`, `startOfMonth`, `toDateKey`, `weekdaysForLocale`
- **Form helpers**: `fieldErrorToProps`, `zodErrorsToSummary` and `type FieldErrorLike`
- **Utilities**: `cn` (Tailwind-aware className helper), `debounce`, `throttle`, `scopeClass` for safely building `sui-*` class strings

### Example Usage

```tsx
import {
  AuthPrompt,
  BackToTop,
  Button,
  CheckboxGroup,
  DatePicker,
  FileUpload,
  Input,
  Pagination,
  RichTextEditor,
  Select,
  Toast,
  ToastContainer,
  ViewToggle,
} from '@scalably/ui';

// Button with variants
<Button variant="destructive">Delete</Button>;
<Button variant="outline">Cancel</Button>;
<Button loading>Loading...</Button>;

// Input with validation
<Input
  label="Email Address"
  type="email"
  placeholder="Enter your email"
  error="Please enter a valid email"
/>;

// Date picker
<DatePicker
  mode="single"
  placeholder="Select a date"
  onChange={(date) => console.log(date)}
/>;

// Toast notifications (declarative; nest Toast inside ToastContainer)
<ToastContainer position="top-right">
  <Toast
    status="success"
    title="Success!"
    message="Your changes have been saved."
  />
</ToastContainer>;

// Rich text editor
<RichTextEditor
  value="<p>Start writing...</p>"
  onChange={(value) => console.log(value)}
  maxCharacters={5000}
/>;

// Marketing-style auth prompt card
<AuthPrompt
  title="Welcome back"
  description="Log in to manage your campaigns."
  primaryActionLabel="Log in"
  onPrimaryAction={() => console.log('login')}
/>;

// Toggle between list and grid views
<ViewToggle view="list" onViewChange={(v) => console.log(v)} />;

// Scroll-to-top helper
<BackToTop />;
```

## 🧩 Components & APIs Overview

This library exposes a set of composable, production-ready primitives. The full list of exports is available in `src/index.ts`, but the main groups are:

- **Form primitives**: `Form`, `FormField`, `FormErrorSummary`, `Input`, `SearchInput`, `QuantityInput`, `CheckBox`, `CheckBoxGroup`, `Radio`, `RadioGroup`, `Select`, `Switch`, `FileUpload`, `DateInput`, `DatePicker`, `TimePicker`
- **Feedback & status**: `StatusBadge`, `Toast`, `ToastContainer` (with `ToastPosition`, stacking at viewport edge), `Skeleton`, `SkeletonText`, `Countdown`
- **Layout & navigation**: `Tabs`, `TabsList`, `TabsTrigger`, `TabsContent`, `Pagination`, `BackToTop`, `ViewToggle`, `Divider`
- **Content & rich text**: `RichTextEditor`, `RichTextViewer`, `Tooltip`, `AuthPrompt`, `LoadingScreen`, `WelcomeBackground`
- **Media & brand**: `AvatarPlaceholder`, `Logo`, `logoAssets`, `defaultAssets`, `welcomeAssets` (all inline React SVGs—no static paths required)
- **Icons**: All icons from `@/icons` and `@/icons/company` are re-exported, e.g. `SearchIcon`, `CalendarIcon`, `SuccessIcon`, `ErrorIcon`, `DiscordIcon`, `FacebookIcon`, `XIcon`, `YoutubeIcon`, and more.

## 🖼️ Assets

All library assets are inline React SVG components (welcome background, logos, avatar placeholders). You can use them directly via the exported asset maps (`welcomeAssets`, `logoAssets`, `defaultAssets`) or override props (size, className, aria-label). If you prefer external URLs (e.g., CDN), pass your own `backgroundImage`, `ellipsesData`, or `linesSrc` to `WelcomeBackground`.

## 🎨 Styling

### Style Isolation

All components use prefixed Tailwind classes (`sui-*`) to ensure complete style isolation:

```tsx
// ✅ Correct - components are properly isolated
<Button>Isolated Button</Button>

// ✅ Your global styles won't break the components
<div className="p-4 bg-red-500">
  <Button>Isolated Button</Button>
</div>
```

### Custom Styling

You can still customize components using the `className` prop:

```tsx
<Button className="sui-w-full sui-mt-4">Full Width Button</Button>
<Card className="sui-max-w-md sui-mx-auto">Centered Card</Card>
```

## 🛠 Development

### Prerequisites

- Node.js 18+
- npm or pnpm

### Setup

```bash
# Clone the repository
git clone git@github.com:quangnle/scalably-components.git
cd scalably-components

# Install dependencies
npm install

# Start Storybook for development
npm run storybook

# Build the library
npm run build

# Run tests
npm test
```

### Available Scripts

- `npm run dev` - Start development build with watch mode
- `npm run build` - Build the library for production
- `npm run build:css` - Build CSS styles separately
- `npm run storybook` - Start Storybook development server
- `npm run build-storybook` - Build Storybook for production
- `npm run lint` - Run ESLint
- `npm run lint:fix` - Run ESLint with auto-fix
- `npm run type-check` - Run TypeScript type checking
- `npm test` - Run tests
- `npm run test:watch` - Run tests in watch mode
- `npm run test:ci` - Run tests with coverage
- `npm run verify` - Run verification script
- `npm run clean` - Clean build artifacts

## 📚 Documentation

Visit our Storybook documentation for:

- Interactive component playground
- Comprehensive prop documentation
- Usage examples and best practices
- Design system guidelines
- Accessibility testing with axe-core
- Design tokens and theming

You can access the latest published Storybook at: `https://scalably.com/dev/storybook`.

## 🎨 Design System

### Colors

- **Primary**: `#36499B` – Main brand color for primary actions (`sui-bg-primary`, `sui-text-primary`)
- **Secondary**: `#F2F6FC` – Light background colors (`sui-bg-secondary`, `sui-text-secondary`)
- **Success**: `#22BC4D` – Positive actions and success states (`sui-bg-success`, `sui-text-success`)
- **Warning**: `#FF7A00` – Caution and in-progress states (`sui-bg-warning`, `sui-text-warning`)
- **Info**: `#2772F0` – Informational content (`sui-bg-info`, `sui-text-info`)
- **Error**: `#EA3540` – Destructive actions and errors (`sui-bg-error`, `sui-text-error`)
- **Inactive**: `#777E90` – Inactive or completed states (`sui-text-inactive`, `sui-border-inactive`)
- **Disabled**: `#B2BBC7` – Disabled elements (`sui-text-disabled`, `sui-border-disabled`)

### Typography

- **Font Family**: Poppins (primary), Inter (fallback), system-ui
- **Font Sizes**: 12px, 14px, 16px, 20px, 24px with 150% line-height and -2% letter-spacing
- **Font Weights**: 400 (normal), 500 (medium), 600 (semibold), 700 (bold)

## 🔧 Configuration

### Fonts

The library uses the **Poppins** font family (with **Inter** and system fonts as fallbacks) and loads Poppins via Google Fonts from within the bundled CSS. In most setups you do **not** need to add additional font imports, but if your build pipeline blocks remote `@import` rules, you can alternatively include Poppins yourself (e.g., via Google Fonts or `@fontsource/poppins`) to ensure consistent typography.

### Tailwind Config

The library uses a custom Tailwind configuration with:

- **Prefix**: `sui-` for all utility classes (provides style isolation)
- **Global Styles**: Automatically injected via `ScalablyUIProvider`
- **CSS Variables**: Design tokens available globally for theming
- **Custom Colors**: Brand-specific color palette
- **Custom Shadows**: Soft, medium, and strong shadow variants
- **Portal Support**: Styles work in portaled components (modals, tooltips, etc.)

### Build Configuration

- **Bundler**: tsup for fast, modern builds
- **Formats**: CommonJS and ES modules with proper exports
- **TypeScript**: Full type definitions included
- **Tree Shaking**: Optimized for minimal bundle size
- **CSS**: Separate CSS build with Tailwind compilation
- **Source Maps**: Generated for debugging
- **Minification**: Production builds are minified

## 📦 Publishing

### Version Management

We use semantic versioning (semver):

- **Major** (1.0.0): Breaking changes
- **Minor** (0.1.0): New features, backward compatible
- **Patch** (0.0.1): Bug fixes, backward compatible

### Publishing Process

```bash
# Build the library
npm run build

# Run tests
npm test

# Publish to registry
npm publish
```

### Development Guidelines

- Follow TypeScript best practices
- Write comprehensive Storybook stories
- Ensure accessibility compliance
- Add tests for new components
- Update documentation

## 📄 License

This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.

## 🆘 Support

- **Documentation**: Storybook (run `npm run storybook`)
- **Issues**: GitHub Issues
- **Discussions**: GitHub Discussions

## 🧪 Testing

The library includes comprehensive testing setup:

- **Unit Tests**: Vitest with React Testing Library
- **Accessibility**: jest-axe for a11y testing
- **Coverage**: Built-in coverage reporting
- **CI/CD**: Automated testing pipeline

## 🔧 Dependencies

### Peer Dependencies
- React 18+
- React DOM 18+

### Key Dependencies
- `@floating-ui/react` - Positioning for tooltips and popovers
- `class-variance-authority` - Component variant management
- `date-fns` - Date manipulation utilities
- `clsx` - Conditional class names
- `tailwind-merge` - Tailwind class merging
- `@tiptap/react` & `@tiptap/starter-kit` - Rich text editor foundation
- `@tiptap/extension-*` - Rich text extensions (character count, image, alignment, placeholder, YouTube, etc.)

---

Built with ❤️ by the Scalably team