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

smartsell-ui-kit

v0.1.18

Published

Public UI primitives and modal helpers for SmartSell applications.

Readme

smartsell-ui-kit

smartsell-ui-kit is the shared React component package for SmartSell custom modules, tenant extensions, and isolated integrations.

It provides the public UI contract that external consumers should rely on instead of importing private SmartSell app internals. The package is built around a few core goals:

  • theme-aware components powered by smartsell-theme
  • self-contained styling with no dependency on private SmartSell CSS files
  • predictable APIs for forms, modals, data tables, and common surfaces
  • async-friendly primitives for remote search and server-driven datasets

Styles

The package keeps style injection enabled for backwards compatibility. New hosts should also import the public stylesheet once from their application entrypoint, which makes CSS loading deterministic in production and SSR builds:

import 'smartsell-ui-kit/styles.css';

Highlights

  • React 18-first component API
  • Consistent theme integration through smartsell-theme
  • Form primitives such as Button, IconButton, Input, Textarea, Checkbox, DatePicker, Select, and SelectAsync
  • Feedback primitives such as Alert and ResultState
  • Display and layout primitives such as Card, CardSmall, and Separator
  • Modal helpers including Modal, ComposableModal, and createModalSlotIds
  • Data components including Table and TableAsync
  • Animations such as Loading, a shopping cart animation for loading states
  • Brand images published from the dedicated smartsell-ui-kit/img subpath
  • Local Storybook workspace for visual QA before release

Requirements

  • React 18+
  • React DOM 18+
  • smartsell-theme

Installation

npm install react@^18 react-dom@^18 smartsell-theme smartsell-ui-kit

Quick start

Wrap your module with ThemeProvider from smartsell-theme, then consume the UI kit components normally.

import { ThemeProvider } from 'smartsell-theme';
import { Button, Card } from 'smartsell-ui-kit';

export default function ExamplePage() {
  return (
    <ThemeProvider>
      <Card title="Example" eyebrow="UI kit">
        <Button onClick={() => window.history.back()}>
          Go back
        </Button>
      </Card>
    </ThemeProvider>
  );
}

Main exports

  • Button, IconButton, Checkbox, DatePicker, Input, Textarea, Select, SelectAsync
  • Alert, ResultState
  • Card, CardSmall, Separator
  • Modal, ComposableModal, createModalSlotIds
  • Table, TableAsync
  • Loading
  • useUiKitTheme
  • smartsell-ui-kit/img for branded logo, icon, favicon, and background image components

Images subpath

Import brand images from the dedicated img subpath so the main entrypoint stays focused on UI primitives.

import { Icon192, Logo, Texture1 } from 'smartsell-ui-kit/img';

export default function BrandHeader() {
  return (
    <div style={{ display: 'grid', gap: 16 }}>
      <Logo style={{ width: 240 }} />
      <Icon192 size={72} alt="SmartSell app icon" />
      <Texture1 decorative style={{ width: '100%', maxWidth: 420 }} />
    </div>
  );
}

All image components resolve their source from the active smartsell-theme context. Right now light and dark modes point to the same files, but the submodule already swaps sources by theme variant so future dark-specific images only require changing the mapped filenames.

Standard modal actions

Use closeAction, cancelAction, confirmAction, or extraActions when the modal owns its footer actions. These actions render the public Button component internally, so changes to the shared button styles are automatically reflected in every standard modal action.

<Modal
  isOpen={isOpen}
  onClose={() => setIsOpen(false)}
  title="Customer details"
  closeAction={{
    label: 'Close',
    onClick: () => setIsOpen(false),
  }}
>
  <div>Customer data</div>
</Modal>

Pass a custom footer only when the application needs a composition that cannot be represented by the standard action properties.

Composable modal pattern

Use ComposableModal when the page should own modal state and business logic, while host applications or extension layers customize the header, body, and footer through stable slot ids.

import { useState } from 'react';

import { Button, ComposableModal, createModalSlotIds } from 'smartsell-ui-kit';

const slotIds = createModalSlotIds('order-review');

export default function ExamplePage() {
  const [isOpen, setIsOpen] = useState(false);

  const submitOrder = () => {
    setIsOpen(false);
  };

  return (
    <>
      <Button onClick={() => setIsOpen(true)}>Open order review</Button>

      <ComposableModal
        isOpen={isOpen}
        onClose={() => setIsOpen(false)}
        onConfirm={submitOrder}
        title="Order review"
        slotBaseId="order-review"
        renderSlot={({ id, context, children }) => {
          if (id === slotIds.footer) {
            return (
              <Button onClick={() => context.confirm?.()}>
                Send order {context.payload?.orderNumber}
              </Button>
            );
          }

          return children;
        }}
        payload={{ orderNumber: 'SO-1001', total: 421.9 }}
      >
        <div>Default modal body</div>
      </ComposableModal>
    </>
  );
}

Slot ids generated by createModalSlotIds('order-review'):

  • order-review:modal-header
  • order-review:modal-body
  • order-review:modal-footer

This pattern is useful when:

  • the page owns data loading, permissions, and submission flow
  • the host app needs stable extension points instead of private imports
  • multiple modal variants should reuse the same contract

Async components

SelectAsync is designed for remote option sources while preserving the same interaction model as the regular Select.

import { useState } from 'react';

import { SelectAsync, type SelectAsyncOption } from 'smartsell-ui-kit';

type CustomerOption = SelectAsyncOption<string>;

function CustomerField() {
  const [value, setValue] = useState<CustomerOption | null>(null);

  return (
    <SelectAsync
      label="Customer"
      value={value}
      onChange={setValue}
      loadOptions={async (query, loadedOptions, { page, signal }) => {
        const response = await fetch(`/api/customers?search=${query}&page=${page}`, { signal });
        const data = await response.json();

        return {
          options: data.items.map((item: { id: string; tradeName: string }) => ({
            value: item.id,
            label: item.tradeName,
          })),
          hasMore: data.hasMore,
        };
      }}
    />
  );
}

TableAsync follows the same idea for remote datasets, with debounced search and request cancellation support built into the component flow.

Both Table and TableAsync accept the optional onRowClick callback. When provided, it receives the clicked item and its visible row index. The desktop table row and the responsive mobile card use the same callback; the only visual change is the pointer cursor. Built-in row action buttons stop propagation so their handlers do not also trigger onRowClick.

<Table
  items={customers}
  desktopColumns={desktopColumns}
  mobileColumns={mobileColumns}
  onRowClick={(customer) => openCustomer(customer)}
/>

Development workflow

Useful local commands:

npm run storybook
npm run test
npm run validate
npm run validate:release

Run Storybook in Docker when you want containerized local development:

docker compose up --build

Storybook workspace

The repository includes a local Storybook workspace for component review and design QA.

  • grouped navigation for forms, feedback, surfaces, modals, and tables
  • a dedicated Images section covering logos, icons, and backgrounds from smartsell-ui-kit/img
  • live Controls for props and responsive behavior
  • a local ThemeProvider preview using the same smartsell-theme contract expected by host apps
  • a Theme/Colors playground for temporary Storybook-only token overrides

Storybook is development-only. It is not published to npm because the package only ships dist, README.md, and LICENSE.

Theme overrides from the Theme/Colors story affect only the local Storybook session. Runtime consumers still read theme values from their host application's smartsell-theme integration.

Release checklist

Before publishing a new version:

npm run validate
npm run build:storybook
npm pack --dry-run
npm publish

License

This package is distributed under the SmartSell proprietary license described in LICENSE.