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

@cossackframework/ui

v0.9.0

Published

UI component library for the Cossack Framework — token-driven, themeable components and Solar icons

Readme

@cossackframework/ui

UI component library for the Cossack Framework : token-driven, themeable components built on native HTML elements and the browser's top-layer API. Styles are written as Tailwind v4 utility classes that reference CSS-token-driven variables, so the whole library retints by overriding a handful of @theme values.

Install

In a Cossack project:

cossack add ui                 # wire the package + CSS imports + @source
cossack add ui --theme=blue    # same, but pre-tint to the blue palette
cossack add ui button          # (optional) eject a customizable copy of Button

The --theme flag selects a color palette (see Theming) and wires the matching themes/<name>.css import into src/style.css. Available themes: neutral (default), zinc, stone, gray, slate, blue, green, red.

Or install the package directly and wire the CSS by hand:

pnpm add @cossackframework/ui

Then add to src/style.css (after @import "tailwindcss";):

@import "@cossackframework/ui/theme/base.css";
@import "@cossackframework/ui/theme/theme.css";
/* Optional: import a named palette AFTER theme.css to retint. */
@import "@cossackframework/ui/theme/themes/blue.css";

/* Tailwind v4 excludes node_modules by default. This @source line tells it to
   scan the package's published bundle (dist), which contains every utility
   class referenced inside the components (bg-card, bg-popover, bg-accent, ...).
   Without it, only classes that also appear in your own source will be
   emitted and most variants will render unstyled. Note the `files` field ships
   only `dist` and `src/theme`, so scan `dist` (not src/components). */
@source "../node_modules/@cossackframework/ui/dist";

cossack add ui wires all of the above automatically.

Components

Buttons & Actions

| Component | Description | |---|---| | Button | Variant button (default, secondary, destructive, outline, ghost, link) with sizes (default, sm, lg, icon) | | Spinner | CSS-animated loading spinner using animate-spin |

Forms & Inputs

| Component | Native element | Description | |---|---|---| | Input | <input> | Token-driven text input with variant/size | | Textarea | <textarea> | Multiline text input | | Select | <select> | Native select with chevron overlay | | NativeSelect | <select> | Styled native select (no JS, no popover : mobile-friendly) | | InputGroup | <input> + addons | Input with prefix/suffix adornments (@, $, icons) | | Checkbox | <input type="checkbox"> | Checkbox with label wrapper | | Switch | <input type="checkbox"> | Toggle switch (role="switch") | | RadioGroup | <input type="radio"> | Radio button group | | Slider | <input type="range"> | Range slider with token accent-color | | InputOTP | <input> ×N | Segmented one-time-password input (paste, auto-advance, arrows) | | Calendar | <div> grid | Month-grid date picker with min/max bounds and ISO onChange | | DatePicker | popover + calendar | Calendar in a native popover; emits ISO date on pick | | Label | <label> | Accessible form label |

Layout & Display

| Component | Native element | Description | |---|---|---| | Card | <div> | Surface container with optional header/body/footer slots | | Separator | <hr> / <div> | Horizontal or vertical divider | | DataTable | <table> | Controlled server paging, filtering, sorting and stable row-ID selection | | Table | <table> | Token-styled table with sub-components (TableHeader, TableBody, TableRow, TableHead, TableCell) | | Avatar | <img> / <span> | Image with initials fallback, circle/square | | AvatarGroup | <div> | Stacked avatar set with "+N" overflow counter | | Badge | <span> | Status/label pill with semantic variants | | Skeleton | <div> | Loading placeholder with animate-pulse | | Progress | <div role="progressbar"> | Progress bar with aria values | | Tooltip | CSS :hover | Pure-CSS hover tooltip (no JS, no portal) | | Typography | semantic tags | Typographic primitives (h1–h4, p, lead, blockquote, code, ul, ol) | | ContextMenu | popover attribute | Right-click menu with separators and destructive items | | Sidebar | <aside> | Collapsible nav rail (icon / offcanvas) with nested groups + agnostic footer slot | | Drawer | <dialog> | Slide-in drawer panel with drag handle grip and backdrop | | Item | <div> | Generic list-row primitive with media/content/trailing slots |

Chat

| Component | Native element | Description | |---|---|---| | Bubble | <div> | Chat message bubble (sent/received variants, tail, timestamp) | | Message | <div> | Chat message wrapper (avatar + name + bubble grouping) | | MessageScroller | <div> | Named keyboard-scrollable viewport with history anchoring and a latest-message action | | Marker | <div> | Date/section divider inside a message list ("Today", "New messages") | | Attachment | <div> | Chat file-attachment card with auto-detected type icon + status |

Overlay & Interactive

| Component | Native element | Description | |---|---|---| | Modal | <dialog> | Controlled modal via dialog.showModal() + @Task | | Popover | popover attribute | Top-layer popover with JS positioning + light dismiss | | DropdownMenu | popover attribute | Menu with keyboard navigation (Arrow/Escape), collision-aware side/align flip, scroll/resize repositioning | | Sheet | <dialog> | Slide-in panel (drawer) from any edge | | Accordion | <button> + <div> | Animated collapsible sections | | Tabs | conditional render | Accessible tabbed interface with ARIA tablist | | Toaster + toast | reactive store | Global toast notification system |

Icons

| Component | Description | |---|---| | Icon | Renders a Solar icon from a direct entry (tree-shakeable). Pass an icon entry imported from @cossackframework/solar-icons/<name>. 6 styles: line, bold, duotone, broken, outline, line-duotone. |

Only Icon is provided. There is no name-based lookup component : importing icons by name pulls the full ~1,200-icon registry (~9 MB of SVG paths) into the bundle. Always import the specific icon you need and pass its entry to <Icon>.

Usage

Components are Cossack components : consume them with the component() helper, not JSX:

import { html, component } from "@cossackframework/renderer";
import { Button, Input, Icon, toast } from "@cossackframework/ui";
import { ArrowRightIcon } from "@cossackframework/solar-icons/arrow-right";

html`
  ${component(Button, { variant: "default", "@click": this.save }, "Save")}
  ${component(Input, { type: "email", placeholder: "[email protected]" })}
  ${component(Icon, { entry: ArrowRightIcon, variant: "duotone", size: 20 })}
`;

Theming

The theme is a two-layer shadcn-style system in src/theme/theme.css:

  1. Raw values : :root { … } (light) and .dark { … } (dark) define OKLCH color values for every semantic token (--primary, --background, --card, --popover, --accent, --border, --ring, --chart-*, --sidebar-*, …). The default palette is shadcn's neutral (black primary on light, near-white on dark).
  2. Tailwind mapping : @theme inline { … } maps each raw variable into a Tailwind utility (--color-primary: var(--primary) → bg-primary, text-primary, border-primary, ring-primary, …) plus a radius scale derived from a single --radius knob.

Dark mode

Dark mode is opt-in: add class="dark" to <html> (or any ancestor of the UI). The .dark token overrides live in theme.css, so no extra import is needed.

Retinting

Override any single token in your own :root / .dark blocks:

@import "@cossackframework/ui/theme/theme.css";

:root {
  --primary: oklch(0.488 0.243 264.376);   /* retint to blue */
  --ring: oklch(0.488 0.243 264.376);
}

Or import a named palette AFTER theme.css. Neutral families retint the whole surface scale; accent palettes retint only --primary / --ring / --chart-* / --sidebar-primary-*, keeping neutral surfaces:

@import "@cossackframework/ui/theme/theme.css";
@import "@cossackframework/ui/theme/themes/zinc.css";   /* neutral family */
/* or: stone, gray, slate, neutral */
/* accent palettes: blue, green, red */

When scaffolding with the CLI, pass --theme to do this automatically:

cossack add ui --theme=zinc     # wires themes/zinc.css into src/style.css

| Palette | Type | Description | |---|---|---| | neutral | neutral | Pure achromatic (the default) | | zinc | neutral | Cool gray with a subtle blue tint | | stone | neutral | Warm gray with a subtle brown/amber tint | | gray | neutral | Balanced, barely-tinted gray | | slate | neutral | Cool blue-gray | | blue | accent | Blue primary (the pre-shadcn default) | | green | accent | Green primary | | red | accent | Red primary |

Radius

All corner radii derive from a single --radius token (default 0.625rem): --radius-sm, --radius-md, --radius-lg, --radius-xl. Override --radius to rescale every rounded-* utility at once.

Cossack extensions

The token set extends shadcn's with success / success-foreground and warning / warning-foreground semantic colors, used by Badge, Alert, Toast, etc.

Icons

Icons are split across two packages:

  • @cossackframework/solar-icons : the icon dataset. A zero-dependency, framework-agnostic package shipping 1,246 Solar icons across six styles as tree-shakeable data entries. Install it to get the icon data:

    pnpm add @cossackframework/solar-icons
  • @cossackframework/ui : provides the Icon component that renders the data. It lives in ui (not in the data package) so it shares your app's single renderer/core module instance.

Usage : fixed icon (tree-shakeable)

Import the icon entry directly from the data package and pass it to Icon:

import { Icon } from "@cossackframework/ui";
import { component } from "@cossackframework/renderer";
import { ArrowRightIcon } from "@cossackframework/solar-icons/arrow-right";

html`${component(Icon, { entry: ArrowRightIcon, variant: "duotone", size: 20 })}`;

For maximum tree-shaking, import a single style instead of the full entry:

import { ArrowRightIcon } from "@cossackframework/solar-icons/arrow-right/bold";
// ArrowRightIcon is now a string (the bold SVG markup only), wrapped by Icon.

Each icon export is suffixed with Icon (e.g. ArrowRightIcon, SettingsIcon) to avoid collisions with common identifiers. Icon names are kebab-case in the import path (arrow-right, eye-closed, alt-arrow-down).

No name-based lookup. If the icon is determined by runtime data, map it to a direct entry yourself (a small Record<string, IconEntry> of the icons you ship). Importing the full registry to resolve names by string pulls all 1,246 icons (~9 MB) into the bundle.

Styles

Solar ships six styles. The style prop selects one; missing styles fall back to line:

| key | Solar source folder | |---------------|---------------------| | line | Linear | | bold | Bold | | duotone | BoldDuotone | | broken | Broken | | outline | Outline | | line-duotone| LineDuotone |

Accordion and Collapsible

Both components accept open for parent-controlled state, or defaultOpen for local state, and call onToggle(open) when the trigger is used. Accordion uses a button summary; Collapsible accepts custom trigger content.

Open content has automatic height and visible overflow, including during SSR. Async rows, validation messages, and absolutely positioned dropdowns can grow or extend beyond the panel. Height is measured on each toggle and clipped only while animating or closed. Closed panels are inert and hidden from assistive technology. Reduced-motion preferences disable the height animation.

Progress values

Progress clamps its value to 0..max for both the visible bar and its ARIA attributes. A non-finite value becomes 0. A zero, negative, or non-finite maximum falls back to 100.

Ejecting components

cossack add ui <component> copies a single component into your project at src/components/ui/<Component>.ts so you can customize it. The ejected copy is yours : re-run with --force to overwrite. Shared private animation helpers are included in ejected Accordion and Collapsible source; Toaster is ejected from the package's Toast module.

Available component names (kebab-case, passed to cossack add ui <name>):

button, input, textarea, select, native-select, input-group, label, checkbox, switch, radio-group, slider, input-otp, password-input, field, toggle, toggle-group, badge, kbd, card, separator, table, avatar, avatar-group, skeleton, progress, spinner, aspect-ratio, typography, empty, item, marker, modal, alert-dialog, popover, dropdown-menu, context-menu, sheet, drawer, tooltip, hover-card, accordion, collapsible, tabs, navigation-menu, menubar, command, combobox, multi-select, calendar, date-picker, carousel, resizable, scroll-area, sidebar, breadcrumb, pagination, button-group, toaster, bubble, message, message-scroller, attachment.

Controlled DataTable

Import DataTable, DataTableProps<Row> and DataTableColumn<Row> from @cossackframework/ui. Compose with component(DataTable<Row>, props). Provide a unique id to preserve query-control focus across same-page URL navigation. Provide a caption, typed columns, a stable getRowId, current-page rows, rowCount, query state and onStateChange. Columns use either a row key or an accessor function. A cell callback can render custom content from the row.

The parent fetches each requested state and rejects stale responses. The table does not fetch or transform rows. Sort, filter and page-size changes reset page to 1. Selection is controlled through selection and onSelectionChange and preserves off-page IDs. Reconcile selection and out-of-range pages in the parent when filters, permissions or the underlying records change.

Use loading, error and onRetry for request states. Native controls provide keyboard behavior, headers expose sort state and row labels name checkboxes. Paging uses focusable aria-disabled buttons that ignore inactive actions. Retry returns focus to the filter before its callback. The table accepts only its declared props, with no general HTML-attribute forwarding.

Run pnpm exec cossack docs --package @cossackframework/ui --symbols --json for the installed prop contracts and package-owned DataTable guide. The guide also explains selection policy, labels and status behavior.

Typed native controls

Button, Input, Label, Checkbox, Switch and Form use typed native attributes and @event listeners. Unknown props, string boolean values and React-style onClick are rejected. Pass children as the third argument to component(). Listener currentTarget is typed as the native control; target can be a descendant. Capture the control before awaiting, because native events clear currentTarget after dispatch. Use disabled: !!loading for loading counts and an explicit Button type in forms. Input's visual size is default, sm or lg, not a numeric HTML size.

Use Label with matching for/id, or provide aria-label. Checkbox's label prop takes precedence over children. Switch renders children inside its label. Both accept controlled checked and emit native @change events. Update the parent from event.currentTarget.checked. Their input type, role and checked property are component-owned. Input supports .value, .checked, bind() and live() for direct property binding. Choose names and values for native FormData. Form's submit callback prevents navigation and defaults to novalidate. Set novalidate: false to keep browser validation, or omit submit for native POST.

The installed native-controls guide documents attributes, event types and labeling requirements. The rest of the catalog remains under contract audit.

Radio groups and sliders

RadioGroup uses a distinct nonempty name per logical group and unique string item values. Provide aria-label or aria-labelledby for the group. onValueChange(value, event) receives an input event with HTMLInputElement currentTarget. A native @change listener receives the bubbled event on the root div. Update parent value from the callback. Items retain DOM identity by value when reordered. disabled, required and form apply to the inputs; other global attributes apply to the div.

Slider owns a finite numeric value, min, max and positive step. The browser clamps and rounds values. Constraints apply before value, and widened bounds restore a previously clamped requested value. Read event.currentTarget.valueAsNumber in @input to update parent state. Supply label, an external Label, or ARIA naming. Use Input with type: 'range' for direct native property-binding behavior.

Checkbox, Switch and RadioGroup wrappers follow the child's native disabled state. Slider styles its own native disabled state. This includes disabled fieldsets. The installed radio-and-range guide describes attributes, events and accessibility.

AuthForm block

import { component } from '@cossackframework/renderer';
import { AuthForm } from '@cossackframework/ui/blocks';

component(AuthForm, {
  id: 'account-login',
  defaultMode: 'login',
  onSubmit: async (data, { signal }) => {
    // Call your authentication service. Validate credentials on the server.
    // Return safe { error, fieldErrors } text when needed.
  },
});

Use mode with onModeChange for controlled state. Native credentials remain in fields; submission reads current FormData, including autofill. Provider actions require explicit providers and onSocial. Localize text with labels. The block owns UI behavior, not authentication or session creation. Actions need JavaScript; fields remain editable before hydration. cossack add ui auth-form ejects the block using public UI imports.

CommandPalette block

import { component } from '@cossackframework/renderer';
import { CommandPalette } from '@cossackframework/ui/blocks';

component(CommandPalette, {
  id: 'app-commands',
  label: 'Application commands',
  shortcut: true,
  commands: [{ id: 'home', label: 'Go home', group: 'Navigation' }],
  onSelect: id => { /* Run the application action for this ID. */ },
});

Use open with onOpenChange for parent ownership, or defaultOpen for local state. setOpen() and toggle() request changes. Native props target the dialog. Command owns search, grouping, active options, focus and shortcut arbitration. Global shortcuts are opt-in and ignore unrelated editable controls by default. Item shortcut strings are hints, not registered bindings. icon accepts trusted Solar artwork. cossack add ui command-palette ejects the composed block.