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

@symbo.ls/default-config

v3.14.794

Published

Default design system configuration for Symbols v3.14. Provides baseline color, typography, spacing, media query, and theme definitions used when no custom config is supplied. The atomic CSS engine (`@symbo.ls/css`) and `@symbo.ls/scratch` consume this co

Readme

Default Config

Default design system configuration for Symbols v3.14. Provides baseline color, typography, spacing, media query, and theme definitions used when no custom config is supplied. The atomic CSS engine (@symbo.ls/css) and @symbo.ls/scratch consume this config to generate design tokens.

Check out the docs page to learn more.

Bridge — host a library that owns its DOM

Bridge (components/Atoms/Bridge.js) is the host for a third-party library that owns a DOM subtree: a rich-text editor, a map, a chart, a code editor. DOMQL owns the host node; the library owns everything inside it, and DOMQL never writes there. Any element that declares onBridgeMount is a host; Bridge is the named one (it keeps tag: 'div' under any key, so Map is not an image map).

Editor: {
  extends: 'Bridge',
  // Once, when the node is connected (just before onMounted). Returns the instance; may be async.
  onBridgeMount: async (node, el, s) => {
    const { default: Quill } = await import('quill')
    const box = node.ownerDocument.createElement('div')
    node.appendChild(box)
    return new Quill(box, { theme: 'snow' })
  },
  // After the mount, and again when a value it read changes (and on an empty el.update()).
  onBridgeUpdate: (quill, el, s) => quill.enable(!s.locked),
  // Exactly once per started mount: removal, an ancestor's removal, a route swap —
  // also when an async mount resolves after the element was removed.
  onBridgeDestroy: (quill) => quill.off('text-change'),
  // The library's nodes: capture hears what never bubbles (a scroll inside) and runs
  // before the library's own listener; selector narrows the target.
  onScroll: { capture: true, passive: true, selector: '.ql-editor', handler: (e, el) => el.call('syncScroll', e) }
}
  • el.bridge is the instance once the mount settled (undefined before and after destroy); other elements reach it through el.lookup / el.lookdown.
  • An if:-hidden host keeps its library; shown again it does not mount twice.
  • Browsers only: a server render (brender) leaves the host empty and runs no bridge hook; the browser mounts once the client re-creates the tree.
  • Keep DOMQL children beside the host, not in it; a text on the host stops at the mount.
  • The { capture, passive, once, selector, handler } form works for every node-level onXxx, not only on a host.
  • frank-audit does not flag DOM work inside the three hooks and the host's own onXxx handlers (FA205, FA503–FA510, FA512); the same code anywhere else still flags.

TooltipLayer — tooltips for nodes that cannot hold a child

The built-in Tooltip is a child element that TooltipParent reveals, so a node a library owns (a Quill toolbar button inside a Bridge host) or an icon-only button cannot get one. TooltipLayer (components/Tooltip/TooltipLayer.js) serves every [data-tooltip] node inside its PARENT, whoever made it and whenever:

RichText: {
  Editor: {
    extends: 'Bridge',
    onBridgeMount: async (node) => {
      const quill = new Quill(…)
      node.querySelector('.ql-bold').setAttribute('data-tooltip', 'Bold')
      return quill
    }
  },
  TooltipLayer: {}   // beside the host, not inside it
},
Toolbar: {
  Close: { extends: 'SquareButton', icon: 'x', aria: { label: 'Close' }, data: { tooltip: 'Close panel' } },
  TooltipLayer: { tooltipPlacement: 'top' }
}
  • Attributes: data-tooltip (the title; empty = none), data-tooltip-description (a second line), data-tooltip-placement (top | bottom | left | right, beats the layer's tooltipPlacement, default bottom). Read at every show.
  • Props: tooltipPlacement, tooltipDelay (hover, ms, default 250), tooltipHideDelay (ms, default 100). Style it like any element (theme, padding, …); default theme: 'dialog'.
  • Opens on hover after the delay (moving to the next target while one shows switches at once) and on keyboard focus (:focus-visible) at once; hover wins over focus; touch never opens it. Escape or a press on the target hides it until the pointer / focus moves to another target. The pointer may move onto the bubble (WCAG 1.4.13).
  • A role="tooltip" node with real text and an id; the target's aria-describedby carries that id while it shows (ids already there stay). It describes; an icon-only control still needs aria-label. Do not add title as well.
  • position: fixed, centred, flipped when the side is short, kept in the viewport, followed on scroll and resize.
  • One layer per parent; with nested layers the innermost serves a target. Listeners are onDocument* / onWindowResize: removed with the element.

Dropdown — keyboard and screen reader

DropdownParent (opens on hover), DropdownParentFocus (opens while focus is inside it) and DropdownSiblingFocus (opens the [dropdown] element after it) reveal their Dropdown / DropdownList panel with CSS, as they always did. On top of that they are menu buttons (WAI-ARIA APG); the documented shape needs nothing more:

FilterMenu: {
  extends: 'DropdownParent',
  Button: { text: '{{ filter | polyglot }}' },
  Dropdown: {
    DropdownList: { options: [{ key: 'all', text: '{{ all | polyglot }}' }, { key: 'mine', text: '{{ mine | polyglot }}' }] }
  }
}
  • The trigger is the parent itself when it renders a button (extends: ['Button', 'DropdownParentFocus']), else its first child other than the panel and Input_trigger. A plain text element there (Label, Text) gets role="button" and tabindex="0"; a link, a field or an icon there leaves the dropdown CSS-only (no attributes, no keys), as before. Prefer a Button.
  • The trigger gets aria-expanded, aria-controls (the panel's id, assigned when it has none) and aria-haspopup with the panel's popup role (menu for a DropdownList; listbox, tree, grid, dialog likewise; none for a plain panel). An attribute you set yourself is kept — a factory too, even while it renders nothing. A menu without a name is labelled by the trigger.
  • Keys: Enter, Space and a click toggle; ArrowDown / ArrowUp open on the first (the checked one, if any) / last item; Escape closes and returns focus to the trigger; Tab out of a menu closes it. Focus moves in at once, while the panel's reveal is still starting: a hidden item cannot take focus, so the dropdown finishes the visibility transitions between the item and its panel (they compute visible once they run anyway); the opacity / transform fade runs on. In a DropdownList (role="menu", rows role="menuitemradio" with aria-checked = the row's isActive): ArrowDown / ArrowUp / Home / End, with a roving tabindex — one item in the tab order: the one focus last went to, else the first enabled item.
  • A click pins it open (touch has no hover); a second click, Escape or focus leaving closes it. A choice in a DropdownParentFocus closes it and returns focus to the trigger (it used to drop to <body>); in a DropdownParent a choice closes it while it is open — a click or a key opened it, or focus went into the panel. A mouse press that focuses the row is focus going in, so a mouse choice closes a hover-opened menu in Chrome and Edge and leaves it open in Safari and Firefox on macOS, which do not focus a button on a press.
  • The parent's data-dropdown-open carries the explicit state: absent, the CSS decides; "true", open; "false", closed even while hovered or focused within, until focus leaves or the mouse comes back.
  • Input_trigger, the invisible checkbox of the two focus variants, stays for the mouse; on a wired dropdown it is tabindex="-1" and aria-hidden, and a child trigger also takes the wrapper's tab stop (the wrapper gets tabindex="-1"). A dropdown left CSS-only keeps both as they were.
  • DropdownList rows use the check icon (they asked for checkmark, which the default icon set does not have).
  • The @symbo.ls/dropdown kit's Dropdown, DropdownList, DropdownParent, DropdownParentFocus and DropdownSiblingFocus are aliases of these (extends: 'smbls.X'): one definition.

DropdownDisclosure — a menu of links and actions

A menu whose items are links and actions (a site's sections, an account menu, a language menu) is a disclosure, not a role="menu": a menu role puts a screen reader into application mode for a list of links.

Account: {
  extends: 'DropdownDisclosure',
  Button: { text: '{{ account | polyglot }}' },
  Dropdown: {
    Profile: { extends: 'Link', href: '/me', text: '{{ profile | polyglot }}' },
    SignOut: { extends: 'Button', text: '{{ signOut | polyglot }}', onClick: (e, el) => el.call('signOut') }
  }
}
  • The trigger (the parent itself when it is a button, else its first child that is not the panel) gets aria-expanded and aria-controls; no aria-haspopup. A text trigger gets role="button" and tabindex="0".
  • The panel's links and buttons keep the normal tab order: no menu roles, no roving tabindex, no arrow keys.
  • Click, Enter and Space toggle; Escape (on the trigger or in the panel) closes and returns focus to the trigger; focus moving out, or a press outside, closes it; a choice in the panel (a link — a router's in-place navigation too — or a button) closes it and returns focus to the trigger. A modified click (a new tab) keeps it open.
  • The panel shows only while it is open (data-dropdown-open="true"): no hover or focus-within reveal. The press-outside listener exists only while one is open.

Dialog — modal, named, translated

ConfirmDelete: {
  extends: 'Dialog',
  modal: (el, s) => s.root.confirmDelete,   // truthy: showModal(); falsy: close()
  onOk: (e, el) => el.call('deleteFile'),
  onClose: (e, el, s) => s.root.update({ confirmDelete: false }),
  DialogHeader: { Title: { Text: { text: '{{ deleteFile | polyglot }}' } } },
  DialogFooter: {}
}
  • modal (a value or (el, s) => …) opens the dialog with showModal() when truthy and closes it when falsy: the top layer, an inert page behind it, Escape, focus moved in and given back by the browser. A closed modal dialog is display: none. modal: true with an if: that mounts the dialog works too (it opens once it is in the document).
  • Escape, Cancel, OK and the close button close the dialog themselves; keep your state in step in onClose. el.node.returnValue is 'ok', 'cancel' or '' (Escape).
  • Events on the dialog: onCancel (Escape, Cancel, the close button) and onOk (OK); e.preventDefault() keeps it open. Any button inside can carry an action: data: { dialogAction: 'ok' }.
  • Named: aria-labelledby points at the DialogHeader title (mark another element with dialogTitle: true); the icon-only close button is labelled. A name you give it yourself (aria-label, aria-labelledby, a factory too) is kept.
  • Texts are polyglot keys: dialog.title, dialog.close, dialog.cancel, dialog.ok. A key your project has not translated shows the framework default for the active language: English (Title, Close, Cancel, OK) or Georgian (სათაური, დახურვა, გაუქმება, კარგი); any other language, and a project without polyglot, shows English. The defaults (DIALOG_COPY) and their resolver (resolveDialogCopy) are exported from this package and from smbls; the windows plugin's Confirm / Alert / Prompt read the same table, so one translation of dialog.cancel serves every framework dialog.
  • Without modal nothing changes on screen: the dialog renders in the flow (display: flex, no open), and a flat open: true is still the native non-modal attribute.
  • locked (a value or (el, s) => …) makes a dialog the person cannot dismiss — a forced password change, an invitation: Escape does nothing (the cancel event and the Escape key itself are prevented — a browser lets a page prevent cancel only once without a new user activation —, and closedby="none" is written), a press on the backdrop does nothing, and the built-in cancel controls (the header's close button, Cancel) do not render. OK still fires onOk. It closes from code: modal going false, or el.node.close(). Only a dialog that declares locked gets the listeners.