An open API service indexing awesome lists of open source software.

https://github.com/devints47/cal7

A zero-configuration React calendar component for displaying Google Calendar events in a responsive weekly view.
https://github.com/devints47/cal7

calendar calendar-api calendar-component calendar-widget google-calendar google-calendar-api google-calendar-integration nextjs react

Last synced: 8 months ago
JSON representation

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

Awesome Lists containing this project

README

          

# cal7

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

## Installation

```bash
npm install cal7
```

## Quick Start

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

// Set environment variable: GOOGLE_CALENDAR_API_KEY=your_api_key

export default function MyApp() {
return ;
}
```
## 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
```tsx
// 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 ;
}
```

2) Client: render the calendar
```tsx
// components/EventsCalendarClient.tsx ("use client")
"use client";

import { CalendarClient } from "cal7";

export default function EventsCalendarClient({ events }: { events: any[] }) {
return (

);
}
```

3) Theming in a few lines (optional)
```tsx
// 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 (



);
}
```

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.

```tsx
// app/layout.tsx
import "cal7/styles";
export default function RootLayout({ children }: { children: React.ReactNode }) {
return {children};
}
```

---

## 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):
```tsx

```

Example (explicit portal to body for add‑to‑calendar in a modal):
```tsx
import { AddToCalendarButton } from "cal7";

```

---

## 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:
```css
: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)

```tsx
import { Calendar } from "cal7";

export default function EventsPage() {
return ;
}
```

### With Theme Provider

```tsx
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 (



);
}
```

## Theming & Customization

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

### Theme Configuration

```tsx
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 (



);
}
```

### Dark Mode

```tsx
import { ThemeProvider } from "cal7";

export default function App() {
return (



);
}
```

### CSS Custom Properties

You can also customize the calendar using CSS custom properties:

```css
: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

```tsx
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` | `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:

```bash
# .env.local
GOOGLE_CALENDAR_API_KEY=your_api_key_here
```

## TypeScript Support

Cal7 is built with TypeScript and provides full type definitions:

```tsx
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