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

@inputcn/card

v0.1.1

Published

Credit card input for React with brand detection and Luhn validation.

Readme

@inputcn/card

Brand detection, Luhn, and grouping that reflows mid-type.

npm license types

Live demo · The contract · All 11 components


Number, expiry and CVC as one component with one canonical value. Grouping reflows as the brand is detected — 4-4-4-4 for Visa, 4-6-5 for Amex — and the CVC length follows it.

Install

Recommended — copy the component source into your project, the way shadcn/ui does:

npx shadcn@latest add https://input-cn.vercel.app/r/card-input.json

Or install the package and import it directly:

npm i @inputcn/card

[!NOTE] Either way, import the stylesheet once in your app root:

import "@inputcn/core/styles.css"

Every colour and radius resolves from the shadcn CSS variables you already define, so it matches your theme with no configuration.

Usage

<CardInput
  label="Card details"
  value={card}         // "4242424242424242" — digits only
  onChange={setCard}
  name="card"
  expiryName="exp"
  cvcName="cvc"
  brands={["visa", "mastercard"]}
  notExpired
/>

[!IMPORTANT] onChange emits the canonical value, never the formatted string.

| Type | Form | Example | |---|---|---| | string | digits only | "4242424242424242" |

Pass name and a hidden input carries that value, so FormData and Server Actions receive it too — with no client component.

What makes it different

  • Focus advances correctly. Only when the number is a valid length for its brand and passes Luhn. Visa accepts 16, 18 and 19 digits, so advancing on the first valid length strands anyone with a 16-digit card.
  • Rejects by name. brands produces “We do not accept American Express”, not “invalid card”.

Validation

Rules are props. No schema, no resolver, no onBlur handler. Errors stay silent until the first blur, then clear the moment the value becomes valid.

| Prop | Type | Description | |---|---|---| | brands? | readonly CardBrand[] | Restrict to the brands your PSP actually accepts. | | notExpired? | boolean | Reject a card whose expiry has passed. Requires an expiry value. | | requireCvc? | boolean | Require the CVC to be filled to the brand's length. |

Every error element carries a data-rule attribute naming the rule that failed, so tests assert the rule rather than the sentence.

A matching Zod schema ships from a separate entry point, so Zod never enters your component bundle:

import { cardSchema } from "@inputcn/card/schema"

Props

| Prop | Type | Description | |---|---|---| | expiry? | string | Expiry as typed, e.g. "04 / 28". Controlled separately from the number. | | defaultExpiry? | string | — | | onExpiryChange? | (value: string) => void | — | | cvc? | string | — | | defaultCvc? | string | — | | onCvcChange? | (value: string) => void | — | | label? | ReactNode | Visible label. Supply this or aria-label — a field with neither has no accessible name. | | hint? | ReactNode | Helper text under the field. Replaced by the error message while one is showing. | | layout? | CardLayout | stacked is three fields; single is one row, Stripe-style. | | expiryName? | string | Field names for native submission. | | cvcName? | string | — |

Plus the 23 props every inputcn component implements identically — value, onChange, name, required, variant, size and the rest. See the props contract.

[!WARNING] This is not a PCI-compliant capture path on its own. It is an input; the compliance boundary is your payment processor iframe.

Accessibility

Keyboard complete, labelled, and errors announced once through role="alert" rather than on every keystroke. 50 axe-core checks run on every commit.

[!CAUTION] Screen readers have not been verified yet. Until they are, this library is structurally accessible, not screen-reader verified. The full audit, including what is untested and why, is in ACCESSIBILITY.md.


Part of inputcn — the inputs shadcn/ui doesn't ship. Not affiliated with or endorsed by shadcn.