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

@tc96/properties

v1.2.0

Published

Domain-neutral property views for React applications using COSS and Base UI.

Readme

Properties

Domain-neutral property views for React applications built with COSS, Base UI, and Tailwind CSS.

The package provides small read and edit surfaces that can be composed into DataGrid cells, Kanban cards, detail panels, forms, and other SaaS views. Your application remains responsible for business meaning, permissions, option loading, mutations, validation, and persistence.

Initial primitives

  • SelectProperty: a typed single-choice badge and Select popup
  • PersonProperty: a person-reference badge with avatar and Select popup
  • MultiPersonProperty: a multi-person badge and multi-select popup
  • DateProperty: a formatted date badge and Calendar popover

The package intentionally does not export StatusProperty, PriorityProperty, AssignedProperty, or OwnerProperty. Those names encode product semantics and should be consumer compositions over the neutral primitives.

Requirements

  • React 19
  • Base UI 1.x
  • Tailwind CSS 4

The package is COSS-first. COSS is distributed as source through its component registry, so the bundled registry item declares the COSS primitives it needs through registryDependencies. A future Radix implementation will be a separate adapter that preserves the same value and callback contracts.

Installation

Install the published package with your package manager:

pnpm add @tc96/properties
# or: bun add @tc96/properties
# or: npm install @tc96/properties

The consumer must provide the peer dependencies:

pnpm add react react-dom @base-ui/react tailwindcss

The COSS primitives used by the copied source are installed by the registry route, not by npm peer dependencies. The @coss/* entries are shadcn/COSS registry items and are not published npm packages.

COSS source location

@tc96/properties supports two installation modes. Use the npm package when you want to import it from node_modules. Use the bundled registry item when you want the source copied into the consuming app.

Keep ui mapped to your primitive components and add patterns for TC96 patterns. patterns is not a replacement for ui:

{
  "$schema": "https://ui.shadcn.com/schema.json",
  "tsx": true,
  "aliases": {
    "components": "@/components",
    "ui": "@/components/ui",
    "patterns": "@/components/patterns"
  }
}

After installing the package, add the local registry item:

bun add @tc96/properties
bunx shadcn@latest add ./node_modules/@tc96/properties/registry/properties.json

The registry item targets @components/patterns/properties, which resolves through aliases.components, and declares the required COSS primitives through registryDependencies so they resolve into ./components/ui.

Tailwind must scan the installed package and the application theme must expose the standard COSS semantic tokens:

@import "tailwindcss";
@source "../node_modules/@tc96/properties/dist";

Select property

import {
  SelectProperty,
  type SelectPropertyOption,
} from "@tc96/properties";

type Category = "alpha" | "beta";

const categories: SelectPropertyOption<Category>[] = [
  { id: "alpha", label: "Alpha" },
  { id: "beta", label: "Beta" },
];

export function CategoryProperty({
  value,
  onChange,
}: {
  value: Category;
  onChange: (value: Category) => void;
}) {
  return (
    <SelectProperty
      ariaLabel="Category"
      options={categories}
      value={value}
      onValueChange={onChange}
    />
  );
}

Omit the change callback or pass readOnly to render a display-only badge.

Person property

const people = [
  {
    value: "alex",
    avatar: { src: alexAvatarUrl, fallback: "AR" },
    name: "Alex Rivera",
    description: "[email protected]",
  },
];

<PersonProperty
  ariaLabel="Reviewer"
  options={people}
  placeholder="Select a person"
  value={reviewerId}
  onValueChange={setReviewerId}
/>

name is the primary identity text. description is optional secondary text for consumer-defined context such as an email address or team. avatar accepts an optional image source and fallback text.

The consumer decides whether a person is a reviewer, owner, assignee, member, or another role. The package only understands a person reference.

PersonProperty accepts null and can emit null when its clear option is selected. For multi-person values such as a set of assignees, use the neutral MultiPersonProperty primitive:

<MultiPersonProperty
  ariaLabel="Assignees"
  options={people}
  placeholder="Add assignees"
  value={assigneeIds}
  onValueChange={setAssigneeIds}
/>

Date property

<DateProperty
  ariaLabel="Target date"
  fallback="No date"
  locale="en-US"
  timeZone="UTC"
  value={targetDate}
  onValueChange={setTargetDate}
/>

Formatting, serialization, calendar placement, and clearing are configurable. Business calculations such as whether a date is overdue belong to the consumer; emphasized only controls generic visual emphasis.

Core contract

@tc96/properties/core exports lightweight definitions without React or visual dependencies:

import type { PropertyDefinition } from "@tc96/properties/core";

const category: PropertyDefinition<"select"> = {
  id: "category",
  label: "Category",
  type: "select",
};

The package does not provide a universal runtime renderer. The current primitives have different value and interaction needs, and a universal renderer would create an unvalidated abstraction. Consumers can compose them explicitly today, either from the npm package or from the copied registry source.

Ownership boundary

| Package owns | Consumer owns | | --- | --- | | accessible trigger and popup composition | business meaning of a property | | generic option, person, and date presentation | authorization and available options | | controlled values and callbacks | remote state, validation, and mutations | | date parsing, formatting, and serialization hooks | canonical storage and time policies | | read-only and disabled presentation | workflows, automation, and navigation |

Development

Use Bun 1.3.14 and Node 24.18.0.

bun install
bun run storybook
bun run lint:ci
bun run typecheck
bun test
bun run build

See CONTRIBUTING.md and ADR-001.

License

MIT © Gabriel Melo.