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

@loomidev/core

v0.3.0

Published

Shared runtime assets for loomi components: theme styles, palette, the accent helper and small DOM utilities. The loomi analogue of BladewindUI's core package.

Readme

@loomidev/core

Shared runtime assets for loomi components — the shared runtime for Loomi components package. Every component imports its theme styles and helpers from here, so the shared plumbing lives in exactly one place.

npm install @loomidev/core lit

Accessibility

For the library-wide baseline, see Foundations — Accessibility.

Responsive behavior

For the shared container and viewport rules, see Foundations — Responsive behavior.

Dark mode

For theme activation, token overrides, and contrast guidance, see Foundations — Dark mode.

Exports

| Export | Description | | -------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | themeStyles | The shared :host design tokens (re-exported from @loomidev/theme). | | loomiStyles(...styles) | Prepends themeStyles, motionStyles, elevationStyles, and focusStyles to a component's own styles. Use in static styles. | | motionStyles | Shared entrance-animation @keyframes + motion tokens (see below). Already included by loomiStyles(). | | elevationStyles | Shared --loomi-shadow-elevated drop-shadow token (see below). Already included by loomiStyles(). | | focusStyles | Shared --loomi-focus-ring-color token (see below). Already included by loomiStyles(). | | accentVars(color) | Returns the per-instance accent custom properties for a color (see below). | | cssColor(color, shade) | A single themed color value with private-default fallback, for inline use. | | onClickOutside(el, handler) | Calls handler on a click or right-click outside el (crosses shadow boundaries). Returns a cleanup fn. | | randomSuffix() | A short random id, e.g. for de-duplicating notification keys across component instances. | | nextMenuFocusIndex(event, currentIndex, itemCount) | Resolves an Arrow/Home/End keydown into the next index to focus in a top-level menu (the shared shape behind @loomidev/dropmenu and @loomidev/context-menu), or undefined for any other key. Doesn't touch the DOM — the caller's own focusItemAt()-style method wraps the index and moves focus. | | deepActiveElement() | Walks into nested shadow roots to find the actually-focused element. | | trapTabFocus(event, focusable) | Keeps Tab/Shift+Tab cycling within focusable — call once event.key === "Tab" is confirmed. Used by @loomidev/modal and @loomidev/lightbox. | | FOCUSABLE_SELECTOR | The CSS selector trapTabFocus and its callers use to find focusable elements. | | OverlayReparent | Moves an overlay element to document.body while open and back on close (moveToBody() / restore()), guarding against the reparent itself firing disconnectedCallback/connectedCallback (isMovingInDom). | | onExitAnimationEnd(el, done) | Calls done once el has finished playing its exit animation (backed by a timer, so it always fires). Returns a cancel fn for a reopen mid-close. Pair with the -out keyframes to close an overlay with motion instead of snapping it away. | | positionFloatingPanel(anchor, panel, placement?) | Places a position: fixed panel beside its anchor in viewport coordinates, flipping above and swapping alignment to stay on screen. Returns the side it settled on ("top" | "bottom"). Used by @loomidev/dropmenu and @loomidev/split-button; publishes --loomi-anchor-width on the panel. | | positionFloatingSubmenu(anchor, panel, options?) | The sibling of the above for submenus: places a panel beside its anchor row, flipping to the row's left when there's no room on the right and sliding up when it's taller than the room below. Returns the side it settled on ("left" | "right"); pass that back as options.prefer for a nested submenu so a flipped chain keeps going the same way. | | supportsPopover(el) | Whether el can be promoted to the top layer with showPopover() — pair it with positionFloatingPanel so a panel escapes ancestor overflow, and fall back to plain position: fixed when it isn't supported. | | setLoomiLocale(locale) / getLoomiLocale() | Set or read the shared locale used by translated component defaults. | | defineLoomiTranslations(locale, messages) | Add or override translations for built-in component text. | | loomiT(path, params, locale) | Translate a shared message by key, with English fallback. | | LOOMI_COLORS, LOOMI_SHADES, isLoomiColor, LoomiColor, LoomiShade | Palette (re-exported from @loomidev/theme). |

import { LitElement } from "lit";
import { loomiStyles, accentVars } from "@loomidev/core";
import { componentStyles } from "./generated/styles.css.js";

class Foo extends LitElement {
  static styles = loomiStyles(componentStyles);
  render() {
    return html`<span style=${accentVars("red")} class="thing">…</span>`;
  }
}

Motion

Don't hand-roll a new fade/pop/slide @keyframes block in a component package. loomiStyles() already prepends a shared set from motionStyles (src/motion.ts), so every component that uses loomiStyles(componentStyles) can reference these by name for free, with prefers-reduced-motion handled centrally:

| keyframe | motion | | ---------------- | -------------------------------------------------- | | loomi-fade-in | opacity only | | loomi-pop-in | fade + scale up from 0.98 | | loomi-rise-in | fade + rise 8px + scale up from 0.98 | | loomi-drop-in | fade + drop down 4px (opens downward, e.g. a menu) | | loomi-slide-in | fade + slide in 12px from the trailing edge | | loomi-spin | continuous 360° rotation, for loading spinners |

.loomi-dialog {
  animation: loomi-rise-in var(--loomi-motion-duration) var(--loomi-motion-ease);
}
.loomi-spinner {
  animation: loomi-spin var(--loomi-spin-duration) linear infinite;
}

--loomi-motion-duration (default 0.16s) and --loomi-motion-ease (default ease) are the shared entrance-animation timing tokens; --loomi-spin-duration (default 0.7s, slows to 1.6s under prefers-reduced-motion rather than stopping, since a spinner going still would hide that work is in progress) times the spin keyframe. Override any of these per-component only if that component genuinely needs different timing (see @loomidev/floating-panel's --loomi-floating-panel-duration, which falls back to var(--loomi-motion-duration) rather than a hardcoded value).

Only add a new keyframe to motion.ts if it's a genuinely new motion primitive reused across components. A component that layers its own positioning transform — e.g. a centered overlay combining translate(-50%, -50%) with a scale-in — should keep that composite keyframe local instead of forcing the shared list to carry a variable transform base; @loomidev/floating-panel's .is-centered variant is the example to follow.

Elevation

Don't retype the floating dialog/panel drop-shadow rgba stack. loomiStyles() prepends --loomi-shadow-elevated from elevationStyles (src/elevation.ts), so any component whose host or panel sits above the page — a modal, drawer, or floating panel — can reference it directly instead of copy-pasting the same shadow:

.loomi-dialog {
  box-shadow: var(--loomi-shadow-elevated);
}

This is for the one "floating surface" elevation tier shared by @loomidev/modal, @loomidev/drawer, and @loomidev/floating-panel. Smaller surfaces like dropmenu, popover, and notification use a lighter shadow that isn't (yet) shared — don't force them onto this token just for the sake of reuse; introduce a second tier only if a third component needs that exact lighter shadow too.

Focus ring

Don't hardcode a --loomi-primary-<shade> for a :focus-visible outline. The public theme slots are deliberately left undeclared (see @loomidev/theme's tokens) so a :root override can inherit straight through the shadow boundary — which means a bare var(--loomi-primary-400) with no fallback silently resolves to nothing, and the outline renders as none for any consumer who hasn't happened to set that exact shade at :root. loomiStyles() prepends --loomi-focus-ring-color from focusStyles (src/focus.ts), which is never unfallback'd:

.loomi-thing:focus-visible {
  outline: 2px solid var(--loomi-focus-ring-color);
  outline-offset: 2px; /* pick whatever offset fits this component's layout */
}

This is the default ring color, for components with no per-instance theming. A component that calls accentVars() (creditcard, slider, ...) should reference --_loomi-accent directly instead — don't route it through --loomi-focus-ring-color. Nested var() references inside an inherited custom property resolve at the element where the outer property was declared, not at the element that finally consumes it, so a :host-level token can't "automatically" pick up an accent color set on a descendant wrapper (which is how accentVars() is typically applied). Only width and offset are left to each component regardless of which color source it uses, since those are genuinely layout-driven (a small round slider thumb needs more breathing room than a rectangular card).

Internationalization

Loomi keeps built-in component copy in @loomidev/core: placeholders, validation messages, aria labels, pagination strings, datepicker month/week names, and similar defaults. User-provided text still wins, so attributes like label, placeholder-line1, ok-button-label, and no-data-message remain the right way to customize one component.

Set the shared locale before rendering components:

import { setLoomiLocale } from "@loomidev/core";
import "@loomidev/datepicker";
import "@loomidev/filepicker";

setLoomiLocale("fr");

Or override a single component:

<loomi-datepicker locale="de"></loomi-datepicker>
<loomi-filepicker locale="pt_BR"></loomi-filepicker>

Built-in locales: en, ar, de, es, fr, it, ml, pt_BR, tr, and zh_CN. Datepicker month and weekday names are formatted with the component/global locale.

Each built-in locale lives in its own file under src/locales/ (e.g. src/locales/fr.ts), so contributing a translation doesn't mean editing one giant file. To add a built-in language, copy en.ts to <locale>.ts, translate the strings, and register it in src/locales/index.ts.

To customize copy or add another language without touching this package at all, register only the keys you want to change at runtime — datepicker custom locales may also provide monthsShort, monthsLong, and weekdaysShort arrays.

import { defineLoomiTranslations, setLoomiLocale } from "@loomidev/core";

defineLoomiTranslations("ak", {
  datepicker: {
    placeholder: "Paw da a wobɛpaw",
    monthsShort: ["S-Ɔ", "K-Ɔ", "E-Ɔ", "E-O", "E-K", "O-A", "A-K", "D-Ɔ", "F-Ɛ", "Ɔ-A", "O-O", "M-Ɔ"],
    weekdaysShort: ["Kwe", "Dwo", "Ben", "Wuk", "Yaw", "Fia", "Mem"],
  },
  filepicker: {
    placeholderLine1: "Paw fael anaa twe bra ha",
    placeholderLine2: "%s kosi %s",
  },
});

setLoomiLocale("ak");

--loomi-* (public theme) vs --_loomi-accent (private, per-instance)

These are two different layers — this is the answer to "why --loomi-accent when --loomi-primary already themes everything?"

  • --loomi-<color>-<shade> are the public theme tokens. Setting one (e.g. :root { --loomi-primary-600: #16a34a }) re-skins every component that uses that color. This is your global theming knob.
  • --_loomi-accent* are private, per-instance variables (note the leading _). A <loomi-checkbox color="error"> and a <loomi-checkbox color="success"> on the same page need different active colors, so each instance sets its own --_loomi-accent from its color attribute, and the component's CSS paints with var(--_loomi-accent).

Crucially, accentVars(color) resolves each accent slot through the public token with the private default as fallback:

--_loomi-accent: var(--loomi-red-600, var(--_loomi-red-600-default));

So a per-instance accent still honors a global --loomi-red-600 override. The default color is primary, so an un-colored control follows --loomi-primary-* automatically.

accentVars(color) defines: --_loomi-accent (600), --_loomi-accent-strong (700), --_loomi-accent-soft (100), --_loomi-accent-softer (50), --_loomi-accent-ring (200), --_loomi-accent-fg (700), --_loomi-accent-border (200).

Dependencies

  • @loomidev/theme