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

@rusl-labs/surface-html

v0.1.0

Published

Composable HTML kit for @rusl-labs/surface.

Downloads

29

Readme

@rusl-labs/surface-html

The HTML kit for Surface. A kit is a collection of React components that Surface uses to render each part of your schema. This kit gives you plain HTML — inputs, selects, checkboxes — with opt-in CSS.

You do not need to read this to get started. The root README covers the basic setup. Read this when you need to configure the kit or understand what renderers and widgets ship by default.

Quick config

import { createHtmlKit, humanizeFieldName } from "@rusl-labs/surface-html";

const kit = createHtmlKit({
  locale: "en-AU",
  fieldNameToLabel: humanizeFieldName,
  money: { currency: "AUD" },
  tel: { defaultCountry: "AU" },
  date: { dateStyle: "medium", timeStyle: "short" },
  form: { saveLabel: "Save", resetLabel: "Reset" },
});

Pass the kit to createSurfaceUi:

const { Surface } = createSurfaceUi({
  schemaResolver,
  validator: createAjvValidator(),
  kit,
});

Configuration options

| Option | Type | What it does | | --- | --- | --- | | locale | string | BCP 47 locale for money, numbers, and dates. Default: runtime locale. | | fieldNameToLabel | (name: string) => string | Format a bare property name as a label. Annotation and schema titles are not passed through. Default: identity. | | money | { currency, currencies?, locked?, currencyDisplay? } | Default currency. Schema const / enum on a currency field overrides. | | tel | { defaultCountry, countries?, showCountry? } | Phone input defaults. A field-level defaultCountry overrides the kit default. | | date | { dateStyle?, timeStyle? } | Display formatting. Input still uses native date controls. | | form | { saveLabel?, resetLabel?, enabled? } | Root Save / Reset button labels. Default enabled in input mode. | | aliases | Record<string, string> | Extra one-hop lookup keys. Host entries merge over built-in aliases. | | resolvers | RegistryEntry[] | Extra registry entries, appended after built-in defaults. |

Register your own renderers

const kit = createHtmlKit({
  resolvers: [
    // By schema $id — your component for the whole type
    { key: PERSON_ID, mode: "display", view: "card", component: PersonCard },
    // By format — your date picker for all date-time fields
    { key: "format:date-time", mode: "input", component: MyDatePicker },
  ],
});

Your entries append after the built-in defaults. Last registration for a key wins. For full registry docs: Building kits.

Built-in renderers

Structural types

The kit registers renderers for every JSON Schema type:

| Key | Mode | What it renders | | --- | --- | --- | | string | input / display | Text input or text display | | number / integer | input / display | Number input | | boolean | input / display | Checkbox | | const | input / display | Read-only fixed value | | enum | input / display | <select> (before string when both apply) | | object | input / display | helpers.fields() layout. Input has Add/Remove for optional properties. | | array | input / display | List with add/remove | | allOf | input / display | Branches over the same data | | oneOf / anyOf | input / display | Variant select (input) or matched branch (display) | | fallback | — | Renders nothing |

Widgets (short names)

Each is registered once by short name. An alias adds one extra lookup hop to the same renderer.

| Key | Input | Display | | --- | --- | --- | | email | Email input | mailto: link | | tel | National draft, E.164 on blur | Locale tel: link | | uri | URL input | Anchor link | | datetime | datetime-local ↔ RFC 3339 | Formatted date-time | | date | Native date input | Formatted date | | media | File input | Image / video / audio | | link | URL input | Anchor | | copy | Read-only value | Value + clipboard button | | input | Typed string control | Text display | | table | — | Display-only object-array table. Sort is view-only. Columns: field, label, sortable, align, fontWeight. |

$id registrations

These widgets are registered by schema $id — no annotation needed:

| $id | Constant | What it renders | | --- | --- | --- | | Money | MONEY_ID | Amount + currency selector (currency.js) | | Phone | PHONE_ID | National draft + E.164 output (libphonenumber-js) |

PHONE_ID is a map key only. The kit does not vendor the phone schema. MONEY_ID is registered directly on the money schema $id.

Built-in aliases

createHtmlKit({ aliases }) merges { ...HTML_KIT_ALIASES, ...user }. Host wins.

| You write | Resolves to | | --- | --- | | datetime / date-time | datetime | | email / idn-email | email | | uri / uri-reference / iri / iri-reference | uri | | widget $kind URIs | the short name | | PHONE_ID | tel |

Root shell

The HTML kit sets kit.Root. In input mode, the root body is wrapped in a <div class="surface-form" role="group"> with Reset and Save buttons — not a native <form> submit.

On Save, the kit deep-applies schema const (forced) and default (when missing), then calls validator.validate(). Only a valid result calls Surface onSubmit({ data }).

Configure the buttons:

createHtmlKit({ form: { saveLabel: "Save invoice", resetLabel: "Discard" } });

CSS

@rusl-labs/surface-html/surface.css is opt-in. Class names ship unstyled. Import the CSS for a baseline. Theme with CSS custom properties:

:root {
  --surface-accent: #265fd1;
  --surface-field-bg: #f8fafc;
  --surface-border: #d7dee9;
  --surface-field-gap: 1rem;
}

Stable class names

Chrome nodes carry these classes (exported as surfaceClass):

| Class | Where | | --- | --- | | surface-object / surface-object-body | Object body and property stack | | surface-field / surface-label / surface-control | Leaf field chrome | | surface-value | Display-mode value | | surface-form / surface-form-actions / surface-button-primary | Root form shell | | surface-form-errors | Form-level issues after failed Save | | surface-invalid / surface-error | Field invalid state and message |

Layout attributes

The kit stamps data-surface-layout and data-surface-direction on object bodies, plus classes surface-layout-props / surface-layout-stack and surface-direction-vertical / surface-direction-horizontal.

<Surface labels={false} />

labels inherits. false hides field chrome on that node and descendants. A child can set labels: true to override.

Related

| Doc | Why | | --- | --- | | Building kits | Full kit authoring guide | | DESIGN.md | HTML kit look, class list, widget contract | | @rusl-labs/surface | Core API reference | | Annotations | Annotation model |