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

@increasebank/card-elements

v1.0.0

Published

Composable, PCI-safe components for rendering Increase Card details in your web app.

Readme

@increasebank/card-elements

Composable React components for rendering Increase Card details in your web app without taking on PCI scope. Each field renders inside its own iframe hosted by Increase.

Install

pnpm add @increasebank/card-elements

React 18.2 or newer, including React 19.

Usage

On your server, mint a short-lived token for a specific card:

POST /cards/card_oc8ihzxcv6q2htumc15j/create_details_token
{
  "type": "card_details_token",
  "token": "3f8a1c0e…",
  "expires_at": "2026-08-26T22:01:31Z"
}

In your React app, hand the token to CardElementsProvider and drop in the sub-components:

import {
  CardCvc,
  CardElementsProvider,
  CardExpiry,
  CardNumber,
} from '@increasebank/card-elements';

export const CardDetails = ({ token }: { token: string }) => (
  <CardElementsProvider
    token={token}
    appearance={{
      fontFamily: 'Inter',
      fontSize: '16px',
      color: '#0a0a0a',
    }}
  >
    <div className="grid">
      <div className="my-field-box">
        <CardNumber appearance={{ letterSpacing: '0.05em' }} />
      </div>
      <CardExpiry />
      <CardCvc />
    </div>
  </CardElementsProvider>
);

Sandbox

A token is only valid in the environment that minted it, so say when it came from Sandbox:

<CardElementsProvider token={token} sandbox>

Appearance

The appearance prop on the provider sets defaults for every child. Per-component appearance props override on a key-by-key basis. Allowed keys:

fontFamily, fontSize, fontWeight, fontStyle, fontSmoothing, letterSpacing, lineHeight, color.

fontSmoothing takes auto (the default) or antialiased. It exists because each engine spells the same request differently, so one value sets both: antialiased asks WebKit and Blink for -webkit-font-smoothing: antialiased and Gecko for -moz-osx-font-smoothing: grayscale. Only macOS honours either, and only there does it do anything — light digits on a dark card look thinner and closer to the copy around them. Set it to match whatever your own page already does, or the element's text will be smoothed differently from the label beside it.

Digits are always laid out on a fixed pitch, which is not negotiable: masking depends on every digit occupying the same width.

Keys outside that list are dropped — file an issue if you need one we don't support.

Typography is all the iframe controls, because typography is all it can't inherit across the frame boundary. Borders, padding, background, and alignment belong on your own wrapper element, where your CSS already works. Each element sizes itself to its text and reports its baseline, so it aligns with adjacent copy like a normal inline word.

Fonts

fontFamily takes a single family, not a CSS stack — the library appends its own fallbacks. A font only renders if the iframe can resolve it, and the iframe cannot see @font-face rules from your page, so the set of usable families is fixed and typed. Passing anything else is a compile error rather than a silent fallback to the default font.

Increase self-hosts these, subset to the characters these elements render and served from the same origin as the iframe:

Inter, Roboto, Open Sans, Montserrat, Source Sans 3, Figtree, Plus Jakarta Sans, Roboto Mono, JetBrains Mono

Every one of them has tabular figures, so the digits already sit on a fixed pitch.

Import HOSTED_FONT_FAMILIES if you want to build a picker over them. Each is a variable font covering its full weight range, so any fontWeight you ask for is a real face rather than a synthesised one — check the family's range if a weight looks clamped (Open Sans starts at 300, Source Sans 3 at 200).

You can also use CSS generics and the families that ship with every OS: system-ui, ui-sans-serif, ui-monospace, sans-serif, serif, monospace, Arial, Helvetica, Georgia, Courier New, Times New Roman, Verdana. These need nothing from us.

Your own font

Declare it with customFont and hand it to the provider:

import basierCircle from './fonts/BasierCircle-Regular.woff2';
import { CardElementsProvider, customFont } from '@increasebank/card-elements';

const basier = customFont({ family: 'Basier Circle', source: basierCircle });

<CardElementsProvider token={token} fonts={[basier]} appearance={{ fontFamily: basier.family }}>

customFont returns the family name as a type only it can produce, so fontFamily stays closed: a family you forgot to declare is still a compile error rather than a silent fallback.

Pass source through your bundler, as above, rather than hard-coding a built asset URL: a hashed URL you imported can never go stale, whereas a URL you paste in breaks on your next deploy.

Serve the file with an Access-Control-Allow-Origin header. The element loads the font itself, from your URL, so the request is cross-origin and the browser will refuse a font that does not allow it. Font CDNs send this already; your own server may not. If you cannot add the header, inline the file as a data: URL instead — source accepts one.

weight takes an @font-face weight or range ('400 700'), style takes normal, italic or oblique, and generic picks what the element falls back to while the file loads: sans-serif (the default), serif or monospace. Declare one entry per file if you need more than one face.

Name it something we don't already resolve. A family called Inter or Arial would lose to the one Increase serves, so customFont refuses those rather than letting your file quietly never render.

An element only loads the faces for the family it is actually rendering, and the browser's cache covers the rest: one download serves every element on the page, and repeat visits too if you send a long max-age. A hashed asset URL can safely be immutable.

onReady waits for your font, so the element will not swap fonts under you after you have dismissed your own loading state. If the file cannot be loaded the element reports font_unavailable and carries on in the fallback, rather than waiting forever.

Your font does not need tabular figures. If its digits are not all the same width — Basier Circle's 1 is a third narrower than its 0 — the element puts each digit in a cell of equal width instead, so the value keeps its pitch and does not reflow when you toggle masked. That costs a little letterfit in exchange for a payment field that stays still.

Increase never stores or serves your font: the element loads it from wherever you already serve it, under your own licence.

PINs

<CardPin /> renders the card's PIN, for programs configured for cash disbursements:

<CardPin masked={hidden} onError={handleError} />

Not every card has one. When there is no PIN the element reports pin_unavailable and renders nothing, rather than sitting there empty — you should be able to tell whether PINs are available for your program rather than guessing from a blank space.

A PIN is never copyable, and <CardPin /> does not take copyable or onCopy. It is typed into a keypad, so there is nowhere to paste it.

Dark pages

The elements sit on whatever background you put behind them, dark included. Each one takes its colour scheme from your page, so a page that declares color-scheme: dark, or one that follows the user's own preference, needs nothing from you.

An element reads that when it mounts. If your app switches theme after that, tell it:

<CardElementsProvider token={token} colorScheme={theme === 'night' ? 'dark' : 'light'}>

colorScheme resolves per element as element ?? provider ?? detected.

Masking

masked replaces the digits with bullets, keeping the separators, so the shape of the value is preserved: •••• •••• •••• 4242. The card number keeps its last four, which you already have from the Card itself; the expiration, verification code and PIN are masked completely. Set it on the provider to mask every element at once, and on any component to override that — masked resolves per element as element ?? provider ?? false, so a <CardCvc masked /> stays hidden while the rest of the card is revealed, and omitting it inherits whatever the provider says.

It is a display affordance — against shoulder-surfing, screen sharing and screenshots — not an access control. The element still fetches the card details while masked, so it hides the value from the person looking at the screen, not from the page.

Copying

copyable turns the value itself into a copy control: clicking or pressing Enter on it writes the value to the clipboard. Set it on the provider for every element, or on any component to override — the same element ?? provider ?? false resolution as masked.

<CardNumber copyable onCopy={() => setNotice('Copied')} />

It copies the real value even while masked, and without the spaces the element renders. onCopy fires once the write succeeds, so the confirmation UI is yours; a refused write reports copy_failed instead.

The clipboard write happens inside the iframe, so the value never passes through your page. That is also why the value is the click target rather than a button of your own: the browser only permits a clipboard write in the document the click landed in, and a click on your page cannot authorise one inside ours.

Formatting

<CardNumber grouping="network" /> (the default) spaces digits the way the card's network prints them — four-six-five for American Express, four-four-four-four for Visa and Mastercard. Pass grouping="none" for unspaced digits.

<CardExpiry format="MM/YY" /> (the default) or format="MM/YYYY".

Lifecycle

onReady() fires once the element has fetched and rendered its card data and reported its size, so it is safe for dismissing your own loading state. onError({ code, message }) fires if anything fails:

token_expired, token_invalid, card_not_found, pin_unavailable, protocol_version_mismatch, initialisation_timeout, element_load_timeout, font_unavailable, copy_failed, network, unknown

font_unavailable and copy_failed are the non-fatal ones — the element carries on displaying its value. Everything else means no card data is shown. element_load_timeout usually means a Content Security Policy is blocking the iframe.

Security

  • Mint tokens server-side. Never expose your Increase API key in the browser.
  • Each token is scoped to a single card and expires in one hour.
  • The iframes load from increase.com — your CSP needs to allow frame-src https://increase.com.
  • Declaring a custom font is the one thing that lets an element load from an origin other than Increase, and only that font file, from the URL you gave it.