Web component
The twist-cal custom element — all attributes, calendar selection, JSON event data, all-day events, and the twistcal:add callback.
Drop <twist-cal> on the page. It renders a button with a dropdown menu. All styles live in a Shadow DOM root — no bleed in either direction.
<script defer src="https://cdn.jsdelivr.net/npm/@freshjuice/twistcal/dist/twistcal.min.js"></script>
<twist-cal
title="Product Launch Webinar"
start="2026-02-10T14:00:00"
end="2026-02-10T15:30:00"
location="Online"
description="Join us for the launch of TwistCal v1.">
</twist-cal>
Attributes
| Attribute | Required | Description |
|---|---|---|
title |
yes | Event title (calendar subject) |
start |
yes | Start datetime — any value new Date() understands |
end |
yes | End datetime |
description |
no | Event description (calendar body) |
location |
no | Event location |
url |
no | Event URL (included in .ics) |
timezone |
no | IANA timezone (e.g. Europe/Oslo). Naive start/end are interpreted as wall-clock in that zone, converted to UTC. DST-aware. Ignored if start/end already carry an offset. See Datetime. |
label |
no | Button text. Default: auto-detected from lang or Add to Calendar |
lang |
no | Language code. Auto-detected from <html lang> or navigator.language if omitted. See i18n. |
variant |
no | solid (default) or outline (white bg, dark border, dark text) |
show-icons |
no | true (default) or false — show/hide brand icons in dropdown |
show-icon |
no | true (default) or false — show/hide the calendar icon on the button |
calendars |
no | Comma-separated list of calendars to show, in order: google,outlook,yahoo,ics. Default: all four. ics and ical both work. Alias: services. |
all-day |
no | true or false (default). Date-only .ics and Google/Yahoo formats. |
branding |
no | true (default) or false — show/hide “Powered by TwistCal” link in dropdown |
Calendar selection
Show only the calendars you want, in the order you want:
<!-- Google + Apple Calendar only -->
<twist-cal calendars="google,ics" title="..." start="..." end="...">
</twist-cal>
<!-- Single calendar — effectively a download button -->
<twist-cal calendars="ics" label="Download .ics" title="..." start="..." end="...">
</twist-cal>
Rich data via JSON
For long descriptions or structured data, pass the event as JSON inside the element:
<twist-cal label="Add to my calendar">
<script type="application/json">
{
"title": "Quarterly Review — Q1 2026",
"start": "2026-03-15T09:00:00",
"end": "2026-03-15T10:00:00",
"location": "Conference Room B, 4th Floor",
"description": "Bring your Q1 metrics.\nDial-in: +1-555-0100, code 4521#",
"url": "https://twistcal.com/demo"
}
</script>
</twist-cal>
JSON overrides matching attributes. Attributes not in the JSON are still read from the element.
All-day events
Set all-day="true" for date-only formats:
<twist-cal
all-day="true"
title="Company offsite"
start="2026-04-20"
end="2026-04-22">
</twist-cal>
Produces date-only .ics (VALUE:DATE) and Google/Yahoo all-day URLs.
twistcal:add callback
Each click dispatches a twistcal:add CustomEvent on the <twist-cal> element with { action, event } detail:
<twist-cal id="cal" title="..." start="..." end="..."></twist-cal>
<script>
document.getElementById('cal').addEventListener('twistcal:add', (e) => {
console.log('Added to:', e.detail.action, e.detail.event);
});
</script>
action is google, outlook, yahoo, ics, or a custom calendar id. The event also fires on document for declarative triggers.
Next
- Styling — CSS custom properties and theming
- Declarative triggers — use your own buttons
- API — programmatic access
