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

@alga-psa/ui-kit

v0.2.0

Published

Zero-dependency React component library for Alga extensions. All components use CSS custom properties for theming and work seamlessly inside extension iframes.

Downloads

2,248

Readme

@alga-psa/ui-kit

Zero-dependency React component library for Alga extensions. All components use CSS custom properties for theming and work seamlessly inside extension iframes.

See the showcase extension (ee/extensions/showcase) for interactive demos of every component.

Install

Import the theme stylesheet once at the root of your app:

import '@alga-psa/ui-kit/theme.css';

Theme Tokens

Tokens are exposed as CSS variables. When running inside an extension iframe the host automatically injects them into :root.

| Variable | Light | Dark | Purpose | |----------|-------|------|---------| | --alga-bg | #ffffff | #0b0f14 | Background | | --alga-fg | #111111 | #e5e7eb | Foreground / text | | --alga-muted | #f5f5f7 | #0f1720 | Muted background | | --alga-muted-fg | #4b5563 | #9ca3af | Muted text | | --alga-primary | #9855ee | #8a4dea | Primary (purple) | | --alga-primary-foreground | #ffffff | #ffffff | Text on primary | | --alga-secondary | #53d7fa | #40cff9 | Secondary (blue) | | --alga-secondary-foreground | #111111 | #0b0f14 | Text on secondary | | --alga-border | #e5e7eb | #1f2937 | Borders | | --alga-radius | 8px | 8px | Border radius | | --alga-danger | #dc2626 | #ef4444 | Danger / error | | --alga-warning | #d97706 | #f59e0b | Warning | | --alga-success | #16a34a | #22c55e | Success |

You can also access tokens programmatically:

import { tokens } from '@alga-psa/ui-kit';

tokens.primary   // 'var(--alga-primary)'
tokens.bg        // 'var(--alga-bg)'

Components

Core

Button

Themed button with multiple variants and sizes.

| Prop | Type | Default | Description | |------|------|---------|-------------| | variant | 'primary' \| 'secondary' \| 'destructive' \| 'outline' \| 'ghost' \| 'link' \| 'soft' \| 'dashed' | 'primary' | Visual style | | size | 'xs' \| 'sm' \| 'md' \| 'lg' \| 'icon' | 'md' | Controls padding and font size |

<Button variant="secondary" size="sm">Save</Button>

Text

Typography component with size, tone, and weight presets.

| Prop | Type | Default | Description | |------|------|---------|-------------| | as | 'span' \| 'p' \| 'label' \| 'strong' | 'span' | HTML element to render | | size | 'xs' \| 'sm' \| 'md' \| 'lg' | 'md' | Font size preset | | tone | 'default' \| 'muted' \| 'danger' \| 'warning' \| 'success' | 'default' | Text colour | | weight | 400 \| 500 \| 600 \| 700 | 400 | Font weight |

<Text as="p" size="lg" tone="muted" weight={600}>Hello</Text>

Card

Bordered container with background, shadow, and rounded corners. Extends standard <div> attributes.

<Card style={{ padding: 24 }}>Content here</Card>

Badge

Small pill-shaped label for status indicators.

| Prop | Type | Default | Description | |------|------|---------|-------------| | tone | 'default' \| 'info' \| 'success' \| 'warning' \| 'danger' | 'default' | Colour scheme |

<Badge tone="success">Active</Badge>

Alert, AlertTitle, AlertDescription

Contextual alert banner with optional icon.

| Prop | Type | Default | Description | |------|------|---------|-------------| | tone | 'info' \| 'success' \| 'warning' \| 'danger' | 'info' | Visual tone | | showIcon | boolean | true | Show tone-specific icon |

<Alert tone="warning">
  <AlertTitle>Heads up</AlertTitle>
  <AlertDescription>This action cannot be undone.</AlertDescription>
</Alert>

Separator

Thin divider line for separating content.

| Prop | Type | Default | Description | |------|------|---------|-------------| | orientation | 'horizontal' \| 'vertical' | 'horizontal' | Direction of the line | | style | CSSProperties | — | Additional inline styles |


Form

Input

Text input with error state support.

| Prop | Type | Default | Description | |------|------|---------|-------------| | error | boolean | — | Show error styling | | errorMessage | string | — | Error message displayed below input |

<Input placeholder="Email" error={!!err} errorMessage={err} />

TextArea

Multi-line text input. Extends <textarea> attributes.

| Prop | Type | Default | Description | |------|------|---------|-------------| | error | boolean | — | Show error border | | errorMessage | string | — | Error text below the field | | resize | 'none' \| 'vertical' \| 'horizontal' \| 'both' | 'vertical' | Resize behaviour |

CustomSelect

Dropdown with search filtering.

| Prop | Type | Default | Description | |------|------|---------|-------------| | options | SelectOption[] | — | Available options | | value | string | — | Selected value | | onChange | (value: string) => void | — | Change callback | | placeholder | string | — | Placeholder text | | searchable | boolean | true | Enable search filtering | | disabled | boolean | false | Disable the select |

SearchInput

Input with built-in search icon and optional debounce.

| Prop | Type | Default | Description | |------|------|---------|-------------| | onSearch | (value: string) => void | — | Debounced search callback | | debounceMs | number | 300 | Debounce delay in ms | | size | 'sm' \| 'md' | 'md' | Input size |

Checkbox

Themed checkbox with label.

| Prop | Type | Default | Description | |------|------|---------|-------------| | label | string | — | Label text | | size | 'sm' \| 'md' \| 'lg' | 'md' | Size preset |

RadioGroup

Group of mutually exclusive radio buttons.

| Prop | Type | Default | Description | |------|------|---------|-------------| | options | RadioOption[] | — | Available options | | value | string | — | Selected value | | onChange | (value: string) => void | — | Change callback | | name | string | auto | HTML name attribute | | disabled | boolean | false | Disable all options | | orientation | 'horizontal' \| 'vertical' | 'vertical' | Layout direction | | size | 'sm' \| 'md' \| 'lg' | 'md' | Size preset |

RadioOption: { value: string; label: string; disabled?: boolean }

<RadioGroup
  options={[{ value: 'a', label: 'Alpha' }, { value: 'b', label: 'Beta' }]}
  value={selected}
  onChange={setSelected}
/>

Switch

Toggle switch for boolean values.

| Prop | Type | Default | Description | |------|------|---------|-------------| | checked | boolean | — | Controlled state | | onChange | (checked: boolean) => void | — | Change callback | | size | 'sm' \| 'md' \| 'lg' | 'md' | Size preset | | disabled | boolean | false | Disable the switch | | label | string | — | Label text |

Label

Styled <label> element.

| Prop | Type | Default | Description | |------|------|---------|-------------| | required | boolean | — | Show required indicator | | disabled | boolean | — | Apply muted styling |


Data Display

DataTable

Paginated table with sorting, search, and custom cell rendering.

| Prop | Type | Default | Description | |------|------|---------|-------------| | data | Row[] | — | Array of row objects | | columns | Column<Row>[] | — | Column definitions | | initialSortKey | string | — | Initial sort column | | searchable | boolean | false | Show search input | | pageSize | number | 10 | Rows per page |

Column<Row>: { key: string; header: string; width?: number; sortable?: boolean; render?: (row) => ReactNode }

<DataTable
  data={items}
  columns={[
    { key: 'name', header: 'Name', sortable: true },
    { key: 'status', header: 'Status', render: (row) => <Badge>{row.status}</Badge> },
  ]}
/>

Navigation

Tabs

Horizontal tab bar.

| Prop | Type | Default | Description | |------|------|---------|-------------| | tabs | TabItem[] | — | Tab definitions | | activeTab | string | — | Currently active tab id | | onChange | (id: string) => void | — | Tab change callback |

TabItem: { id: string; label: string; icon?: ComponentType; disabled?: boolean }

<Tabs
  tabs={[{ id: 'one', label: 'Tab 1' }, { id: 'two', label: 'Tab 2' }]}
  activeTab={tab}
  onChange={setTab}
/>

ViewSwitcher

Segmented button group for switching between views.

| Prop | Type | Default | Description | |------|------|---------|-------------| | currentView | T | — | Active view value | | onChange | (view: T) => void | — | View change callback | | options | ViewSwitcherOption<T>[] | — | Available views |

ViewSwitcherOption<T>: { value: T; label: string; icon?: ComponentType }

<ViewSwitcher
  currentView={view}
  onChange={setView}
  options={[{ value: 'list', label: 'List' }, { value: 'grid', label: 'Grid' }]}
/>

Breadcrumbs

Horizontal breadcrumb navigation trail.

| Prop | Type | Default | Description | |------|------|---------|-------------| | items | BreadcrumbItem[] | — | Breadcrumb segments | | separator | ReactNode | '/' | Custom separator |

BreadcrumbItem: { label: ReactNode; href?: string; onClick?: () => void }

<Breadcrumbs items={[
  { label: 'Home', href: '/' },
  { label: 'Settings', onClick: goSettings },
  { label: 'Profile' },
]} />

DropdownMenu

Popover menu triggered by a button.

| Prop | Type | Default | Description | |------|------|---------|-------------| | trigger | ReactNode | — | Trigger element | | items | DropdownMenuItem[] | — | Menu items |

DropdownMenuItem: { label: string; onClick?: () => void; icon?: ComponentType; danger?: boolean; disabled?: boolean; separator?: boolean }


Layout

Stack

Flexbox layout helper for stacking children with consistent spacing.

| Prop | Type | Default | Description | |------|------|---------|-------------| | direction | 'row' \| 'column' | 'column' | Flex direction | | gap | number \| string | 8 | Gap between children (numbers = px) | | align | 'stretch' \| 'flex-start' \| 'center' \| 'flex-end' \| 'baseline' | — | Cross-axis alignment | | justify | 'flex-start' \| 'center' \| 'flex-end' \| 'space-between' \| 'space-around' \| 'space-evenly' | — | Main-axis alignment |

<Stack direction="row" gap={12} align="center">
  <Text>Left</Text>
  <Button>Right</Button>
</Stack>

Drawer

Slide-in panel from the right edge with focus trapping and overlay.

| Prop | Type | Default | Description | |------|------|---------|-------------| | open | boolean | — | Whether the drawer is open | | onClose | () => void | — | Close callback | | width | string | 'fit-content' | CSS width | | maxWidth | string | '60vw' | Max width | | title | ReactNode | — | Drawer title | | overlay | boolean | true | Show backdrop overlay | | closeOnOverlayClick | boolean | true | Close on overlay click | | closeOnEscape | boolean | true | Close on Escape key |

Dialog / ConfirmDialog

Modal dialogs.

Dialog — base modal with title and children.

| Prop | Type | Default | Description | |------|------|---------|-------------| | isOpen | boolean | — | Open state | | onClose | () => void | — | Close callback | | title | string | — | Dialog title |

ConfirmDialog — confirm / cancel modal.

| Prop | Type | Default | Description | |------|------|---------|-------------| | isOpen | boolean | — | Open state | | title | string | — | Title | | message | string | — | Body text | | variant | 'default' \| 'danger' | 'default' | Visual tone | | onConfirm | () => void | — | Confirm callback | | onCancel | () => void | — | Cancel callback |

<ConfirmDialog
  isOpen={open}
  title="Delete Item"
  message="Are you sure?"
  variant="danger"
  onConfirm={handleDelete}
  onCancel={() => setOpen(false)}
/>

Popover

Positioned content popover anchored to a trigger element.

| Prop | Type | Default | Description | |------|------|---------|-------------| | trigger | ReactNode | — | Trigger element | | open | boolean | — | Controlled open state | | onOpenChange | (open: boolean) => void | — | Open state callback | | placement | 'top' \| 'bottom' \| 'left' \| 'right' | 'bottom' | Popover placement |


Feedback

Spinner / LoadingIndicator

Spinner — animated circular loading indicator.

| Prop | Type | Default | Description | |------|------|---------|-------------| | size | 'button' \| 'xs' \| 'sm' \| 'md' \| 'lg' | 'md' | Diameter preset | | variant | 'default' \| 'inverted' | 'default' | Use 'inverted' on coloured backgrounds |

LoadingIndicator — spinner with optional text.

| Prop | Type | Default | Description | |------|------|---------|-------------| | size | same as Spinner | 'md' | Spinner size | | text | string | — | Label next to spinner | | layout | 'inline' \| 'stacked' | 'inline' | Text position |

<Spinner size="sm" />
<LoadingIndicator text="Loading..." layout="stacked" />

Tooltip

Hover tooltip anchored to its children.

| Prop | Type | Default | Description | |------|------|---------|-------------| | content | ReactNode | — | Tooltip content | | placement | 'top' \| 'bottom' \| 'left' \| 'right' | 'top' | Position | | delay | number | 200 | Show delay in ms |

<Tooltip content="More info">
  <Button variant="ghost">?</Button>
</Tooltip>

Progress

Horizontal progress bar.

| Prop | Type | Default | Description | |------|------|---------|-------------| | value | number | — | Current value (0–100) | | max | number | 100 | Maximum value | | size | 'sm' \| 'md' \| 'lg' | 'md' | Bar height | | tone | 'primary' \| 'success' \| 'warning' \| 'danger' | 'primary' | Bar colour |

<Progress value={75} tone="success" />

Skeleton / SkeletonText / SkeletonCircle / SkeletonRectangle

Placeholder loading shapes.

| Prop | Type | Default | Description | |------|------|---------|-------------| | width | string \| number | '100%' | Width | | height | string \| number | '1em' | Height | | variant | 'text' \| 'circular' \| 'rectangular' | 'text' | Shape | | animation | 'pulse' \| 'wave' \| 'none' | 'pulse' | Animation type | | lines | number | 3 | Number of lines (text variant) |

<SkeletonText lines={3} />
<SkeletonCircle width={40} height={40} />

Hooks

useTheme()

Returns { setMode, getMode } for reading and setting the current theme mode ('light' | 'dark').

import { useTheme } from '@alga-psa/ui-kit';

const { setMode, getMode } = useTheme();
setMode('dark');

applyThemeVars(vars)

Applies a Record<string, string> of CSS custom property values to the document root.

import { applyThemeVars } from '@alga-psa/ui-kit';

applyThemeVars({ 'alga-primary': '#ff6600' });

Extension Theme Bridge

For extensions running in iframes, use the built-in theme bridge to receive host theme variables:

import { applyThemeVars } from '@alga-psa/ui-kit';

window.addEventListener('message', (ev) => {
  const data = ev.data;
  if (data?.alga === true && data?.version === '1' && data?.type === 'theme') {
    applyThemeVars(data.payload || {});
  }
});

// Signal readiness to the host
window.parent.postMessage({ alga: true, version: '1', type: 'ready' }, '*');