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

@19h47/calendar

v2.0.0

Published

Calendar

Downloads

292

Readme

@19h47/calendar

Lightweight date / range picker. Markup and CSS are yours — the package fills the grid and handles selection.

Installation

pnpm add @19h47/calendar

Markup

Required hooks:

| Selector | Role | | -------- | ---- | | Root element | Passed to new Calendar(el, options) | | .js-previous / .js-next | Month navigation (optional) | | .js-title | Month / year label; click advances to the next month (same as .js-next) | | .js-days | Weekday headers row | | .js-body | Day cells | | .js-day | Day button (generated) |

<div class="Calendar js-calendar">
	<header>
		<button type="button" class="js-previous">Previous</button>
		<button type="button" class="js-title" id="calendar-label" aria-live="polite"></button>
		<button type="button" class="js-next">Next</button>
	</header>
	<table role="grid" aria-labelledby="calendar-label">
		<thead>
			<tr class="js-days"></tr>
		</thead>
		<tbody class="js-body"></tbody>
	</table>
</div>

Usage

import Calendar, { type Options } from "@19h47/calendar";

const el = document.querySelector(".js-calendar") as HTMLElement;
const options: Partial<Options> = {
	locale: "fr",
	deselect: true,
	buttonClass: "btn btn-outline-primary", // optional — lib ships unstyled buttons
};

const calendar = new Calendar(el, options);
calendar.init();

el.addEventListener("Calendar.change", ({ detail }) => {
	// detail.values → string[] (`YYYY-MM-DD`)
	console.log(detail.values);
});

destroy() detaches listeners and clears the grid.

Single date

new Calendar(el, { single: true, deselect: true }).init();

Date range

new Calendar(el, { single: false }).init();

Locale & week start

locale drives labels via Intl. Week start defaults from the locale (weekInfo); pass firstDay to override.

new Calendar(el, { locale: "fr" }).init();
// fr → week starts Monday

new Calendar(el, { locale: "en", firstDay: 1 }).init();
// force Monday despite en

Update at runtime:

import Calendar, { getWeekStart } from "@19h47/calendar";

calendar.options.locale = "ja";
calendar.options.firstDay = getWeekStart("ja");
calendar.render();

Custom labels

new Calendar(el, {
	locale: "fr",
	days: ["Dim", "Lun", "Mar", "Mer", "Jeu", "Ven", "Sam"],
	months: [
		"Janvier", "Février", "Mars", "Avril", "Mai", "Juin",
		"Juillet", "Août", "Septembre", "Octobre", "Novembre", "Décembre",
	],
}).init();

Cell hook

Override renderInner to enrich or rebuild each day cell. Keep .js-day and data-day.

calendar.renderInner = (inner, date) => {
	const button = inner.querySelector(".js-day");
	if (!button) return;

	button.classList.add("MyDay");
	button.innerHTML = `<time datetime="${date.toISOString().slice(0, 10)}">${date.getDate()}</time>`;
};

Pair with custom stateClasses / header markup — see the Custom markup demo in index.html.

Options

| Option | Type | Default | Description | | ------ | ---- | ------- | ----------- | | locale | string | document.documentElement.lang or "en" | BCP 47 locale for title and weekday/month labels | | buttonClass | string | "" | Extra classes on day buttons (e.g. Bootstrap) | | months | string[] | — | Month names override (Jan→Dec). Defaults to Intl | | days | string[] | — | Weekday labels override (Sun→Sat). Defaults to Intl | | single | boolean | true | Single date vs range | | firstDay | number | from locale | Week start (0=Sun … 6=Sat) | | deselect | boolean | false | Allow clearing the selection in single mode | | allowPast | boolean | false | Allow selecting past dates | | name | string | — | Forwarded in Calendar.change detail | | stateClasses | object | see source | Selection classes (active, range, start, end) |

Exported types: Options, StateClasses, Current. Also exported: getWeekStart, toDayString, fromDayString.

Attributes

| Attribute | Description | | --------- | ----------- | | data-month | Initial month (011) | | data-year | Initial year | | data-picked-dates | JSON array of YYYY-MM-DD days to preselect, e.g. ["2023-11-05","2023-11-07"] |

Accessibility

The calendar grid follows the WAI-ARIA APG date grid practices.

In your markup (source of truth): role="grid", aria-labelledby, aria-live on the title, aria-label on nav buttons, etc.

Generated by the lib:

  • Roving tabindex (one focusable day at a time)
  • aria-selected on selected days
  • aria-disabled on unavailable days (still in the keyboard grid)
  • aria-current="date" on today
  • Weekday headers with abbr (full day name)

Keyboard (focus in the grid)

| Key | Action | | --- | --- | | Arrow keys | Move by day / week (crosses months) | | Home / End | First / last day of the week | | Page Up / Page Down | Previous / next month (same day number, or last day of month) | | Shift + Page Up / Down | Previous / next year | | Enter / Space | Select the focused day |

Events

Calendar.change — fired on selection change.

detail: {
	values: string[]; // `YYYY-MM-DD`
	name?: string;
}

Live demos

  • GitHub Pages (from docs/)
  • Local: pnpm dev then open the app (see index.html)

Refresh the Pages demo after lib changes:

pnpm run docs

Acknowledgments