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

@inlayphp/ui

v0.3.14

Published

Renderer-neutral control and button class vocabulary shared by Inlay's React and Vue packages.

Readme

Inlay UI

npm License

Renderer-neutral control, button, and headless-primitive vocabulary shared by Inlay's React and Vue packages

@inlayphp/ui holds the centralized recipe layer that decides what controls, buttons, surfaces, menus, and table primitives look like. It also owns the icon-resolution contract consumed by renderer-specific primitives. It contains no components and imports neither React nor Vue: class strings and contracts are renderer-neutral, so both renderers read the same ones.

Applications do not normally install this directly. It arrives through Forms, Tables, Panels, or a plugin.

import { recipes, buttonBaseClass, controlClass, labelClass, resolveIcon } from '@inlayphp/ui'

| Export | Applies to | | --- | --- | | controlClass | Every text input, textarea, and select | | buttonBaseClass | Every button, before its variant adds colour | | focusRingClass | Focusable non-button elements | | labelClass, descriptionClass | Form labels and supporting copy | | cardClass, dialogClass | Surface and dialog shells | | badgeClass | Compact status and metadata badges | | iconButtonClass, menuItemClass | Icon actions and menu rows | | tableHeaderClass, tableRowClass, tableCellClass | Consistent table structure | | recipes.spacing, recipes.typography | Shared spacing and type scales | | recipes.focus, recipes.motion | Keyboard focus and reduced-motion behavior | | recipes.variants | Button, control, surface, badge, and table variants | | resolveIcon() | Exact-then-wildcard resolution across maps and registries |

The aggregate is useful for community components that need a recipe without depending on a renderer:

const toolbar = `${recipes.spacing.inline} ${recipes.typography.body}`
const destructive = `${recipes.variants.button.base} ${recipes.variants.button.danger}`

Recipe values read semantic variables such as --inlay-space-card, --inlay-font-size-body, --inlay-focus-ring-color, and --inlay-motion-duration. Override those tokens in the PHP Theme or a generated CSS contract and every consumer updates together.

Compose rather than copy. A denser control adds to the shared string:

const cellControlClass = `${controlClass} sm:py-1.5 sm:text-sm`
const fieldLabelClass = `${labelClass} mb-1.5`

const icon = resolveIcon('heroicon-o-save', pageIcons, packageIconRegistry)

Why this package exists

These strings used to live in @inlayphp/ui-react, which has no Vue counterpart, so every Vue package wrote its own copy — and the copies drifted where nothing could see it. Slot and custom-property checks compare names rather than the classes carrying them, and a renderer test asserts what a component does rather than how tall it is.

Measured in a browser, against React rendering the same payload: Vue's form control had lost min-h-(--inlay-control-height), so the theme's control size never reached the most common element in any form, and aria-invalid:ring-(--inlay-danger), so an invalid field showed no red ring. Vue's table buttons were 40px tall with text-base where React's were 36px with text-sm. Vue's editable table cells were bg-white with ring-zinc-950/10, ignoring the theme entirely.

tests/ThemeTokenContractTest.php fails if a renderer declares its own control or button class instead of composing one from here.

Tokens these classes read

The strings reference --inlay-* custom properties rather than fixed colours, so a theme changes them without a rebuild. The default control ring is a soft zinc-300 border and focus switches to the accent token, matching the visual hierarchy of fluent fields. Whichever root mounts the component has to declare the tokens it reads — notably --inlay-control-height, without which a control has no minimum height at all.

Tailwind source scanning

The package exports class names rather than a compiled stylesheet. Tailwind CSS 4 applications must scan this package so the CSS-variable utilities are emitted. Add the following to the application's CSS entry point (adjust the relative path for your layout):

@source '../../node_modules/@inlayphp/*/src/**/*.{ts,tsx,vue}';

The universal npm glob keeps React and Vue adapters covered as packages are added. Without this entry the generic ring-1 utility is emitted but its semantic control color can fall back to currentColor (usually a harsh black).

The token vocabulary and data-slot names are part of the shared UI contract and are kept identical across the React and Vue adapters.