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

sangam-ui

v0.1.11

Published

ESDS Sangam is a design system for the ESDS platform. It provides a set of components and utilities to help you build your UI.

Readme

The ESDS Sangam Design System is the unified UI library for the ESDS platform. This package, sangam-ui, provides typed, accessible React components built on top of shared icons, design tokens, and global styles, so product teams can ship consistent and high‑quality interfaces quickly.

This README is written for developers who will install and use the design system in their applications.


1. Package Overview

sangam-ui is the UI components package of the Sangam Design System. It exposes reusable primitives, components, patterns, hooks, and types that are:

  • Themed using Sangam tokens and styles.
  • Accessible by default (ARIA-friendly, keyboard navigable).
  • Composable and type-safe for modern React apps.

The design system is split into multiple npm packages so you can adopt only what you need while sharing a single source of truth for the visual language.


2. Packages Included

| Layer | Package name | Description | | ------ | --------------------- | -------------------------------------------------------------------------- | | Icons | @esds-sangam/icons | SVG-based icon components and icon utilities for React. | | Tokens | @esds-sangam/tokens | Design tokens for colors, spacing, typography, radius, shadows, and more. | | Styles | @esds-sangam/styles | Global CSS, theme variables (light/dark), and Tailwind integration styles. | | UI | sangam-ui | React UI primitives and components built on top of tokens and styles. |

You are currently viewing the README for the UI package: sangam-ui.


3. Installation

Install the full stack (recommended for most app teams):

npm

npm install sangam-ui

yarn

yarn add sangam-ui

pnpm

pnpm add sangam-ui

You can also install packages individually if you only need a subset, for example:

npm install @esds-sangam/icons

4. Peer Dependencies

The design system targets modern React applications.

| Package | Version (minimum) | | ----------- | ----------------- | | react | ^18.0.0 | | react-dom | ^18.0.0 |

Ensure your application satisfies these peer dependencies to avoid warnings or runtime issues.


5. Initial Setup (Vite + React + TypeScript)

Follow these steps after React + TypeSCript Project Setup to wire sangam-ui into project.

5.1 Configure PostCSS — order matters

[!IMPORTANT] At your project root, create a postcss.config.cjs file. The postcss-import plugin must come first — if the order is wrong, Tailwind will error with "@import must precede all other statements" or "@layer base used but no matching @tailwind base directive".

// postcss.config.cjs
module.exports = {
  plugins: {
    "postcss-import": {}, // MUST be first: inlines @import before Tailwind sees the file
    tailwindcss: {},
    autoprefixer: {},
  },
};

5.2 Configure Tailwind — use the Sangam preset

[!IMPORTANT] At your project root, create a tailwind.config.cjs file. Always spread ...sangamTailwind and extend its content array — never replace it. Replacing it strips the Sangam theme (colors, spacing, radii, shadows, darkMode) and component class paths, causing broken styles.

// tailwind.config.cjs
const sangamTailwind = require("@esds-sangam/tailwind-config");

/** @type {import('tailwindcss').Config} */
module.exports = {
  ...sangamTailwind,
  content: [
    ...sangamTailwind.content,
    "./index.html",
    "./src/**/*.{ts,tsx}",
    "./node_modules/sangam-ui/dist/**/*.{js,mjs}",
  ],
};

Spreading ...sangamTailwind gives you the Sangam theme (colors, spacing, radii, shadows), darkMode setting, and any plugins — without having to duplicate them.

Base44: using .js config files

If your project (e.g. Base44) expects postcss.config.js and tailwind.config.js instead of .cjs, use the same content with the .js extension:

postcss.config.js

// postcss.config.js
export default {
  plugins: {
    "postcss-import": {}, // MUST be first: inlines @import before Tailwind sees the file
    tailwindcss: {},
    autoprefixer: {},
  },
};

tailwind.config.js

// tailwind.config.js
import sangamTailwind from "@esds-sangam/tailwind-config";
 
/** @type {import('tailwindcss').Config} */
export default {
  ...sangamTailwind,
  content: [
    ...sangamTailwind.content,
    "./index.html",
    "./src/**/*.{ts,tsx}",
    "./node_modules/sangam-ui/dist/**/*.{js,mjs}",
  ],
};

5.3 Wire styles — single CSS entry file

[!IMPORTANT] Use one main CSS file as the Tailwind entry (e.g. src/index.css). The @import must appear before the @tailwind directives:

/* src/index.css */
@import "@esds-sangam/styles/tailwind.css"; /* pulls in globals, theme, and Tailwind layers */

@tailwind base;
@tailwind components;
@tailwind utilities;

Common mistakes

  • Putting @tailwind before @import → build error: "@import must precede all other statements"
  • Importing individual Sangam CSS files (e.g. theme.css, dark.css) directly in main.tsx → each file is a separate PostCSS pass, causing "@layer base used but no matching @tailwind base directive" errors

5.4 App entry — import only your CSS file

In src/main.tsx, import only your single CSS file. Do not import Sangam CSS files directly here:

// src/main.tsx
import "./index.css"; // ← only this; Sangam styles are pulled in via @import inside index.css

import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App";

ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);

5.5 Dark theme (optional)

To support dark mode, add the dark theme import inside src/index.css:

/* src/index.css */
@import "@esds-sangam/styles/tailwind.css";
@import "@esds-sangam/styles/dark.css"; /* add this line */

@tailwind base;
@tailwind components;
@tailwind utilities;

Toggle dark mode at runtime by adding or removing the dark class on <html>:

document.documentElement.classList.toggle("dark");

5.6 Setup checklist

| Step | Action | | ---- | ------------------------------------------------------------------------------------------------------------------------- | | 1 | Install sangam-ui, @esds-sangam/styles, @esds-sangam/tokens, @esds-sangam/icons | | 2 | Install tailwindcss, postcss, autoprefixer, postcss-import as dev dependencies | | 3 | In postcss.config.cjs — put postcss-import first, then tailwindcss, then autoprefixer | | 4 | In tailwind.config.cjs — spread sangamTailwind and extend content to include src/**/* and esds-sangam/dist/**/* | | 5 | In src/index.css@import "@esds-sangam/styles/tailwind.css" first, then @tailwind directives | | 6 | In src/main.tsx — only import "./index.css", no direct Sangam CSS imports |


6. Usage Guide

6.1 Quick Start

Once setup is complete, import and use Sangam components anywhere in your app:

import { Button, Input, Badge } from "sangam-ui";

export function App() {
  return (
    <main className="p-6 space-y-4">
      <Input label="Project name" placeholder="my-project" />
      <div className="flex items-center gap-3">
        <Button variant="primary" size="big">
          Deploy
        </Button>
        <Badge size="small" variant="pill" state="success">
          Live
        </Badge>
      </div>
    </main>
  );
}

6.2 Full app example

// src/App.tsx
import {
  Button,
  Card,
  Input,
  Dropdown,
  SearchField,
  Checkbox,
  Badge,
  Toast,
  Loader,
  Skeleton,
  Toggle,
} from "sangam-ui";

function App() {
  return (
    <main className="mx-auto flex max-w-3xl flex-col gap-6 p-6">
      <header className="flex items-center justify-between gap-4">
        <div>
          <h1 className="text-2xl font-semibold">ESDS Console</h1>
          <p className="text-sm text-neutral-600">Manage resources across your ESDS projects.</p>
        </div>
        <Button variant="primary" size="big">
          Create resource
        </Button>
      </header>

      <Card className="space-y-4">
        <Input label="Filter by name" placeholder="production-api" />
        <div className="flex items-center justify-between gap-4">
          <Dropdown
            label="Status"
            placeholder="All statuses"
            options={[
              { value: "running", label: "Running" },
              { value: "stopped", label: "Stopped" },
            ]}
          />
          <SearchField placeholder="Search resources" />
        </div>
      </Card>
    </main>
  );
}

7. Component Examples

7.1 Button

import { Button } from "sangam-ui";
import { ChevronDown, Close } from "@esds-sangam/icons";

export function ButtonExamples() {
  return (
    <div className="flex flex-wrap gap-4">
      <Button variant="primary" size="big">
        Primary action
      </Button>
      <Button variant="secondary" size="big">
        Secondary
      </Button>
      <Button variant="danger" size="big" icon={<Close />} leadingIcon>
        Delete
      </Button>
      <Button variant="link" size="small" icon={<ChevronDown />} trailingIcon>
        View more
      </Button>
    </div>
  );
}

7.2 Input

import { useState } from "react";
import { Input } from "sangam-ui";

export function InputExample() {
  const [value, setValue] = useState("");

  return (
    <div className="space-y-4 max-w-sm">
      <Input
        label="Project name"
        required
        placeholder="Enter a project name"
        helperText="This will be visible across your ESDS account."
        value={value}
        onChange={(e) => setValue(e.target.value)}
      />
      <Input
        label="API key"
        type="password"
        placeholder="••••••••••"
        error={value.length > 0 && value.length < 8}
        helperText={
          value.length > 0 && value.length < 8
            ? "API key must be at least 8 characters."
            : "Keep this key secret."
        }
      />
    </div>
  );
}

7.3 Dropdown

import { useState } from "react";
import { Dropdown } from "sangam-ui";

const options = [
  { value: "prod", label: "Production" },
  { value: "staging", label: "Staging" },
  { value: "dev", label: "Development" },
];

export function DropdownExample() {
  const [value, setValue] = useState<string | null>(null);

  return (
    <Dropdown
      label="Environment"
      placeholder="Select environment"
      required
      options={options}
      value={value}
      onChange={setValue}
      helperText="This environment will be used for the next deployment."
      showHelperIcon
    />
  );
}

7.4 SearchField

import { useEffect, useState } from "react";
import { SearchField, Loader } from "sangam-ui";

export function SearchFieldExample() {
  const [query, setQuery] = useState("");
  const [isLoading, setIsLoading] = useState(false);

  useEffect(() => {
    if (!query) return;
    setIsLoading(true);
    const timeout = setTimeout(() => setIsLoading(false), 600);
    return () => clearTimeout(timeout);
  }, [query]);

  return (
    <div className="space-y-3 max-w-md">
      <SearchField
        placeholder="Search resources"
        value={query}
        onChange={(e) => setQuery(e.target.value)}
        showClearButton
      />
      <div className="flex items-center gap-2 text-sm text-neutral-600">
        {isLoading && <Loader size="small" aria-label="Searching" />}
        <span>{isLoading ? "Searching…" : "Type to search across all resources."}</span>
      </div>
    </div>
  );
}

7.5 Checkbox

import { useState } from "react";
import { Checkbox } from "sangam-ui";

export function CheckboxExample() {
  const [marketing, setMarketing] = useState(false);
  const [terms, setTerms] = useState(false);

  return (
    <div className="space-y-3">
      <Checkbox
        title="Email updates"
        subtext="Product tips, release notes, and marketing messages."
        checked={marketing}
        onCheckedChange={setMarketing}
      />
      <Checkbox
        title="I agree to the Terms of Service"
        checked={terms}
        onCheckedChange={setTerms}
      />
    </div>
  );
}

7.6 Badge

import { Badge } from "sangam-ui";

export function BadgeExample() {
  return (
    <div className="flex flex-wrap items-center gap-3">
      <Badge size="small" variant="pill" state="info">
        New
      </Badge>
      <Badge size="small" variant="rounded" state="warning" isSolid>
        Beta
      </Badge>
      <Badge size="small" variant="notification" state="error" isSolid>
        3
      </Badge>
    </div>
  );
}

7.7 Toast

import { useState } from "react";
import { Button, Toast } from "sangam-ui";

export function ToastExample() {
  const [visible, setVisible] = useState(false);

  function handleSave() {
    setVisible(true);
    setTimeout(() => setVisible(false), 2500);
  }

  return (
    <div className="space-y-4">
      <Button variant="primary" onClick={handleSave}>
        Save settings
      </Button>
      {visible && (
        <div className="fixed bottom-6 right-6 z-50">
          <Toast
            variant="success"
            title="Settings saved"
            description="Your changes have been applied."
            showClose
          />
        </div>
      )}
    </div>
  );
}

7.8 Loader

import { useState } from "react";
import { Button, Loader, Card } from "sangam-ui";

export function LoaderExample() {
  const [isLoading, setIsLoading] = useState(false);

  function handleClick() {
    setIsLoading(true);
    setTimeout(() => setIsLoading(false), 1500);
  }

  return (
    <div className="space-y-6 max-w-md">
      <Card className="flex items-center justify-center gap-3 py-8">
        {isLoading ? (
          <>
            <Loader size="small" aria-label="Loading data" />
            <span className="text-sm text-neutral-700">Fetching latest metrics…</span>
          </>
        ) : (
          <span className="text-sm text-neutral-700">Metrics are up to date.</span>
        )}
      </Card>
      <Button variant="primary" size="big" onClick={handleClick} disabled={isLoading}>
        {isLoading ? "Refreshing…" : "Refresh data"}
      </Button>
    </div>
  );
}

7.9 Skeleton

import { useState, useEffect } from "react";
import { Card, Skeleton } from "sangam-ui";

export function SkeletonExample() {
  const [loaded, setLoaded] = useState(false);

  useEffect(() => {
    const timeout = setTimeout(() => setLoaded(true), 1200);
    return () => clearTimeout(timeout);
  }, []);

  return (
    <Card className="w-full max-w-md p-4 space-y-3">
      {loaded ? (
        <>
          <h2 className="text-base font-semibold">Usage by region</h2>
          <p className="text-sm text-neutral-600">
            Your workload is evenly distributed across all configured regions.
          </p>
        </>
      ) : (
        <>
          <Skeleton className="h-5 w-1/2" />
          <Skeleton className="h-4 w-full" />
          <Skeleton className="h-4 w-5/6" />
        </>
      )}
    </Card>
  );
}

7.10 Toggle

import { useState } from "react";
import { Toggle } from "sangam-ui";

export function ToggleExample() {
  const [autoscale, setAutoscale] = useState(true);
  const [alerts, setAlerts] = useState(false);

  return (
    <div className="space-y-4">
      <div className="flex items-center justify-between gap-4">
        <div>
          <p className="text-sm font-medium text-neutral-900">Autoscale</p>
          <p className="text-xs text-neutral-600">
            Automatically scale instances up and down based on load.
          </p>
        </div>
        <Toggle size="big" checked={autoscale} onCheckedChange={setAutoscale} />
      </div>
      <div className="flex items-center justify-between gap-4">
        <div>
          <p className="text-sm font-medium text-neutral-900">Email alerts</p>
          <p className="text-xs text-neutral-600">
            Get notified when usage crosses configured thresholds.
          </p>
        </div>
        <Toggle size="small" checked={alerts} onCheckedChange={setAlerts} />
      </div>
    </div>
  );
}

8. License

This project is licensed under the MIT License.

See the LICENSE file at the repository root for the full license text.