Skip to content
/ cal7 Public

A zero-configuration React calendar component for displaying Google Calendar events in a responsive weekly view.

Notifications You must be signed in to change notification settings

devints47/cal7

Repository files navigation

cal7

A zero-configuration React calendar component for displaying Google Calendar events in a responsive weekly view.

Installation

npm install cal7

Quick Start

import { Calendar } from "cal7";
import "cal7/styles";

// Set environment variable: GOOGLE_CALENDAR_API_KEY=your_api_key

export default function MyApp() {
  return <Calendar showSubscribeButton={true} />;
}

Zero‑key ICS Quick Start (Recommended)

Use a public Google Calendar ICS feed on the server, then render the client calendar with events. No Google API key required.

  1. Server: fetch the ICS and parse to events
// app/events/page.tsx (Next.js App Router, Server Component)
import { parseICS } from "cal7";
import "cal7/styles"; // one CSS import

async function getEventsFromICS() {
  // Replace with your public Google Calendar ICS URL
  const ICS_URL = "https://calendar.google.com/calendar/ical/your_calendar_id%40group.calendar.google.com/public/basic.ics";
  const res = await fetch(ICS_URL, { next: { revalidate: 300 } }); // cache 5min
  if (!res.ok) throw new Error("Failed to fetch ICS");
  const icsText = await res.text();
  return parseICS(icsText); // returns CalendarEvent[]
}

export default async function EventsPage() {
  const events = await getEventsFromICS();
  // Pass serializable events to the client
  return <EventsCalendarClient events={events} />;
}
  1. Client: render the calendar
// components/EventsCalendarClient.tsx ("use client")
"use client";

import { CalendarClient } from "cal7";

export default function EventsCalendarClient({ events }: { events: any[] }) {
  return (
    <CalendarClient
      events={events}
      locale="en-US"
      timeZone="America/New_York"
      calendarName="Our Events"
      showSubscribeButton
      titleAlignment="center"
    />
  );
}
  1. Theming in a few lines (optional)
// app/events/page.tsx (wrap client render)
import { ThemeProvider, type CalendarTheme } from "cal7";
import "cal7/styles";

const theme: CalendarTheme = {
  colors: {
    primary: "#059669",         // brand green
    today: "#059669",
    todayBackground: "#D1FAE5",
    eventBorder: "#A7F3D0",
  },
};

export default async function EventsPage() {
  const events = await getEventsFromICS();
  return (
    <ThemeProvider config={{ theme }}>
      <EventsCalendarClient events={events} />
    </ThemeProvider>
  );
}

Notes:

  • parseICS focuses on VEVENT blocks, DATE vs DATETIME, Z vs local, and all‑day detection. Recurrence should be pre‑expanded by your provider (Google does this in ICS).
  • For private calendars or advanced features (attendees, write access), use the API‑key flow described below.

Next.js App Router Recipe (Client + Server)

  • Server components: fetch/parse ICS or fetch via the Google API if you need private calendars.
  • Client component: render with the normalized events.
  • Styles: import "cal7/styles" once (global or server layout), or rely on ThemeProvider tokens to map to CSS variables.
// app/layout.tsx
import "cal7/styles";
export default function RootLayout({ children }: { children: React.ReactNode }) {
  return <html><body>{children}</body></html>;
}

Placement and Portals (dropdowns, menus)

Dropdown menus can render via a fixed‑position portal to avoid clipping inside modals or containers.

Props (1.2.0):

  • AddToCalendarButton, SubscribeButton:
    • portalTarget?: HTMLElement | string; defaults to document.body when provided
    • dropdownPlacement?: "up" | "down" | "auto" (auto chooses based on available space)
    • offset?: number (px), default 8

Example (subscribe opens upward by default):

<CalendarClient
  events={events}
  showSubscribeButton
/>

Example (explicit portal to body for add‑to‑calendar in a modal):

import { AddToCalendarButton } from "cal7";

<AddToCalendarButton
  event={event}
  portalTarget="body"
  dropdownPlacement="auto"
  offset={10}
/>

Theming Tokens Reference (excerpt)

Cal7 maps theme tokens to CSS variables. You can set tokens via ThemeProvider or override CSS vars globally.

Colors (examples):

  • primary, secondary, background, surface, text, textSecondary, textMuted
  • border, borderLight
  • today, todayBackground
  • eventBackground, eventBorder, eventText
  • headerEven, headerOdd, dayEven, dayOdd, dayHover

Other:

  • typography: fontFamily, fontSize.{xs|sm|base|lg|xl|2xl}, fontWeight.{normal|medium|semibold|bold}
  • spacing: { xs, sm, md, lg, xl, 2xl }
  • borderRadius: { none, sm, md, lg, full }
  • shadows: { none, sm, md, lg, xl }
  • zIndex: { dropdown, modal, tooltip }

CSS example:

:root {
  --cal7-color-primary: #059669;
  --cal7-color-today: #059669;
  --cal7-color-today-background: #D1FAE5;
  --cal7-color-event-border: #A7F3D0;
}

Accessibility

  • Roles: dialog, menu, menuitem with aria‑expanded/aria‑controls bound to triggers
  • Modal focus loop (dependency‑free) with Escape and outside‑click dismiss
  • Keyboard navigation: arrows in menus, Tab cycle in modal
  • Respects prefers‑reduced‑motion and high‑contrast

Features

  • 🚀 Zero Configuration: Just set your API key and go
  • 📱 Responsive Design: Adapts from desktop 7-day view to mobile-friendly layout
  • ♿ Accessibility First: Full keyboard navigation and screen reader support
  • 🎨 Customizable Theming: Extensive theme system with CSS custom properties
  • 🌙 Dark Mode: Built-in dark theme with system preference detection
  • đź“… Add to Calendar: Device-aware calendar integration (Google, Apple, iCal)
  • đź”’ Secure: Server-side API key handling, no client-side exposure
  • ⚡ Performance: Built-in caching and optimized rendering
  • 📦 Zero runtime dependencies by default; small bundle footprint
  • 🎯 TypeScript: Full type safety and IntelliSense support

Screenshot 2025-07-26 at 11 32 54 AM

Framework Compatibility

This package is designed specifically for React-based frameworks with the following support levels:

âś… Fully Supported

  • Next.js 13+ (App Router) - Primary target with full server-side rendering support
  • Next.js 12+ (Pages Router) - Full compatibility with getServerSideProps/getStaticProps

⚠️ Partial Support

  • Create React App / Vite - Client-side only, requires custom fetcher prop for API calls
  • Remix - May work with adaptation, but not officially tested

❌ Not Compatible

  • Vue/Nuxt, Svelte/SvelteKit, Angular - Different framework ecosystems, would need separate packages

Recommended: Use with Next.js for the best experience with server-side rendering and secure API key handling.

Basic Usage

Server Component (Recommended)

import { Calendar } from "cal7";

export default function EventsPage() {
  return <Calendar locale="en-US" timeZone="America/New_York" />;
}

With Theme Provider

import { Calendar, ThemeProvider } from "cal7";

const customTheme = {
  colors: {
    primary: "#3b82f6",
    background: "#ffffff",
    eventBackground: "#f8fafc",
  },
  typography: {
    fontFamily: "Inter, sans-serif",
    customFontUrl:
      "https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap",
  },
};

export default function App() {
  return (
    <ThemeProvider config={{ theme: customTheme }}>
      <Calendar />
    </ThemeProvider>
  );
}

Theming & Customization

Cal7 provides extensive theming capabilities through CSS custom properties and a comprehensive theme system.

Theme Configuration

import { Calendar, ThemeProvider, type CalendarTheme } from "cal7";

const customTheme: CalendarTheme = {
  colors: {
    // Primary colors
    primary: "#3b82f6",
    secondary: "#6b7280",
    background: "#ffffff",
    surface: "#f9fafb",

    // Text colors
    text: "#111827",
    textSecondary: "#374151",
    textMuted: "#6b7280",
    eventPastText: "#9ca3af", // Color for past events

    // Border colors
    border: "#e5e7eb",
    borderLight: "#f3f4f6",

    // State colors
    today: "#92400e",
    todayBackground: "#fef3c7",
    focus: "#3b82f6",
    hover: "#f3f4f6",

    // Day alternating colors
    dayEven: "#fafafa",
    dayOdd: "#ffffff",
    dayHover: "#f8fafc",

    // Header alternating colors
    headerEven: "#f3f4f6",
    headerOdd: "#f9fafb",

    // Event colors
    eventBackground: "#ffffff",
    eventBorder: "#e5e7eb",
    eventText: "#111827",
  },

  typography: {
    fontFamily: "Inter, -apple-system, BlinkMacSystemFont, sans-serif",
    customFontUrl:
      "https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap",
    fontSize: {
      xs: "0.75rem",
      sm: "0.875rem",
      base: "1rem",
      lg: "1.125rem",
      xl: "1.25rem",
      "2xl": "1.5rem",
    },
    fontWeight: {
      normal: 400,
      medium: 500,
      semibold: 600,
      bold: 700,
    },
  },

  spacing: {
    xs: "0.25rem",
    sm: "0.5rem",
    md: "1rem",
    lg: "1.5rem",
    xl: "2rem",
    "2xl": "3rem",
  },

  shadows: {
    calendar:
      "0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06)",
    sm: "0 1px 2px rgba(0, 0, 0, 0.05)",
    md: "0 2px 4px rgba(0, 0, 0, 0.1)",
    lg: "0 4px 6px rgba(0, 0, 0, 0.1)",
    xl: "0 10px 15px rgba(0, 0, 0, 0.1)",
  },

  event: {
    height: "auto", // Customizable event section height
    width: "100%", // Customizable event width
    titleMinHeight: "2.5rem", // Minimum height for event titles (2 lines)
  },
};

export default function App() {
  return (
    <ThemeProvider config={{ theme: customTheme }}>
      <Calendar />
    </ThemeProvider>
  );
}

Dark Mode

import { ThemeProvider } from "cal7";

export default function App() {
  return (
    <ThemeProvider
      config={{
        mode: "dark", // 'light' | 'dark' | 'system'
        theme: customLightTheme,
        darkTheme: customDarkTheme,
      }}
    >
      <Calendar />
    </ThemeProvider>
  );
}

CSS Custom Properties

You can also customize the calendar using CSS custom properties:

:root {
  /* Colors */
  --cal7-color-primary: #3b82f6;
  --cal7-color-background: #ffffff;
  --cal7-color-surface: #f9fafb;
  --cal7-color-text: #111827;
  --cal7-color-border: #e5e7eb;

  /* Day alternating colors */
  --cal7-color-day-even: #fafafa;
  --cal7-color-day-odd: #ffffff;
  --cal7-color-day-hover: #f8fafc;

  /* Header alternating colors */
  --cal7-color-header-even: #f3f4f6;
  --cal7-color-header-odd: #f9fafb;

  /* Past events */
  --cal7-color-event-past-text: #9ca3af;

  /* Typography */
  --cal7-font-family: "Inter", sans-serif;
  --cal7-font-size-base: 1rem;
  --cal7-font-weight-bold: 700;

  /* Event customization */
  --cal7-event-height: auto;
  --cal7-event-width: 100%;
  --cal7-event-title-min-height: 2.5rem;

  /* Shadows */
  --cal7-shadow-calendar: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);

  /* Spacing */
  --cal7-spacing-sm: 0.5rem;
  --cal7-spacing-md: 1rem;
  --cal7-spacing-lg: 1.5rem;
}

Custom Font Integration

const themeWithCustomFont = {
  typography: {
    fontFamily: "Poppins, sans-serif",
    customFontUrl:
      "https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600;700&display=swap",
  },
};

Component Props

Calendar

Prop Type Default Description
locale string 'en-US' Locale for date/time formatting
timeZone string 'UTC' Timezone for event display
revalidate number 300 Cache revalidation time in seconds
className string '' Additional CSS classes
theme CalendarTheme undefined Custom theme configuration
darkTheme CalendarTheme undefined Custom dark theme configuration
mode ThemeMode 'light' Theme mode: 'light', 'dark', or 'system'
classPrefix string 'cal7' CSS class prefix for custom styling
showSubscribeButton boolean false Show "Subscribe to Calendar!" button with .ics download
fetcher () => Promise<Event[]> undefined Custom data fetcher function
onError (error: Error) => void undefined Error handler callback

ThemeProvider

Prop Type Default Description
config.theme CalendarTheme defaultTheme Light theme configuration
config.darkTheme CalendarTheme defaultDarkTheme Dark theme configuration
config.mode 'light' | 'dark' | 'system' 'light' Theme mode
config.classPrefix string 'cal7' CSS class prefix

Styling Features

Enhanced Visual Design

  • Drop Shadow: Entire calendar component has a subtle drop shadow
  • Alternating Colors: Days and headers alternate background colors for better visual separation
  • Smooth Animations: Hover effects with scale and smooth transitions
  • Past Event Styling: Past events are automatically greyed out using theme-appropriate colors

Event Card Enhancements

  • Clock Icons: Time displays include clock icons
  • Start/End Times: Shows both start and end times (e.g., "8:00AM to 10:00PM")
  • Bold Titles: Event titles are bold and have increased font size
  • Minimum Height: Event titles have a minimum 2-line height by default
  • Location Icons: SVG location icons instead of emoji pins
  • Customizable Dimensions: Event height and width can be customized via theme

Interactive Features

  • Clickable Links: Email addresses and URLs in event descriptions are automatically clickable
  • Map Integration: Event locations are clickable and open in device-appropriate map apps
  • Add to Calendar: Event modal dates are clickable with add-to-calendar functionality
  • Themed Buttons: Add-to-calendar buttons match the selected theme (no gradients)

Subscribe to Calendar Button

When enabled with showSubscribeButton={true}, displays a prominent "Subscribe to Calendar!" button that provides:

  • Full Calendar Subscription: Users can subscribe to the entire calendar feed via URL
  • Download .ics File: Users can download all calendar events as a local .ics file
  • Upward-Opening Menu: Dropdown menu opens upward to avoid being cut off
  • Theme Integration: Button styling matches your current theme with orange gradient
  • Desktop/Mobile Friendly: Works across all devices and calendar applications

Navigation Improvements

  • Current Week Indicator: Shows "Current Week" badge when viewing the current week
  • Themed Borders: Themed border between week selector and calendar grid
  • Date Format: Week selector shows dates in "Jul 20 - 26, 2025" format
  • Button Layout: Navigation arrows positioned left and right of "Today" button

Environment Setup

Set your Google Calendar API key as an environment variable:

# .env.local
GOOGLE_CALENDAR_API_KEY=your_api_key_here

TypeScript Support

Cal7 is built with TypeScript and provides full type definitions:

import type { CalendarTheme, CalendarEvent, ThemeConfig } from "cal7";

const theme: CalendarTheme = {
  colors: {
    primary: "#3b82f6",
    // ... other theme properties with full IntelliSense
  },
};

Accessibility

  • Keyboard Navigation: Full keyboard support with arrow keys, Enter, Space, and Escape
  • Screen Reader Support: Comprehensive ARIA labels and live region announcements
  • Focus Management: Proper focus trapping in modals and logical tab order
  • High Contrast: Supports high contrast mode preferences
  • Reduced Motion: Respects user's reduced motion preferences

Browser Support

  • Chrome 90+
  • Firefox 88+
  • Safari 14+
  • Edge 90+

License

MIT