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

@hyperfrontend/ui-utils

v0.0.9

Published

Modular DOM utilities for dynamic styling, gesture detection, element lifecycle, and color manipulation.

Readme

Modular DOM utilities for dynamic styling, gesture detection, element lifecycle, and color manipulation.

• 👉 See documentation • 👉 See API reference • 👉 See guides & tutorials

What is @hyperfrontend/ui-utils?

Sometimes a framework is not on the table. You are writing an embed that drops into someone else's page, a debug overlay, a script tag, a canvas experiment: something where React would be more runtime than the thing it wraps. So you are back to document.createElement and appendChild, a <style> tag you have to remember to remove, and a ResizeObserver you have to remember to disconnect. This package is that pile of chores, written once and tested.

The parts worth the install:

  • createElement gives you the node plus attach, detach, show, and hide, with an opacity transition when you pass a duration.
  • addStylesheet injects real CSS and hands back the function that removes it, so your rules leave when your widget does.
  • syncElementDimensions pins an overlay to an element you do not control and keeps it there through resizes.
  • getElementAsync polls for a node that has not rendered yet and returns a cancel function.
  • createGestureListener covers Escape and pinch-out with one cleanup.
  • setupAudio waits for the click or touch that browsers require before an AudioContext will start.

Anything that attaches something gives you back the function that detaches it.

At a glance:

import { createElement, syncElementDimensions } from '@hyperfrontend/ui-utils/element'
import { addStylesheet } from '@hyperfrontend/ui-utils/style'

const [, removeStyles] = addStylesheet(
  {
    '.hf-hint': { position: 'fixed', opacity: '0', outline: '2px solid #f0f' },
  },
  'hf-hint'
)

const hint = createElement('div', { className: 'hf-hint' })
hint.attachTo(document.body)
hint.show(150) // opacity transition over 150ms

// follow a node you do not own, through every resize
const stopTracking = syncElementDimensions('#third-party-widget', hint.ref)

// teardown leaves the page as you found it
stopTracking()
hint.detachFromParent()
removeStyles()

Key Features

Architecture Highlights

Each capability sits behind its own secondary entry point (/element, /style, /selector, /color, /event, /audio, /mobile, /time, /misc, /component), so importing one never drags in the rest. Everything is built on plain browser APIs (ResizeObserver, touch events, Web Audio) with no third-party dependencies.

Why Use @hyperfrontend/ui-utils?

A framework is not always an option

Embeds on someone else's page, browser extensions, tooling panels, canvas demos, snippets that ship as one script tag: places where you cannot mount a component tree, or would rather not pay for one. These are plain functions over plain DOM nodes, so they run under any framework or none, and they do not care what rendered the page around them.

The cleanup is the point

Overlay code leaks in predictable ways: a stylesheet that outlives the widget it styled, an observer nobody disconnected, a poll still running long after the element showed up. Every function here that attaches something returns the thing that removes it, so teardown is a short list of calls you already have instead of a hunt through the document.

Following elements you do not control

syncElementDimensions takes a source and a target, copies width, height, top, left, and position from one to the other, and repeats that on every resize of the source. Both arguments accept a selector, so the source can be a node that has not rendered yet: getElementAsync polls for it every 100ms and gives up after 10 seconds by default, and the cleanup function cancels the poll if you gave up first.

Import one corner, not the package

Every capability is its own entry point, so import { hexToRgb } from '@hyperfrontend/ui-utils/color' pulls in the color conversions and nothing else. That matters when the whole budget for an embed is a few kilobytes.

Installation

npm install @hyperfrontend/ui-utils

Quick Start

// Element creation with lifecycle methods
import { createElement } from '@hyperfrontend/ui-utils/element'

const modal = createElement('div', {
  className: 'modal',
  inlineStyle: { position: 'fixed', zIndex: '1000' },
})

modal.attachTo(document.body)
modal.show(300) // Fade in over 300ms
modal.hide(300) // Fade out over 300ms
modal.detachFromParent() // Clean removal

// Type-safe CSS selector building
import { CssSelector } from '@hyperfrontend/ui-utils/selector'

const selector = new CssSelector('div').class('card').attribute('data-status', 'active').hover().toString() // 'div.card[data-status="active"]:hover'

// Color manipulation
import { getColorVariation, hexToRgb, rgbToHex } from '@hyperfrontend/ui-utils/color'

const dimmedBlue = getColorVariation('#0066cc', 128) // 'rgba(0,51,102,0.5019607843137255)'
const rgb = hexToRgb('#ff5500') // { r: 255, g: 85, b: 0 }
const hex = rgbToHex(255, 85, 0) // '#ff5500'

// Gesture detection with cleanup
import { createGestureListener } from '@hyperfrontend/ui-utils/event'

const cleanup = createGestureListener(() => console.log('Escape or pinch detected'))
// Later: cleanup() to remove listeners

API Overview

One rule organises the whole surface: anything that attaches something hands back the function that detaches it. addStylesheet returns a tuple of the <style> element it injected and the function that removes it; onElementResize and createGestureListener return that remover on its own, one call closing all four listeners in the gesture case. Teardown is a list of functions you are already holding rather than a hunt through the document, which is what the counters above are counting.

The second shape to know is what createElement hands back: not the node, but an object around it carrying attachTo, detachFromParent, addChild, removeChild, show, hide, a visible flag, and ref, the live element for anything the wrapper does not do. show and hide take an optional duration in milliseconds and transition opacity over it. The tag shorthands (div, button, canvas and twenty-one others) are the same function with the tag already chosen.

Targets are elements or selector strings, interchangeably, and that is what lets syncElementDimensions pin an overlay to a third-party node before that node exists: underneath, getElementAsync polls every 100ms, gives up after 10 seconds, and the cleanup it returns cancels the poll if you gave up first.

Ten secondary entry points sit beside the root one, and they exist for weight rather than filing: importing from @hyperfrontend/ui-utils/color costs the color conversions and nothing else, which is what lets any of this into an embed with a few kilobytes to spend. Two are worth naming because their names give nothing away: /time is a promise delay and a UTC timestamp formatter, and /misc is one function, simpleHash, which turns a string into six characters (simpleHash('hello world') returns 'to5x38').

Every export, option and type is in the API reference, listed under the entry point it belongs to.

Compatibility

Note: Some DOM utilities require browser APIs; check individual exports.

Output Formats

| Format | File | Tree-Shakeable | | ------ | -------------------------- | :------------: | | ESM | index.esm.js | ✅ | | CJS | index.cjs.js | ❌ | | IIFE | bundle/index.iife.min.js | ❌ | | UMD | bundle/index.umd.min.js | ❌ |

CDN Usage

<!-- unpkg -->
<script src="https://unpkg.com/@hyperfrontend/ui-utils"></script>

<!-- jsDelivr -->
<script src="https://cdn.jsdelivr.net/npm/@hyperfrontend/ui-utils"></script>

<script>
  const { createElement, hexToRgb, rgbToHex } = HyperfrontendUIUtils
</script>

Global variable: HyperfrontendUIUtils

Part of hyperfrontend

This library is part of the hyperfrontend monorepo.

📖 Full documentation

License

MIT