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

@doriansmith/kiln

v0.4.1

Published

Accessible React component library for indie developers. WCAG AA, < 25 KB gzipped, zero dependencies, zero config. Install and ship in under 2 minutes.

Readme


Why Kiln

1. Accessible by default

Every component meets WCAG AA out of the box. Keyboard navigation, focus management, focus rings, and correct ARIA are built in — not bolted on. Accessibility debt costs more to fix later than to build correctly now.

2. Performance-first

Kiln components don't tank your Lighthouse score. The production site scores 99 Performance / 100 Accessibility / 100 SEO on Lighthouse (FCP 0.6s, LCP 0.6s, TBT 0ms, CLS 0.005). Zero layout shift on every interaction. All animations use transform and opacity — GPU-accelerated, no layout thrashing. Bundle size is measured and budgeted. These scores are a floor, not a one-time achievement — every new component must maintain them.

3. Genuinely mobile-ready

Every component works on real devices at 375px. All interactive elements meet the 44×44px WCAG touch target requirement. No text below 14px on small screens. Positioned overlays are viewport-constrained. Mobile is not optional.

4. Built for solo devs

From npm install to rendering a Kiln page in under 2 minutes. No config files, no setup wizards, no theme provider components, no required context wrappers. Every code example is copy-paste ready. TypeScript is fully inferred — no required generic annotations.


Install

npm install @doriansmith/kiln

Peer deps (already in your project):

npm install react react-dom

Usage

Import the CSS once at your app root:

import '@doriansmith/kiln/kiln.css';

Use components:

import { Button, Input, Card } from '@doriansmith/kiln';

export default function App() {
  return (
    <Card variant="raised">
      <Input label="Email" placeholder="[email protected]" />
      <Button variant="primary">Ship it</Button>
    </Card>
  );
}

Dark mode: set data-theme="dark" on <html>. Use the built-in ThemeToggle component for automatic localStorage persistence.

document.documentElement.setAttribute('data-theme', 'dark');

What's included

| Component | Description | |---|---| | AppLayout | Full-page app shell. Composes sidebar, tools panel, breadcrumbs, notifications, page header, and split panel. | | Breadcrumbs | Hierarchical nav trail with chevron separators and mobile truncation. | | Button | Primary / secondary / ghost / danger. Loading state, icons, link mode. | | Input | Label, helper text, error state, left/right icons. ARIA-linked. | | Textarea | Like Input, plus character counter. | | Card | Default / raised / glass / gradient-border / coming-soon. Hover lift. | | Header | Page and section heading block with optional tagline, description, and actions slot. Provides consistent spacing after the Nav. | | Hero | Full-width page hero section with eyebrow, title, description, actions, and optional media slots. Semantic <section> landmark. Three variants (default / gradient / glass), three sizes, left or centre alignment. | | Badge | Severity (critical → low) and status (success / warning / error / info / pending / running). | | Chip | Selectable filter chip. Controlled and uncontrolled. | | Toggle | Binary switch for boolean settings. Three sizes, controlled and uncontrolled. | | Tabs | Arrow-key navigation, ARIA tablist / tab / tabpanel. | | Modal | Portal, focus trap, Escape to close, returns focus on dismiss. | | Nav | Complete drop-in nav bar — logo slot, desktop links, actions slot, and built-in mobile hamburger + slide-out drawer. | | NavMenu | Desktop-only link strip. Used inside Nav, also available as a primitive for custom nav bars. | | MobileNav | Standalone mobile hamburger + slide-out drawer. Use with NavMenu when building a custom nav bar without Nav. | | NotificationBar | Stacked dismissible banners with info / success / warning / error variants and aria-live. | | SideNav | Grouped vertical nav with active state and keyboard navigation. | | SplitPanel | Expandable bottom panel with drag-to-resize handle and keyboard resize (arrow keys). | | TableOfContents | Sticky ToC with IntersectionObserver active-section tracking. | | ThemeToggle | Light/dark toggle, persists to localStorage. | | Toast / ToastContainer | Non-blocking toasts with four severity variants and configurable position. | | Tooltip | Hover/focus popup label attached to any element. | | ToolsPanel | Collapsible right panel for help or tools. Desktop slide animation, mobile overlay drawer. | | Footer | Logo, link list, copyright. | | Grid + GridItem | Responsive CSS grid. Fixed-column mode (4→2→1) or container-aware auto-fit. Span cells with GridItem. | | LoadingIndicator | Spinner, inline or block. aria-live announcement. | | ErrorMessage | Error display with optional retry button. | | ScrollToTop | Scrolls to top on route change. Renders nothing. | | CodeBlock | Styled <pre><code> with copy button and language label. |


Nav

A complete, drop-in navigation bar. Renders a sticky <header> with a logo slot on the left, a desktop link strip in the centre, a right-side actions slot, and a fully accessible mobile hamburger with a focus-trapped slide-out drawer — all from a single component.

import { Nav, ThemeToggle } from '@doriansmith/kiln';

const NAV_ITEMS = [
  { href: '/', label: 'Home' },
  { href: '/docs', label: 'Docs' },
  { href: '/about', label: 'About' },
];

<Nav
  logo={<img src="/logo.png" alt="MyApp" style={{ height: 32 }} />}
  items={NAV_ITEMS}
  isActive={(href) => window.location.pathname === href}
  onNavigate={(href) => navigate(href)}
  actions={<ThemeToggle />}
/>

Props

| Prop | Type | Default | Description | |---|---|---|---| | logo | React.ReactNode | — | Left-side brand slot | | items | NavItem[] | [] | { href, label, icon? } links — rendered on desktop and in the mobile drawer | | actions | React.ReactNode | — | Right-side slot (ThemeToggle, avatar, CTAs, etc.) | | sticky | boolean | true | Sticks the bar to the top of the viewport | | isActive | (href) => boolean | pathname match | Returns true to mark a link active (aria-current="page") | | onNavigate | (href, e) => void | — | Called on any link click — call e.preventDefault() for client-side routing | | ariaLabel | string | 'Main navigation' | Accessible label for the nav landmark and mobile dialog |

For full control over the nav bar layout, use the lower-level NavMenu and MobileNav primitives instead.


AppLayout

A full-page application shell — drop it in once and get sidebar, tools panel, breadcrumbs, notifications, and a sticky top bar wired up and accessible.

import { AppLayout, Nav, SideNav, ThemeToggle } from '@doriansmith/kiln';

// SideNav manages its own open/collapse state internally
<AppLayout
  topBar={<Nav logo={logo} items={navItems} actions={<ThemeToggle />} />}
  sideBar={<SideNav groups={navGroups} activeId={activeId} onSelect={setActiveId} />}
  breadcrumbs={[{ label: 'Home', href: '/' }, { label: 'Dashboard' }]}
  header={<h1>Dashboard</h1>}
>
  <YourPageContent />
</AppLayout>

Slots

| Prop | Description | |---|---| | topBar | Sticky header (renders inside <header role="banner">) | | sideBar | Collapsible left panel — pass a <SideNav> that manages its own open/collapse state | | toolsPanel | Collapsible right panel — toggle via toolsOpen / onToolsChange | | header | Page header rendered above children inside the content column | | children | Main page content (rendered inside <main>) | | splitPanel | Expandable bottom panel with its own toggle button |

Notifications

Pass an array of AppLayoutNotification objects for dismissible banners above the content area:

const [notes, setNotes] = useState([
  { id: '1', type: 'success', message: 'Deployed.', dismissible: true,
    onDismiss: (id) => setNotes((n) => n.filter((x) => x.id !== id)) },
]);

<AppLayout notifications={notes}>...</AppLayout>

type is 'info' | 'success' | 'warning' | 'error'.

CSS token overrides

Override panel widths via inline style:

<AppLayout
  style={{
    '--kiln-app-layout-sidebar-width': '200px',
    '--kiln-tools-panel-width': '320px',
  } as React.CSSProperties}
>
  ...
</AppLayout>

Responsive behaviour

  • Desktop (≥ 768px): sideBar and toolsPanel slide in/out without overlapping content.
  • Mobile (< 768px): both panels become fixed overlay drawers with a backdrop. SideBar closes on Escape or backdrop click.

Header

A consistent page and section heading block. Eliminates custom per-page hero layouts — use Header everywhere a heading is needed and spacing is automatically correct.

import { Header, Button } from '@doriansmith/kiln';

// Page-level header with decorative brand tagline
<section aria-labelledby="page-heading">
  <Header
    id="page-heading"
    variant="h1"
    tagline="Analytics"
    description="Monitor usage, performance, and billing across all workspaces."
  >
    Dashboard
  </Header>
</section>

// Section header with inline actions
<Header
  variant="h2"
  description="Manage access for your organisation."
  actions={
    <>
      <Button variant="secondary">Export CSV</Button>
      <Button variant="primary">Add user</Button>
    </>
  }
  divider
>
  Users
</Header>

Props

| Prop | Type | Default | Description | |---|---|---|---| | variant | 'h1' \| 'h2' \| 'h3' | 'h2' | Heading level and visual size. h1 centres content for page heroes; h2/h3 are left-aligned for sections. | | tagline | string | — | Large decorative display text above the heading, rendered with the Kiln brand gradient. Hidden from assistive technology. | | description | React.ReactNode | — | Subtitle rendered below the heading in muted text. | | actions | React.ReactNode | — | Right-aligned slot for buttons or badges. Centres below the description for h1. | | divider | boolean | false | Renders a hairline separator below the header block. | | id | string | — | Forwards to the heading element — pair with aria-labelledby on the parent <section>. | | style | React.CSSProperties | — | Use --kiln-header-max-width (default 1100px) and --kiln-header-padding-x to customise the container. |


Card — coming-soon variant

Pass variant="coming-soon" to render a self-contained "work in progress" placeholder. No children required.

import { Card } from '@doriansmith/kiln';

// Default text
<Card variant="coming-soon" />

// Custom text
<Card
  variant="coming-soon"
  title="Under construction"
  description="This section will be ready soon."
/>

Use --kiln-card-coming-soon-max-width to override the default 440 px maximum width.


NavMenu + MobileNav

Use these when Nav's layout doesn't fit your design and you need to assemble your own nav bar.

NavMenu — the desktop-only link strip (a <nav> with styled links and active-state handling).
MobileNav — a self-contained hamburger button + focus-trapped slide-out drawer.

import { NavMenu, MobileNav } from '@doriansmith/kiln';

// Desktop link strip
<NavMenu
  items={NAV_ITEMS}
  isActive={(href) => location.pathname === href}
  onNavigate={(href) => navigate(href)}
/>

// Mobile hamburger + drawer
<MobileNav
  items={NAV_ITEMS}
  logo={<img src="/logo.png" alt="MyApp" style={{ height: 32 }} />}
  isActive={(href) => location.pathname === href}
  onNavigate={(href) => navigate(href)}
/>

For most cases, use Nav — it handles all of the above automatically.


Breadcrumbs

Hierarchical navigation trail. Last item is the current page (no link, aria-current="page").

import { Breadcrumbs } from '@doriansmith/kiln';

<Breadcrumbs
  items={[
    { label: 'Home', href: '/' },
    { label: 'Projects', href: '/projects' },
    { label: 'Kiln' },
  ]}
/>

NotificationBar

Stacked dismissible banners. Pass dismissible: true and onDismiss to enable per-item removal. Announces changes via aria-live.

import { NotificationBar } from '@doriansmith/kiln';

const [items, setItems] = useState([
  { id: '1', type: 'success', message: 'Deployed successfully.', dismissible: true },
]);
const dismiss = (id) => setItems((n) => n.filter((x) => x.id !== id));

<NotificationBar items={items.map((n) => ({ ...n, onDismiss: dismiss }))} />

type is 'info' | 'success' | 'warning' | 'error'.


SplitPanel

Expandable bottom panel with drag-to-resize handle and keyboard resize (↑/↓ arrow keys in 20px steps).

import { SplitPanel } from '@doriansmith/kiln';

<SplitPanel header="Logs" defaultOpen defaultHeight={240} resizable>
  <LogViewer />
</SplitPanel>

ToolsPanel

Collapsible right-side panel anchored to the right edge.

import { ToolsPanel } from '@doriansmith/kiln';

<ToolsPanel header="Help" defaultOpen>
  <HelpArticle />
</ToolsPanel>

Token override: --kiln-tools-panel-width (default 280px).


Grid

Two modes, zero breakpoint config.

Fixed columns — cols

Declare the max column count. Kiln collapses it automatically: 4 cols at desktop, 2 at tablet, 1 on mobile.

import { Grid, GridItem } from '@doriansmith/kiln';

<Grid cols={4} gap="md">
  {cards}
</Grid>

Auto-fit — minColWidth

Tell Kiln how wide each item should be. The browser calculates the column count from the container width — no breakpoints, no config. Works inside sidebars, modals, and any nested layout.

// 1100px container → ~4 cols. 600px → 2 cols. 300px → 1 col. No code change.
<Grid minColWidth={260} gap="md">
  {cards}
</Grid>

GridItem — spanning cells

<Grid cols={3} gap="md">
  <GridItem colSpan={2}><Card>Wide</Card></GridItem>
  <Card>Narrow</Card>
</Grid>

colSpan is responsive-safe: it caps to 2 at tablet and resets to 1 on mobile so items never overflow implicit columns.

Dense packing

<Grid cols={4} gap="sm" dense>
  {photos}
</Grid>

dense enables grid-auto-flow: dense — fills gaps when items vary in height, useful for image galleries.

Gap tokens

| gap prop | Value | |---|---| | none | 0 | | xs | 0.5rem | | sm | 1rem | | md | 1.5rem (default) | | lg | 2rem | | xl | 3rem |

Override per-instance with the --kiln-grid-gap CSS token:

<Grid cols={3} style={{ '--kiln-grid-gap': '2rem' } as React.CSSProperties}>
  {cards}
</Grid>

TypeScript

All props are fully typed. Named type exports:

import type {
  ButtonVariant, ButtonSize,
  CardVariant,
  BadgeVariant, BadgeSeverity, BadgeStatus, BadgeSize,
  TabItem, NavItem, CodeBlockProps,
  BreadcrumbItem,
  NotificationBarItem, NotificationBarType,
  ToolsPanelProps, SplitPanelProps,
  AppLayoutBreadcrumb, AppLayoutNotification,
} from '@doriansmith/kiln';

No generic annotations required anywhere.


Bundle size

| Artifact | Minified | Gzipped | |---|---|---| | kiln.css | ~74 KB | ~10 KB | | index.js (ESM) | ~36 KB | ~13 KB |

Total gzipped: < 26 KB. Budget: 50 KB gzipped.


Status

v0.3.0 — Stable. APIs are stable. The visual style is opinionated and will not change without a major version bump.


Roadmap

Future releases

  • Storybook component explorer
  • GSAP-enhanced animation variants (optional peer dependency — base components remain CSS-only)
  • Additional primitives: Select, Combobox, DatePicker
  • Component-level token documentation

License

MIT