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

fvn-ui

v0.2.0

Published

Minimalist vanilla JS component library

Downloads

582

Readme

fvn-ui

Minimal vanilla JS component library with layout helpers. Zero dependencies.

🤖 AI/LLM Users: See AGENTS.md for quick reference or LLM.md for complete documentation.

Quick Start

Via CDN

<script src="https://unpkg.com/fvn-ui@latest/dist/ui.js"></script>
<script>
  // direct
  ui.card()
  // or granular
  const { card } = window.ui;
</script>

Via NPM

npm install fvn-ui

With a bundler (Vite, webpack, esbuild):

import { ui } from 'fvn-ui'

ui.button({ label: 'Save' })
ui.switch({ label: 'Dark mode' })

Without a bundler (native ES modules):

import { ui } from 'https://unpkg.com/fvn-ui@latest/dist/ui.esm.js'

Tree-shakeable imports (bundler only):

import 'fvn-ui/style.css'
import { button } from 'fvn-ui/button'
import { card } from 'fvn-ui/card'

Components

Layout (layout. or direct import) | Component | Description | |-----------|-------------| | row / col | Flexbox containers (grow by default) | | card | Container with title, description, content | | dashboard | View management with navigation | | header | Title + description group | | title / description | Text primitives | | label | Form label | | divider | Horizontal/vertical separator |

Inputs | Component | Description | |-----------|-------------| | button | Buttons with variants, colors, icons | | checkbox | Checkbox | | editable | Contenteditable with text input features | | input / textarea | Text input with label and validation | | radio | Radio button group | | select | Dropdown with filter and multiselect | | switch / toggle | Boolean inputs | | toggleGroup | Tab-style button group |

Overlays | Component | Description | |-----------|-------------| | modal / tooltip | Dialogs and popovers | | confirm | Confirmation dialog with trigger | | collapsible | Expandable sections | | tabs | Tabbed content |

Media | Component | Description | |-----------|-------------| | avatar | User avatar | | image | Image with loading states | | svg | Icon system |

Layout Helpers

Flexbox containers that grow by default to fill parent. Args can be in any order.

import { el, row, col, layout } from 'fvn-ui'

// Basic usage
layout.row([ button({ label: 'A' }), button({ label: 'B' }) ])
layout.col(parent, { gap: 2, children: [...] })

// Alignment shorthands (same for row and col)
row({ start: true }, [...])    // aligned left (default)
row({ center: true }, [...])   // centered
row({ end: true }, [...])      // aligned right
col({ end: true }, [...])      // aligned bottom

// Push child to end
row([
  button({ label: 'Cancel' }),
  button({ label: 'Save', end: true })  // pushed right
])

// Opt-out of grow
row({ grow: false }, [...])    // shrink to content

| Container Props | Description | |-----------------|-------------| | gap: 4 | Space between children (0-10, default: 2) | | start, center, end | Align children on main axis | | grow: false | Shrink to content |

| Child Props | Description | |-------------|-------------| | end: true | Push to end (right in row, bottom in col) | | start: true | Push to start |

| Spacing Props | Description | |---------------|-------------| | padding: 4 | All-around padding (1-10) | | block: 4 | Vertical padding (1-10) | | inline: 4 | Horizontal padding (1-10) |

Event Callbacks

All callbacks follow a consistent (value, ...context, event) pattern:

// First arg is always the unwrapped value, last arg is the event
input({ onInput: (value, event) => console.log(value) })
checkbox({ onChange: (checked, event) => ... })
radio({ onChange: (value, item, event) => ... })
select({ onChange: (value, item, event) => ... })
tabs({ onChange: (value, item, event) => ... })

// `this` is bound to the element
input({ onChange(value) { console.log(this.id, value) } })

Runtime Limit Updates

input() and editable() support runtime limit updates for validation/counters.

const bio = ui.input({ label: 'Bio', rows: 4, counter: true, min: 10, max: 500 })
bio.setLimits(5, 300)
bio.setLimits({ max: 120 })  // min unchanged
bio.setLimits({ min: null }) // clear min

const notes = ui.editable({ label: 'Notes', counter: true, min: 10, max: 500 })
notes.setLimits({ max: 200 })

setLimits(...) accepts (min, max) or { min, max }. undefined keeps existing bounds, null clears a bound.

Editable Rich + Markdown Mode

editable({ rich: true }) uses the built-in contenteditable rich driver and a minimal fixed toolbar: heading, bold, italic, quote, list, link, and markdown mode toggle.

const ed = ui.editable({ label: 'Body', rich: true, rows: 6 })

await ed.toggleMarkdownMode() // rich <-> markdown editor
await ed.toMarkdown()         // Promise<string>
await ed.toHTML()             // Promise<string>
  • value is mode-dependent:
    • rich mode: HTML
    • markdown mode: markdown
  • onInput(value, event) / onChange(value, event) emit mode-dependent values.
  • Link uses one universal button (set URL / clear URL).
  • Numbered lists are not supported in toolbar (unordered list only).
  • toMarkdown() lazily loads turndown from jsDelivr on first use.
  • toHTML() lazily loads marked from jsDelivr on first use.

CSS Variables

Common CSS variables available for custom styling. See style.css for full list.

| Variable | Description | |----------|-------------| | --space-1 to --space-10 | Spacing scale (used by gap, padding props) | | --back | Background color | | --text | Text color | | --muted | Muted/secondary text | | --hover | Hover state background | | --border | Border color | | --radius | Common border radius |

AI Assistant Setup

When using AI coding assistants with fvn-ui, copy the docs to your project for better discoverability.

Quick Setup (all tools)

# Using npx (recommended)
npx fvn-ui

# Or add to package.json scripts
{
  "scripts": {
    "docs": "fvn-ui"
  }
}

This copies AGENTS.md and LLM.md to your project root.

Tool-Specific Rules

Option 1: Repository instructions (recommended)

mkdir -p .github && cp node_modules/fvn-ui/RULES.md .github/copilot-instructions.md

Option 2: VS Code settings (Cmd/Ctrl+Shift+P → "Preferences: Open User Settings (JSON)")

{
  "github.copilot.chat.codeGeneration.instructions": [
    { "file": "AGENTS.md" },
    { "file": "LLM.md" }
  ]
}

Copilot also auto-discovers AGENTS.md, CLAUDE.md, and GEMINI.md in project root.

Option 1: Project rules (recommended)

mkdir -p .cursor/rules && cp node_modules/fvn-ui/RULES.md .cursor/rules/fvn-ui.md

Option 2: Legacy .cursorrules (still supported, will be deprecated)

cp node_modules/fvn-ui/RULES.md .cursorrules

Option 3: User rules (Cursor Settings → Rules → add global rules)

Cursor also auto-discovers AGENTS.md in project root and subdirectories.

Option 1: Workspace rules (recommended)

mkdir -p .windsurf/rules && cp node_modules/fvn-ui/RULES.md .windsurf/rules/fvn-ui.md

Option 2: Global rules (applies to all projects) Create/edit ~/.windsurf/global_rules.md and paste contents of RULES.md.

cp node_modules/fvn-ui/RULES.md CLAUDE.md

Claude looks for CLAUDE.md in project root.

Most AI tools look for AGENTS.md in the project root (already copied above). If your tool supports custom instructions, point it to LLM.md for complete documentation.

Documentation

Each component has JSDoc with examples. See source files in src/fvn-ui/components/ or example page.