# Button und ks:subscribe

Der Button ist der einfachste Weg, Besucher deiner Website zu Abonnenten zu machen. Er braucht keinen API-Key, nur die Kalender-ID.

```html
<div class="ks-subscribe" data-calendar="cal_XXXXXXXXXXXX"></div>
<script src="https://app.kalender-sync.de/embed/v1/button.js" async></script>
```

<SubscribeButtonPreview />

## Attribute

| Attribut | Wirkung | Standard |
|---|---|---|
| `data-calendar` | Kalender-ID (`cal_` plus 12 Zeichen). Pflicht; ohne gültige ID rendert nichts. | |
| `data-color` | Hex-Farbe `#rrggbb`. Die Textfarbe wählt der Button selbst für ausreichend Kontrast. | `#2563eb` |
| `data-label` | Eigener Button-Text | „Kalender abonnieren“ / „Subscribe to calendar“ |
| `data-lang` | `de` oder `en` | `lang` der Seite, dann Browsersprache, sonst Englisch |

Das Script brauchst du einmal pro Seite. Es rendert jedes Element mit der Klasse `ks-subscribe` und einem `data-calendar`.

## Wie der Button funktioniert

- Er rendert in ein **Shadow DOM**. Das CSS deiner Seite greift nicht hinein, und sein CSS nicht hinaus.
- Er setzt **keine Cookies**, speichert nichts und lädt nichts außer dem Script selbst.
- Die Menüeinträge sind normale Links auf `https://app.kalender-sync.de/api/public/subscribe/{calendar_id}/go/{app}`. Erst dieser Aufruf legt das Abo an und leitet zur Kalender-App weiter. Deine Seite schickt also keine Anfragen per JavaScript an uns, und du brauchst kein CORS.
- Apps im Menü: Apple Kalender, Google Kalender, Microsoft 365, Outlook.com, Outlook (Desktop) und „Andere App“, das die Abo-Seite mit dem Feed-Link öffnet. Die passende App für das Gerät steht oben.
- Escape schließt das Menü, ein Klick daneben auch.

## Das Event `ks:subscribe`

Wir tracken keine Besucher. Damit du trotzdem weißt, wie dein Button genutzt wird, feuert er bei jedem Klick auf einen Menüeintrag das Event **`ks:subscribe`**. Es bubbelt aus dem Shadow DOM heraus (`composed: true`), du kannst es also am Element oder am `document` abgreifen.

```js
document.addEventListener('ks:subscribe', (event) => {
  // event.detail = { calendar: 'cal_XXXXXXXXXXXX', app: 'google' }
  const { calendar, app } = event.detail;

  // Beispiel: an dein eigenes Analytics weitergeben, unter deiner Einwilligung
  window.plausible?.('Calendar subscribe', { props: { calendar, app } });
});
```

`app` ist einer dieser Werte: `apple`, `google`, `office365`, `outlook`, `outlook_desktop`, `other`.

Das Event läuft komplett auf deiner Seite. Ob und wie du es auswertest, entscheidest du, ebenso über die Einwilligung dafür.

## Seiten, die später rendern

Page-Builder (Elementor, Divi …) und Single-Page-Apps (React, Vue …) fügen Inhalte oft erst ein, nachdem das Script gelaufen ist. Ruf dann nach dem Einfügen auf:

```js
window.KalenderSync?.mountButtons();
```

Bereits gerenderte Buttons bleiben dabei unverändert. Willst du einen Button mit anderen Attributen neu rendern, ersetze das Element durch ein neues und ruf `mountButtons()` erneut auf.

Beispiel für React:

```jsx
useEffect(() => {
  window.KalenderSync?.mountButtons();
}, []);

return <div className="ks-subscribe" data-calendar="cal_XXXXXXXXXXXX" />;
```

## Eigene Buttons statt des Scripts

Willst du kein Script einbinden, verlinke die `subscribe_page_url` des Kalenders mit einem eigenen Button. Abos über die Abo-Seite werden genauso gezählt.

Die `subscribe_links` aus der [Kalender-Antwort](https://docs.kalender-sync.de/api/calendars/#die-antwort) funktionieren auch, werden aber in der [Statistik](https://docs.kalender-sync.de/api/stats/) nicht gezählt.

## Content-Security-Policy

Setzt deine Website eine CSP, ergänze in den bestehenden Direktiven:

```
script-src https://app.kalender-sync.de;
style-src 'unsafe-inline';
img-src data:;
```

`style-src` und `img-src` brauchst du nur, wenn deine CSP diese Direktiven überhaupt einschränkt: Der Button bringt Inline-Styles im Shadow DOM mit, und das Microsoft-Icon ist als `data:`-Bild eingebettet. `connect-src` und `frame-src` sind nicht nötig.