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

kalendly

v0.2.0

Published

A universal calendar web component — works in React, Vue, Angular, svelte, and plain HTML with no framework dependency

Readme

kalendly

A universal calendar web component — works in React, Vue, Svelte, Angular, Solid.js, and plain HTML with no framework dependency.

Features

  • Framework-agnostic: Single <kal-calendar> custom element, no framework required
  • Responsive: Mobile-friendly, matches your existing UI
  • Themeable: CSS variables + JS property API
  • Type Safe: Full TypeScript support
  • Event-rich: Categories, priorities, time ranges, attendees, and more
  • Accessible: Built with accessibility in mind
  • Tree-shakeable: Import only what you need

Live Demo

Kalendly Calendar Demo

Try the Interactive Demo →

Installation

npm install kalendly

Usage

Vanilla HTML / CDN

<link
  rel="stylesheet"
  href="https://unpkg.com/kalendly/dist/styles/calendar.css"
/>
<script src="https://unpkg.com/kalendly/dist/index.umd.js"></script>

<kal-calendar
  id="cal"
  title="My Calendar"
  initial-date="2025-01-15"
></kal-calendar>

<script>
  const cal = document.getElementById('cal');

  // Set events (JS property — not an attribute)
  cal.events = [
    { id: 1, name: 'Team Meeting', date: new Date(2025, 0, 15) },
    { id: 2, name: 'Project Deadline', date: new Date(2025, 0, 20) },
  ];

  // Listen to custom events
  cal.addEventListener('cal-date-select', e => {
    console.log('Selected:', e.detail.date, e.detail.events);
  });

  cal.addEventListener('cal-month-change', e => {
    console.log('Month:', e.detail.year, e.detail.month);
  });
</script>

ES Modules

import 'kalendly';
import 'kalendly/styles';

// <kal-calendar> is now registered and ready

React 19

React 19 has full custom element support — pass objects/arrays as props and listen to custom events directly.

import 'kalendly';
import 'kalendly/styles';

function App() {
  return (
    <kal-calendar
      title="My Calendar"
      events={events}
      oncal-date-select={e => console.log(e.detail.date)}
      oncal-month-change={e => console.log(e.detail.year, e.detail.month)}
    />
  );
}

React 18 users: React 18 does not forward object/array props or custom events to custom elements. You need to wire these up via a ref:

import { useRef, useEffect } from 'react';
import 'kalendly';

function Calendar({ events, onDateSelect, onMonthChange, ...attrs }) {
  const ref = useRef(null);

  useEffect(() => {
    if (ref.current) ref.current.events = events;
  }, [events]);

  useEffect(() => {
    const el = ref.current;
    if (!el) return;
    const onSelect = e => onDateSelect?.(e.detail.date, e.detail.events);
    const onChange = e => onMonthChange?.(e.detail.year, e.detail.month);
    el.addEventListener('cal-date-select', onSelect);
    el.addEventListener('cal-month-change', onChange);
    return () => {
      el.removeEventListener('cal-date-select', onSelect);
      el.removeEventListener('cal-month-change', onChange);
    };
  }, [onDateSelect, onMonthChange]);

  return <kal-calendar ref={ref} {...attrs} />;
}

Vue 3

Vue 3 supports custom elements natively — bind props with : and listen to events with @:

<template>
  <kal-calendar
    title="My Calendar"
    :events="events"
    @cal-date-select="onDateSelect"
    @cal-month-change="onMonthChange"
  />
</template>

<script setup>
import 'kalendly';
import 'kalendly/styles';

const events = [{ id: 1, name: 'Team Meeting', date: new Date(2025, 0, 15) }];

function onDateSelect(e) {
  console.log('Selected:', e.detail.date);
}

function onMonthChange(e) {
  console.log('Month:', e.detail.year, e.detail.month);
}
</script>

Angular

// app.config.ts
import { ApplicationConfig } from '@angular/core';
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';

// app.component.ts
import 'kalendly';
import 'kalendly/styles';
<!-- app.component.html -->
<kal-calendar
  title="My Calendar"
  [events]="events"
  (cal-date-select)="onDateSelect($event)"
  (cal-month-change)="onMonthChange($event)"
></kal-calendar>

Svelte 5

<script>
  import 'kalendly';
  import 'kalendly/styles';
  let { events = [] } = $props();
</script>

<kal-calendar {events} oncal-date-select={e => console.log(e.detail.date)} />

Svelte 4

<script>
  import { onMount } from 'svelte';
  import 'kalendly';
  import 'kalendly/styles';
  export let events = [];
  let calEl;
  onMount(() => { calEl.events = events; });
  $: if (calEl) calEl.events = events;
</script>

<kal-calendar bind:this={calEl} on:cal-date-select on:cal-month-change />

Solid.js

import 'kalendly';
import 'kalendly/styles';

function App() {
  return (
    <kal-calendar
      title="My Calendar"
      prop:events={events}
      on:cal-date-select={e => console.log(e.detail.date)}
    />
  );
}

Migration from v0.1.x

v0.2.0 replaces the four separate framework packages with a single web component.

| Before (v0.1.x) | After (v0.2.0+) | | --------------------------------------------------- | ---------------------------------- | | import { Calendar } from 'kalendly/react' | import 'kalendly' | | import { Calendar } from 'kalendly/vue' | import 'kalendly' | | import { Calendar } from 'kalendly/react-native' | Not supported | | import { createCalendar } from 'kalendly/vanilla' | import 'kalendly' | | <Calendar events={events} /> | <kal-calendar events={events} /> |

React Native is out of scope and not replaced.

Styling

Loading styles

// Bundler (Vite, webpack) — add once in your app entry (e.g. main.tsx)
import 'kalendly/styles';

// Plain HTML
// <link rel="stylesheet" href="/node_modules/kalendly/dist/styles/calendar.css">

// Angular — add to angular.json → projects → architect → build → styles
// "node_modules/kalendly/dist/styles/calendar.css"

Overriding styles

kalendly uses Light DOM — all standard CSS techniques work:

/* 1. CSS custom properties (recommended) */
:root {
  --calendar-primary-color: #6366f1;
  --calendar-background: #1e1e2e;
  --calendar-border-color: #334155;
}

/* 2. Direct class overrides */
.kalendly-calendar .calendar--card {
  border-radius: 12px;
}
// 3. JS theme property
document.querySelector('kal-calendar').theme = {
  primary: '#6366f1',
  background: '#1e1e2e',
};

Attributes

Primitives are set as HTML attributes:

| Attribute | Type | Default | Description | | ----------------------- | ---------- | ---------------- | ------------------------------------- | | title | string | — | Calendar title | | initial-date | string | today | ISO date string for initial view | | min-year | string | currentYear - 30 | Minimum year in picker | | max-year | string | currentYear + 10 | Maximum year in picker | | week-starts-on | "0"\|"1" | "0" | Week start: 0 = Sunday, 1 = Monday | | use-short-month-names | string | — | Present = use abbreviated month names |

Properties

Rich objects are set as JS properties (not attributes):

| Property | Type | Description | | ---------------- | ---------------------------------- | -------------------------------- | | events | CalendarEvent[] | Events to display | | theme | CalendarTheme | Custom theme colors | | categoryColors | CategoryColorMap | Per-category color overrides | | renderEvent | (event: CalendarEvent) => string | Custom event HTML renderer | | renderNoEvents | () => string | Custom empty-state HTML renderer |

Custom Events

| Event | detail shape | Description | | ------------------ | ----------------------------------------- | ------------------------- | | cal-date-select | { date: Date, events: CalendarEvent[] } | User clicked a date | | cal-month-change | { year: number, month: number } | Month navigation occurred |

Both events bubble and are composed (cross Shadow DOM boundaries).

JavaScript API

const cal = document.querySelector('kal-calendar');

cal.updateEvents(newEvents); // Re-render with new events
cal.updateTheme(newTheme); // Apply new theme
cal.goToDate(new Date(2025, 5, 1)); // Navigate to date
cal.getCurrentDate(); // Returns currently selected Date
cal.getEngine(); // Access CalendarEngine directly

CalendarEvent Interface

interface CalendarEvent {
  id: string | number;
  name: string;
  date: string | Date;

  startTime?: string; // e.g. "09:00"
  endTime?: string; // e.g. "10:00"
  allDay?: boolean;

  description?: string;
  color?: string;
  category?:
    | 'work'
    | 'personal'
    | 'meeting'
    | 'deadline'
    | 'appointment'
    | 'other';
  location?: string;
  url?: string;

  status?: 'scheduled' | 'completed' | 'cancelled' | 'tentative';
  priority?: 'low' | 'medium' | 'high';

  attendees?: string[];
  organizer?: string;
  reminders?: number[]; // minutes before event
  recurring?: {
    frequency: 'daily' | 'weekly' | 'monthly' | 'yearly';
    interval?: number;
    endDate?: string | Date;
    daysOfWeek?: number[];
  };

  notes?: string;
  tags?: string[];
  [key: string]: unknown;
}

Theming

All CSS variables

:root {
  --calendar-primary-color: #fc8917;
  --calendar-secondary-color: #fca045;
  --calendar-tertiary-color: #fdb873;
  --calendar-text-color: #2c3e50;
  --calendar-text-light: #6b7280;
  --calendar-border-color: #dee2e6;
  --calendar-today-outline: #f7db04;
  --calendar-event-indicator: #1890ff;
  --calendar-background: #fff;
  --calendar-cell-hover: #f3f4f6;
  --calendar-selected-bg: #eff6ff;
}

JS theme property (full reference)

cal.theme = {
  primary: '#3b82f6',
  secondary: '#60a5fa',
  tertiary: '#93c5fd',
  textColor: '#111827',
  textLight: '#6b7280',
  background: '#ffffff',
  cellHover: '#f3f4f6',
  borderColor: '#e5e7eb',
  todayOutline: '#fbbf24',
  selectedBg: '#eff6ff',
  eventIndicator: '#10b981',
};

Dark theme example

cal.theme = {
  primary: '#6366f1',
  secondary: '#818cf8',
  textColor: '#f9fafb',
  textLight: '#d1d5db',
  background: '#1f2937',
  cellHover: '#374151',
  borderColor: '#4b5563',
  todayOutline: '#fbbf24',
  selectedBg: '#312e81',
  eventIndicator: '#34d399',
};

Core Engine (advanced)

import { CalendarEngine } from 'kalendly/core';

const engine = new CalendarEngine({ events, initialDate: new Date() });

const unsubscribe = engine.subscribe(() => {
  const viewModel = engine.getViewModel();
  // re-render
});

engine.getActions().next();
engine.getActions().previous();
engine.getActions().jump(2025, 5);
engine.getActions().goToToday();

unsubscribe();
engine.destroy();

Browser Support

Custom Elements v1 — Chrome 67+, Firefox 63+, Safari 12.1+, Edge 79+.

Contributing

See CONTRIBUTING.md.

git clone https://github.com/callezenwaka/kalendly.git
cd kalendly
npm install
npm test
npm run dev:examples

License

MIT © Callis Ezenwaka

Changelog

See CHANGELOG.md.

Recent Updates

  • v0.2.0: Migrated to a single <kal-calendar> web component — works natively in React, Vue, Angular, and plain HTML with no framework dependency
  • v0.1.7: Vanilla calendar performance optimization with event delegation
  • v0.1.6: Navigation enhancements — Today button, month/year picker, optional title prop
  • v0.1.5: Universal theming system, TypeScript improvements
  • v0.1.0: Initial release with React, Vue, React Native, and Vanilla JavaScript support