@hyperfrontend/ ui-utils§
Modular DOM utilities for dynamic styling, gesture detection, element lifecycle, and color manipulation.
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:
createElementgives you the node plus attach, detach, show, and hide, with an opacity transition when you pass a duration.addStylesheetinjects real CSS and hands back the function that removes it, so your rules leave when your widget does.syncElementDimensionspins an overlay to an element you do not control and keeps it there through resizes.getElementAsyncpolls for a node that has not rendered yet and returns a cancel function.createGestureListenercovers Escape and pinch-out with one cleanup.setupAudiowaits for the click or touch that browsers require before anAudioContextwill 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
Modular secondary entry points
for importing one corner of the package at a time
attach, detach, show, hide, and a live
ref, all on the objectcreateElementreturnsinject rules from a CSS string or a style map, label them, and get the remover back
chainable
id,class,attribute,nth,childOf, and pseudo-class methods with validationhex and RGB in both directions, with alpha, plus scaled variations of a base color
Escape key and pinch-out behind one listener with one cleanup
ResizeObserverand dimension syncing that stop when you call what they returnedvia user agent parsing
resolves an
AudioContextafter the click or touch browsers insist on
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
Runs on
- Node.js>=18.0.0partially supported
- Browserssupported
- Web Workerssupported
Some utilities require browser APIs; check individual exports.
Ships as
- ESMTree-shakeable
- CJSNode and older bundlers
- IIFEHyperfrontendUIUtils
- UMDHyperfrontendUIUtils
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
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.
API Reference§
Module Structure
11 modules · 154 total exports
@hyperfrontend/ui-utils
UI utilities for audio, color, components, elements, events, selectors, and styling.
@hyperfrontend/ui-utils/audio
Audio context initialization utilities.
@hyperfrontend/ui-utils/color
Color manipulation utilities for RGB/hex conversions and color variations.
@hyperfrontend/ui-utils/component
Styled DOM component factory with style and create functions.
@hyperfrontend/ui-utils/element
DOM element creation, retrieval, and dimension synchronization utilities.
@hyperfrontend/ui-utils/event
Synthetic mouse events and gesture listener utilities.
@hyperfrontend/ui-utils/misc
Miscellaneous UI utilities including simple string hashing.
@hyperfrontend/ui-utils/mobile
Mobile device detection utilities.
@hyperfrontend/ui-utils/selector
CSS selector builder and validation utilities.
@hyperfrontend/ui-utils/style
Dynamic styling utilities for CSS rules and stylesheet management.
@hyperfrontend/ui-utils/time
Animation-frame based pause and timestamp formatting utilities.
Related reading§
- How-to
How to style a widget you inject into someone else's page
My widget ships as one script into markup I do not own, and the style tag it injects has no owner: mounting twice leaves two copies, and unmounting leaves the rules behind.
- Getting started
Getting Started
Set HyperFrontend up and embed a first feature.
- Architecture
Architecture Guide
How the packages fit together.

