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

kanto-ds

v1.9.0

Published

Eighty accessible web components for dashboards, admin tools and forms — themeable in one line, in React, Vue, Angular, Svelte or plain HTML.

Readme

Kanto

Eighty web components for dashboards, admin tools and forms — in your colours, in any framework.

npm CI Accessibility: axe, both themes License: MIT

Open the live docs and playground. Pick a colour, a font and a density, watch a real screen change, and copy the one line that does it in your project — or open it on StackBlitz.

Or build a page from whole sections: the site's blocks — a hero, pricing, a blog post, a sign-in form, a settings section and more — are live, each with its own documentation.

They are standard custom elements, so the same <kt-button> runs in React, Vue, Angular, Svelte and plain HTML. Two complete themes, every element audited by axe in both and driven by the keyboard in its tests, and no framework to adopt.

Start

npm install kanto-ds
import 'kanto-ds';
import 'kanto-ds/styles.css';
<kt-card>
  <h6 slot="header">Recent transactions</h6>
  <kt-table></kt-table>
  <kt-button slot="footer" variant="text">View full history</kt-button>
</kt-card>

No bundler? One stylesheet and one script tag:

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/kanto-ds@1/dist/styles.css" />
<script type="module" src="https://cdn.jsdelivr.net/npm/kanto-ds@1"></script>

Per-framework setup — React 18 and 19, Vue, Angular, Svelte — is in docs/frameworks.md.

Make it yours

Colour, font, corners, density and text size are each one attribute, on the page or on any container:

<html data-accent="teal" data-radius="round" data-density="compact"></html>

Or any colour at all — Kanto solves the shades for it and keeps every text at 4.5:1 in both themes:

import { setAppearance } from 'kanto-ds';

setAppearance({ accent: '#e11d48', radius: 'round', textSize: 'large' });

Fonts other than Kanto's own (data-font="inter", "plex", "geist") need the face loaded by your page, from Google Fonts or your own files.


Showcase

dashboard.png

| guide.png | component.png | | ----------------------------------- | ------------------------------------------- |

Why custom elements

One implementation, every framework. The alternative — a React port, a Vue port, an Angular library — is three codebases drifting apart.

The trade is real and worth naming: custom elements need JavaScript to upgrade, so during SSR they hold their place, hidden, until the bundle lands. In return, a component is written once and a fix reaches every consumer at the same time.

See docs/frameworks.md for the per-framework setup. Two rules hold everywhere:

  • Data goes in as properties, not attributes. options, data, columns, items — anything that is not a string or a boolean. An attribute can only hold a string.
  • Events are kt--prefixed CustomEvents, payload in detail. They bubble and cross shadow boundaries, so you can listen on a container.

The system

Two complete themes. :root carries dark, which is what you get by default; data-theme="light" switches, and data-theme="auto" follows the OS. Both are built from the same token names, so no component has theme-specific CSS and neither theme is an afterthought.

Elevation is a lighter surface, not a shadow. Surfaces are an eleven-step ramp, --color-dark-8 through --color-dark-24. The only real shadow in the system is on toasts, which genuinely float.

Fields take an outline, never a border. A border would reflow the field on focus; the outline sits outside the box — grey on hover, primary on focus, danger on error.

Motion is fast and utilitarian. 100–200ms, no bounces, no springs. The one decorative animation is the striped progress bar, and it earns its place: it is how an operation says it is still working when the number is not moving.

Copy is English, and terse. "Search everything...", "No data to display", "2 min ago". Sentence case for labels and buttons; uppercase only for overline section titles. No emoji in product UI — the one exception is the flag in the phone-input country picker, which is functional.

Every string an element writes itself — empty states, button labels, validation messages, and the accessible names only a screen reader hears — comes from one registry. An application in another language replaces them once:

import { setStrings } from 'kanto-ds/strings';

setStrings({
  clear: 'Effacer',
  noData: 'Aucune donnée',
  pageOf: (page, total) => `Page ${page} sur ${total}`,
});

Anything left out stays in English, and switching at runtime re-renders every element. Strings that carry a value are functions, so a translation owns the word order. Where an element also takes a string as a property (emptyText, placeholder, confirmLabel…), setting it still wins for that one element. The full list is KtStrings in src/internal/strings.ts.

Full details in src/tokens/README.md.

Components

Eighty elements. Each has a page beside its source.

| Group | Elements | | -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Core | button · split-button · card · copy-button · code · icon · avatar · avatar-group · badge · kbd | | Forms | input · prompt-input · textarea · time-input · label-input · form · select · checkbox · color-picker · date-input · date-picker · calendar · otp-input · number-input · radio-group · input-menu · multi-select · slider · toggle · drag-drop | | Navigation | app-shell · user-menu · header · footer · breadcrumb · sub-menu-navigation · page-header · tabs · segmented-control · steps · toggle-button · toggle-button-group | | Feedback | progress-bar · skeleton · tooltip · toast · toast-container + toaster · alert · empty-state | | Overlays | dropdown · side-panel · confirm-dialog · modal · accordion · tour · collapsible · command-palette | | Data | table · pagination · stat · chat-message · description-list · chart · meter · tree · timeline | | Blocks | newsletter · post-grid · faq · logo-cloud · testimonials · pricing-table · cta · feature-grid · hero · blog-post · error-page · empty-page · settings-section · auth-form |

Import the whole system, or one element:

import 'kanto-ds'; // everything
import 'kanto-ds/components/core/kt-button'; // just this one

Icons

<kt-icon> resolves Lucide icons by name at render time, which means the set cannot be tree-shaken. So Kanto ships the 24 its own elements draw and nine a first screen reaches for (plus, pencil, settings, download, upload, external-link, filter, ellipsis, refresh-cw). Register what else your application uses:

import { Rocket, Wallet } from 'lucide';
import { registerIcons } from 'kanto-ds/icons';

registerIcons({ Rocket, Wallet }); // <kt-icon name="rocket">

Forms

Every field is a form-associated custom element. It serialises into FormData under its name, resets with the form, and reports validity like a native input — no hidden mirror inputs, no manual wiring.

<form>
  <kt-label-input label="Email address" required>
    <kt-input name="email" type="email" required></kt-input>
  </kt-label-input>
  <kt-button type="submit">Send</kt-button>
</form>

Editor support

The package ships a Custom Elements Manifest — every element's attributes, properties, events, slots and CSS parts — and editor data generated from it, so plain HTML gets completion and hover docs:

  • WebStorm / JetBrains read web-types.json from the package on their own.

  • VS Code needs pointing at it once, in .vscode/settings.json:

    {
      "html.customData": ["./node_modules/kanto-ds/dist/vscode.html-custom-data.json"],
      "css.customData": ["./node_modules/kanto-ds/dist/vscode.css-custom-data.json"]
    }
  • Storybook and API-docs generators take kanto-ds/custom-elements.json.

AI agents get instructions of their own: the package ships dist/AGENTS.md — the rules that make code right the first time, the mistakes agents make and the fix, every component in a line. One line points Claude Code, Cursor, Copilot or Codex at it; the AI agents guide has each. Each component's page ships at node_modules/kanto-ds/dist/docs/<tag>.md, and a chat that cannot read your project takes llms.txt or llms-full.txt, every page in one file.

Accessibility

Not a phase at the end; it is why several of these components exist in this shape. Some of what that meant:

  • The segmented control is a radio group: one tab stop, arrows to move.
  • Sortable table headers are buttons — a header you can only sort with a mouse is a table half the users cannot sort.
  • The side panel and confirm dialog are native <dialog>s: top layer, trapped focus, inert background, Escape handled by the platform.
  • The confirm dialog focuses Cancel, not Confirm.
  • Tooltips are dismissible with Escape, and are never a control's only name.
  • Every motion respects prefers-reduced-motion, through the token layer.

Development

npm install
npm run dev          # the documentation site, on src/
npm test             # 459 tests
npm run build        # JS, types, and the static CSS + fonts
npm run verify       # everything CI runs, and everything a release requires

The docs site is built from the design system itself, so a broken component breaks its own documentation.

Licence

MIT. The bundled webfonts — Manrope, Mulish and Source Code Pro — are under the SIL Open Font License 1.1; see src/assets/fonts/README.md.