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

@svmukhin/edible-web-components

v1.1.1

Published

Interactive web components for EdibleCSS — just add HTML, get styled components.

Readme

edible-web-components

Interactive web components for EdibleCSS. Same philosophy — just add HTML, get styled components. No classes. No configuration.

Features

  • Zero Classes — Attribute-based API only, no CSS classes required
  • Single Script Tag — Add <script src="edible-wc.js" defer></script> and all components activate
  • EdibleCSS Tokens — Colors, spacing, and dark mode work automatically via inherited custom properties
  • Accessible — Full ARIA wiring, keyboard navigation, and focus management built in
  • Form-ready — Form components participate in FormData natively
  • Tiny — Under 6 KB gzipped for the full component set
  • No Shadow DOM — Light DOM first; EdibleCSS styles cascade in naturally
  • Modern Browsers — Chrome, Firefox, Safari, Edge (last 2 versions)

What Is This?

EdibleCSS styles native HTML5 elements automatically. But some UI patterns genuinely require JavaScript — a searchable dropdown, a multi-tag input, a toast notification. Native HTML has no equivalent elements for these.

edible-web-components fills that gap with custom elements that:

  • look and feel like native HTML5 tags
  • inherit EdibleCSS design tokens automatically (colors, spacing, dark mode)
  • require zero CSS classes
  • activate with a single <script> tag

Installation

CDN

<!-- 1. EdibleCSS (required peer) -->
<link
  rel="stylesheet"
  href="https://unpkg.com/@svmukhin/edible-css@latest/dist/edible.css"
/>

<!-- 2. Web components -->
<script
  src="https://unpkg.com/@svmukhin/edible-web-components@latest/dist/edible-wc.js"
  defer
></script>

That's it. All components are now available as HTML tags.

Always use defer when loading the script in <head>. Without it the custom element is defined before the browser has parsed the component's child elements (e.g. <option> inside <edible-combobox>), so connectedCallback fires on an empty element. defer makes the script run after the full HTML document is parsed, ensuring children are available on first connect. If you load the script at the end of <body> instead, defer is not needed.

npm

npm install @svmukhin/edible-web-components
import "@svmukhin/edible-web-components";

Components

<edible-combobox> — Searchable dropdown

Replaces <select> when the list is long enough to need filtering.

<label for="city">City</label>
<edible-combobox id="city" name="city" placeholder="Select a city…">
  <option value="ams">Amsterdam</option>
  <option value="ber">Berlin</option>
  <option value="par">Paris</option>
  <option value="rom">Rome</option>
  <option value="vie">Vienna</option>
</edible-combobox>

| Attribute | Type | Description | | ------------- | ------- | ------------------------------------------ | | name | string | Field name submitted with the form | | placeholder | string | Placeholder text shown in the search input | | disabled | boolean | Disables the component | | required | boolean | Marks the field as required |

Keyboard: Arrow Down/Up navigate options · Enter selects · Escape/Tab closes.

JavaScript: .value getter/setter to read or set the selected value.

<edible-tags-input> — Multi-value tag input

For entering multiple values as removable pill tags (skills, recipients, labels).

<label for="skills">Skills</label>
<edible-tags-input id="skills" name="skills" placeholder="Add a skill…">
</edible-tags-input>

| Attribute | Type | Description | | ------------- | ------- | ------------------------------------- | | name | string | Field name submitted with the form | | placeholder | string | Placeholder shown while no tags exist | | disabled | boolean | Disables all interaction |

Keyboard: Enter or , commits a tag · Backspace on empty input removes the last tag.

JavaScript: .value (array getter) · .add(tag) · .remove(tag).

Submits as a comma-separated hidden field under name.

<edible-file-drop> — Drag-and-drop file upload

A styled drop target that wraps <input type="file">.

<edible-file-drop name="attachment" accept=".pdf,.docx" multiple>
</edible-file-drop>

| Attribute | Type | Description | | ---------- | ------- | ---------------------------------- | | name | string | Field name submitted with the form | | accept | string | MIME types or extensions to accept | | multiple | boolean | Allow selecting multiple files | | disabled | boolean | Disables interaction |

Keyboard: Enter or Space opens the file picker.

JavaScript: .files getter returns the current FileList.

<edible-toast> — Notification message

Programmatically triggered notification that auto-dismisses.

<!-- Declare the outlet once, anywhere on the page -->
<edible-toast id="notifications"></edible-toast>
const t = document.getElementById("notifications");
t.show("File saved successfully.");
t.show("Something went wrong.", "error");
t.show("Session expiring soon.", "warning", 8000);

Method: .show(message, type = 'info', duration = 4000)

| type | Description | | --------- | ---------------- | | info | Default (accent) | | success | Green | | warning | Yellow | | error | Red |

Toasts stack in the bottom-right corner and auto-dismiss after duration ms. A shared region element is reused across all <edible-toast> instances.

<edible-tooltip> — Styled tooltip

Replaces the browser's unstyled title attribute with a consistent popover. Place it as the last child of any interactive element.

<button>
  Delete
  <edible-tooltip>This action cannot be undone.</edible-tooltip>
</button>

<a href="/docs">
  Learn more
  <edible-tooltip>Opens the documentation page.</edible-tooltip>
</a>

Tooltip text comes from the element's textContent. Shows on hover and keyboard focus; aria-describedby is wired automatically.

<edible-badge> — Status badge

A pill-style inline badge. The element itself is the badge surface.

<edible-badge>New</edible-badge>
<edible-badge type="success">Active</edible-badge>
<edible-badge type="warning">Pending</edible-badge>
<edible-badge type="error">Failed</edible-badge>

| type | Color | | --------- | ------ | | (none) | Accent | | success | Green | | warning | Yellow | | error | Red |

<edible-tabs> — Tab panel

Accessible tab interface. Declare <edible-tab> children with a label attribute — the tablist is built automatically.

<edible-tabs>
  <edible-tab label="Overview">
    <p>Content for the Overview tab.</p>
  </edible-tab>
  <edible-tab label="Details">
    <p>Content for the Details tab.</p>
  </edible-tab>
  <edible-tab label="History">
    <p>Content for the History tab.</p>
  </edible-tab>
</edible-tabs>

Keyboard: Arrow Left/Right moves between tabs · Home/End jumps to first/last.

Full ARIA wiring: role="tablist", role="tab", role="tabpanel", aria-selected, aria-controls, aria-labelledby, roving tabindex.

<edible-nav-dropdown> — Navigation dropdown

Groups navigation links under a labelled trigger. Place it alongside <li> items inside a <nav><ul>. Child <a> or <button> elements become the dropdown items — the trigger button is built automatically.

<nav>
  <ul>
    <li><a href="/">Home</a></li>

    <edible-nav-dropdown label="Products">
      <a href="/products/basic">Basic</a>
      <a href="/products/pro">Pro</a>
      <a href="/products/enterprise">Enterprise</a>
    </edible-nav-dropdown>

    <edible-nav-dropdown label="Resources">
      <a href="/docs">Documentation</a>
      <a href="/blog">Blog</a>
    </edible-nav-dropdown>

    <li><a href="/contact">Contact</a></li>
  </ul>
</nav>

| Attribute | Type | Description | | ---------- | ------- | ---------------------------------------------- | | label | string | Required. Text shown on the trigger button | | disabled | boolean | Disables the trigger and all child links |

Hover: opens on mouseenter, closes on mouseleave (with a brief delay so the cursor can travel to the dropdown list without it closing).

Keyboard on trigger: Arrow Down opens and focuses the first item · Arrow Up opens and focuses the last item · Escape closes.

Keyboard inside list: Arrow Down/Up cycle through items (wrapping) · Arrow Up on the first item closes and returns focus to the trigger · Home/End jump to first/last · Escape closes and returns focus to the trigger.

Opening one <edible-nav-dropdown> automatically closes all others on the page. Uses the WAI-ARIA APG Disclosure Navigation Menu pattern.

Design Tokens

All components inherit EdibleCSS custom properties. Override at :root level to affect both EdibleCSS and the components simultaneously:

:root {
  --accent: #7c3aed; /* purple — applied everywhere automatically */
}

No component-specific CSS variables needed.

Browser Support

Modern evergreen browsers (Chrome, Firefox, Safari, Edge). Custom Elements v1 and adoptedStyleSheets are required — both supported natively in all target browsers.

Relationship to EdibleCSS

| | EdibleCSS | edible-web-components | | ------------------ | --------------------- | ------------------------------- | | Requires JS | No | Yes | | Activation | <link> | <script> | | Uses classes | Never | Never | | Covers | Native HTML5 elements | Interactive custom elements | | Versioned together | — | Tracks EdibleCSS major versions |

Development

# Install dependencies
npm install

# Build (outputs dist/edible-wc.js and dist/edible-wc.esm.js)
npm run build

# Run tests
npm test

Contributing

Contributions are welcome! Please note that edible-web-components is intentionally minimal and opinionated. New components must align with the EdibleCSS constitution principles.

Commit Message Format

This project uses Conventional Commits for automated versioning and changelog generation. Please format your commit messages as:

<type>(<scope>): <description>

[optional body]
[optional footer]

Types:

  • feat: - New feature (bumps minor version)
  • fix: - Bug fix (bumps patch version)
  • docs: - Documentation only changes
  • style: - Code style changes (formatting, missing semi-colons, etc)
  • refactor: - Code refactoring
  • test: - Adding or updating tests
  • chore: - Maintenance tasks

Examples:

git commit -m "feat: add print stylesheet"
git commit -m "fix(forms): correct input border radius"
git commit -m "docs: update CDN installation instructions"

License

MIT — see LICENSE.txt for details.

Credits

Created by Sergei Mukhin. Built for use with EdibleCSS.