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

v1.2.16

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;

SangamToggle 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 { SangamButton, SangamInput, SangamBadge } from "sangam-ui";

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

6.2 Full app example

// src/App.tsx
import {
  SangamButton,
  SangamCard,
  SangamInput,
  SangamDropdown,
  SangamSearchField,
  SangamCheckbox,
  SangamBadge,
  SangamToast,
  SangamLoader,
  SangamSkeleton,
  SangamToggle,
} 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>
        <SangamButton variant="primary" size="big">
          Create resource
        </SangamButton>
      </header>

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

7. Component Examples

7.1 SangamButton

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

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

7.2 SangamInput

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

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

  return (
    <div className="space-y-4 max-w-sm">
      <SangamInput
        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)}
      />
      <SangamInput
        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 SangamDropdown

import { useState } from "react";
import { SangamDropdown } 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 (
    <SangamDropdown
      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 SangamSearchField

import { useEffect, useState } from "react";
import { SangamSearchField, SangamLoader } 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">
      <SangamSearchField
        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 && <SangamLoader size="small" aria-label="Searching" />}
        <span>{isLoading ? "Searching…" : "Type to search across all resources."}</span>
      </div>
    </div>
  );
}

7.5 SangamCheckbox

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

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

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

7.6 SangamBadge

import { SangamBadge } from "sangam-ui";

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

7.7 SangamToast

import { useState } from "react";
import { SangamButton, SangamToast } 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">
      <SangamButton variant="primary" onClick={handleSave}>
        Save settings
      </SangamButton>
      {visible && (
        <div className="fixed bottom-6 right-6 z-50">
          <SangamToast
            variant="success"
            title="Settings saved"
            description="Your changes have been applied."
            showClose
          />
        </div>
      )}
    </div>
  );
}

7.8 SangamLoader

import { useState } from "react";
import { SangamButton, SangamLoader, SangamCard } 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">
      <SangamCard className="flex items-center justify-center gap-3 py-8">
        {isLoading ? (
          <>
            <SangamLoader 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>
        )}
      </SangamCard>
      <SangamButton variant="primary" size="big" onClick={handleClick} disabled={isLoading}>
        {isLoading ? "Refreshing…" : "Refresh data"}
      </SangamButton>
    </div>
  );
}

7.9 SangamSkeleton

import { useState, useEffect } from "react";
import { SangamCard, SangamSkeleton } from "sangam-ui";

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

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

  return (
    <SangamCard 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>
        </>
      ) : (
        <>
          <SangamSkeleton className="h-5 w-1/2" />
          <SangamSkeleton className="h-4 w-full" />
          <SangamSkeleton className="h-4 w-5/6" />
        </>
      )}
    </SangamCard>
  );
}

7.10 SangamToggle

import { useState } from "react";
import { SangamToggle } 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>
        <SangamToggle 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>
        <SangamToggle size="small" checked={alerts} onCheckedChange={setAlerts} />
      </div>
    </div>
  );
}

8. MUI layer (primitives + theme)

sangam-ui ships MUI primitives and MuiThemeProvider. Tokens come from @esds-sangam/tokens.
Composed blocks (ButtonMenu, FilterCard, ReusableTable, pagination) live in the app, built from these primitives.

Installation

npm install sangam-ui @mui/material @emotion/react @emotion/styled

Usage

import {
  MuiThemeProvider,
  Button,
  Menu,
  MenuItem,
  Typography,
  Box,
  Tooltip,
  Divider,
} from "sangam-ui";

export function App() {
  return (
    <MuiThemeProvider mode="light">
      <Button variant="contained">Save</Button>
      {/* Compose ButtonMenu / table / pagination in your app using Menu, MenuItem, … */}
    </MuiThemeProvider>
  );
}

Dark mode

<MuiThemeProvider mode="dark">
  <Button variant="contained">Dark</Button>
</MuiThemeProvider>

Notes

  • Import standard MUI names (Button, Menu, …) — not *Sangam and not shadcn SangamButton
  • Wrap with MuiThemeProvider so token-driven theme overrides apply
  • See packages/ui/src/mui/primitives/README.md for Tanvi import-swap examples

9. License

This project is licensed under the MIT License.

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