Zum Hauptinhalt springen

anny Widget Agent Skill

Verfasst von Lucian Holtwiesche

---

name: anny-widget-integration

description: >

How to embed anny booking widgets and the AI chat assistant on a website. Use when integrating

anny widgets into a site, landing page, or web app. Covers script setup, widget types, seamless

layout embedding (panels in cards, no extra padding, overflow hidden, border radius),

full-page vs panel vs button vs AI chat widgets, fullscreen with nav-height for single-scroll

pages, height behavior, design tokens, login/cart placement in the top-right nav,

and asking for the correct resource/service/organization/plan/package slug before generating code.

---


# anny Widget Integration

## Quick Facts

- Widgets are **web components** (custom HTML elements).

- One `<script>` tag loads all widgets. Load it once per page.

- Widgets communicate via the iframe they contain — **no extra wrapper div or padding is needed**. Place the element directly where you want the content.

- Panels auto-resize to their content. Page widgets and maps support `fullscreen` or a fixed `height`.

- **Prefer `fullscreen="true"` + `nav-height` for full-page booking** so the page itself never scrolls — only the widget scrolls internally. This avoids the awkward double scrollbar (page scroll *and* widget scroll).

- **Put `a-login-button` and `a-cart-modal-button` in the top-right of the main navigation.** The cart should show icon + count indicator only (`show-icon="true"`, no label).

- `a-agent-chat` embeds the **anny AI chat assistant** — a launcher in the corner, an in-flow button, or an inline frame. It can drive the whole booking flow conversationally.

- UTMs and click IDs from the current page URL are forwarded automatically.

- Start in admin when possible. Admin-generated snippets are the safest source for widget type, slug wiring, and supported options.


## Ask First

Before generating integration code, make sure these inputs are known:

1. Which widget type is needed: page, panel, button, utility, or AI chat?

2. Which entity is being embedded: `resource`, `service`, `organization`, `plan`, or `package`?

3. What is the exact slug for that entity? (For `a-agent-chat`, the `organization` slug and an `assistant-id`.)

4. Is booking the main page content, part of an existing layout, or behind a CTA?

5. Are login and cart needed in the main nav (top-right)?


If the widget depends on an entity slug and the slug is missing, **ask the user for it before writing the final snippet**.

Use these prompts:

- "What is the exact resource slug?"

- "What is the exact service slug?"

- "What is the exact organization slug?"

- "What is the exact plan slug?"

- "What is the exact package slug?"


Do not guess slugs.

### When details are missing, ask for the admin snippet

If a required value isn't obvious — an `assistant-id` for `a-agent-chat`, an `idp-uuid` for SSO, or any slug — **ask the user to paste the widget code copied from the anny admin area** rather than guessing. Admin generates a ready-made snippet with the element, the correct slugs/ids, and supported options already wired.


Prompt:

- "Open this widget in the anny admin area, copy the generated embed code, and paste it here — I'll adapt it to your site."

Then keep the admin-provided slugs/ids verbatim and only adjust layout, theming, and placement around them.


## Integration Workflow

1. Ask for the missing slug if the widget is entity-based.

2. Choose the right widget type: page, panel, button, utility, or AI chat.

3. Load the widget script once.

4. Add the required entity slug attribute for that widget.

5. Place the widget directly into the page layout. For full-page booking, prefer `fullscreen="true"` + `nav-height` so only the widget scrolls.

6. If embedding inside a card, style the wrapper, not the widget.

7. Add `a-login-button` and `a-cart-modal-button` once in the top-right of the main nav when needed (cart = icon + count only).

8. Add design tokens as HTML attributes if brand customization is needed.

9. Add event listeners only if external tracking hooks such as GA4 are needed.


## Fast Decision Guide

| Need | Choose |

|---|---|

| Booking is the main content of the page | page widget (`fullscreen="true"` + `nav-height`) |

| Existing marketing/editorial page needs embedded booking flow | panel widget |

| Booking should open after a click | button widget |

| Shared auth or cart control in nav | utility widget (top-right) |

| Conversational / assisted booking, support-style helper | AI chat (`a-agent-chat`) |

Prefer admin-generated snippets when the user already has the widget configured in anny admin. Use manual HTML when building the integration directly in code or when custom layout control is needed.


## Preferred Defaults For New Websites

When building a new website from scratch, prefer these defaults unless the user asks for a different pattern:

1. Use page content plus a booking panel in the layout.

2. **For a dedicated booking page, use a fullscreen page widget with `nav-height` set to your header height.** This gives a single scroll *inside* the widget and none on the page — no double scrollbar.

3. **Put `a-login-button` and `a-cart-modal-button` in the top-right of the main navigation.** Render the cart as icon + count only (`show-icon="true"`, no `label`).

4. Use `a-subscription-button` for plans instead of embedding the plan page inline.

5. Use a fullscreen calendar page when calendar browsing is a primary use case.

6. Add `a-agent-chat` as a bottom-right launcher when the site benefits from assisted/conversational booking or a support helper.


| Scenario | Preferred pattern |

|---|---|

| Dedicated booking page | page widget with `fullscreen="true"` + `nav-height="64px"` (single in-widget scroll) |

| Landing page or marketing page with booking | content + `a-resource-booking-panel` or `a-service-booking-panel` |

| Auth + cart in site chrome | `a-login-button` + `a-cart-modal-button`, top-right of the main nav (cart = icon + count) |

| Plan / subscription sales | `a-subscription-button` |

| Calendar-first experience | `a-resource-calendar` or `a-organization-calendar` with `fullscreen="true"` + `nav-height` |

| Assisted / conversational booking | `a-agent-chat` launcher (`placement="bottom-right"`) |

Do not default every new site to a full embedded page widget *inside* a content layout. Either give booking its own fullscreen page (with `nav-height`), or embed a panel in the existing layout — pick based on whether booking is the whole page.


## Script

```html

```

Place once in `<head>` or before `</body>`. Do not load more than once per page.


## Widget Types

| Type | Elements | Use when |

|---|---|---|

| **Page widget** | `a-organization-page`, `a-resource-page`, `a-service-page`, `a-resource-calendar`, `a-resource-map`, `a-organization-map`, `a-organization-calendar`, `a-subscription-page`, `a-package-page`, `a-my-bookings` | Booking is the main content of the page or section |

| **Panel widget** | `a-resource-booking-panel`, `a-service-booking-panel` | You already have a page layout and want the booking flow embedded inside it |

| **Button widget** | `a-resource-button`, `a-service-button`, `a-organization-button`, `a-subscription-button`, `a-package-button`, `a-modal-button` | Booking opens on user click in a modal |

| **Utility** | `a-login-button`, `a-cart-modal-button` | Shared auth/cart controls — place top-right in the main nav |

| **AI chat** | `a-agent-chat` | Conversational / assisted booking and support — launcher, in-flow button, or inline frame |


## Minimal Page Skeleton

```html

<!DOCTYPE html>

<html lang="en">

<head>

<meta charset="UTF-8" />

<meta name="viewport" content="width=device-width, initial-scale=1.0" />

<title>Booking</title>

</head>

<body>

<!-- Main nav: brand on the left, links in the middle, auth + cart top-right -->

<header style="display: flex; align-items: center; gap: 16px; padding: 0 24px; height: 64px;">

<a href="/">Logo</a>

<nav style="flex: 1;"><!-- navigation links --></nav>

<a-login-button base-url="https://anny.co"></a-login-button>

<a-cart-modal-button base-url="https://anny.co" modal-layout="drawer" show-icon="true"></a-cart-modal-button>

</header>

<main>

<a-resource-booking-panel

base-url="https://anny.co"

resource="my-resource"

></a-resource-booking-panel>

</main>

</body>

</html>

```

For a **dedicated booking page** where booking is the whole page, swap the panel in `<main>` for a fullscreen page widget so only the widget scrolls:

```html

<main>

<a-resource-page

base-url="https://anny.co"

resource="my-resource"

fullscreen="true"

nav-height="64px"

></a-resource-page>

</main>

```

## Seamless Embedding Patterns

### Panel inside a card

Panels resize dynamically. Put them directly inside a card — no extra wrapper or padding needed. Apply `overflow: hidden` and `border-radius` to the card, not the widget element.

```html

<div style="border-radius: 16px; overflow: hidden; box-shadow: 0 4px 24px rgba(0,0,0,.1);">

<a-resource-booking-panel

base-url="https://anny.co"

resource="my-resource"

></a-resource-booking-panel>

</div>

```

Do **not** add padding inside the card around the widget — the widget handles its own internal spacing.


### Full-page widget with a single internal scroll (preferred for booking pages)

```html

<section style="height: 100vh;">

<a-organization-page

base-url="https://anny.co"

organization="my-org"

fullscreen="true"

nav-height="64px"

></a-organization-page>

</section>

```

- `fullscreen="true"` → `height: 100vh`

- `nav-height` subtracts your sticky header: `calc(100vh - 64px)`. Match it to the real header height so the widget sits flush below the nav and fills the rest of the viewport exactly.

- The result: the **page does not scroll — only the widget scrolls internally**. This is the preferred pattern for any full-page booking, calendar, or map experience. It avoids the double-scrollbar problem where both the page and the widget scroll independently.

- If you give the widget a fixed `height` attribute, auto-resize is disabled — do not combine `height` with `fullscreen`.

Use this especially for booking-first and calendar-first pages where the widget should dominate the layout.


### Widget beside editorial content

```html

<div style="display: grid; grid-template-columns: 1fr 420px; gap: 32px; align-items: start;">

<div>

<h1>Book your space</h1>

<p>…description…</p>

</div>

<a-resource-booking-panel

base-url="https://anny.co"

resource="my-resource"

></a-resource-booking-panel>

</div>

```

Panels expand vertically as the user navigates the booking flow. Use `align-items: start` so they don't stretch.

This is the preferred default for new marketing sites and content-driven pages.


### Button widget with a custom trigger

All button widgets accept a slotted element that replaces the built-in button.

```html

<a-resource-button base-url="https://anny.co" resource="my-resource">

<button class="hero-cta">Book now</button>

</a-resource-button>

```

When a slot is provided, `label`, `button-background`, `button-text`, and `button-width` are ignored. Click and keyboard handling are still managed by the widget wrapper.


### Login and cart in the top-right of the main nav

Put `a-login-button` and `a-cart-modal-button` together in the **top-right of the main navigation** — the spot users instinctively look for account and cart. Render the cart as an **icon with a count indicator only** (no text label), like any e-commerce header.

```html

<header style="display: flex; align-items: center; gap: 16px; padding: 0 24px; height: 64px;">

<a href="/">Logo</a>

<nav style="flex: 1;">…links…</nav>

<!-- pushed to the far right by the flex:1 nav above -->

<a-login-button base-url="https://anny.co"></a-login-button>

<a-cart-modal-button

base-url="https://anny.co"

modal-layout="drawer"

show-icon="true"

></a-cart-modal-button>

</header>

```

- These are utility widgets — put them in a stable position users can always find. They sync auth state and cart across all widgets on the page automatically.

- The cart button shows its own item-count indicator; keep it icon-only (omit `label`) so it reads as a standard cart icon.

- `drawer` is a good `modal-layout` for the cart so it slides in from the right, matching its top-right anchor.

For new sites, this top-right placement should be the default.


### Plan CTA button


For plans and subscriptions, prefer a button that opens checkout instead of embedding the full plan page into a mixed-content page.

```html

<a-subscription-button

base-url="https://anny.co"

plan="my-plan"

label="Choose plan"

modal-layout="drawer"

></a-subscription-button>

```

Use `a-subscription-page` only when the whole page is dedicated to the plan checkout experience.


## AI Chat Assistant (`a-agent-chat`)

`a-agent-chat` embeds the anny **AI chat assistant**. The visitor can ask questions and the assistant can guide them through the booking flow conversationally; when it needs to show the booking UI it opens it in a modal over the host page. Login (when required) and the user's access token are handled first-party on the anny chat page — the widget only renders the launcher/panel chrome and forwards configuration.


It needs the `organization` slug and an `assistant-id` (created in anny admin). Ask for both before generating the snippet.


### Placement

Set `placement` to choose how the chat appears:

| `placement` | Result | Use when |

|---|---|---|

| `bottom-right` (default) | Fixed launcher button in the viewport corner; opens a floating panel | Site-wide support / assisted booking helper |

| `relative` / `none` | Launcher rendered in normal document flow (e.g. inside a section) | A "chat with us" button placed in your own layout |

| `inline` | No launcher — the chat frame is embedded directly | Chat *is* the page section; give the parent a fixed size |


### Floating launcher (most common)

```html

<a-agent-chat

base-url="https://anny.co"

organization="my-org"

assistant-id="my-assistant"

placement="bottom-right"

greeting-message="Need a hand? Chat with us!"

></a-agent-chat>

```

The launcher is fixed bottom-right and the panel goes fullscreen on mobile. Place it once per page, near `</body>`.


### Inline frame

When the chat is itself a page section, use `placement="inline"` and give the **parent element** a fixed width/height — the frame fills it.

```html

<div style="width: 400px; height: 560px;">

<a-agent-chat

base-url="https://anny.co"

organization="my-org"

assistant-id="my-assistant"

placement="inline"

></a-agent-chat>

</div>

```

### Chat attributes

| Attribute | Purpose |

|---|---|

| `organization` | **Required.** Organization slug whose chat to embed |

| `assistant-id` | Assistant identifier (from anny admin) |

| `placement` | `bottom-right` (default), `relative` / `none`, or `inline` |

| `start-message` | Message the conversation is pre-seeded with |

| `greeting-message` | Teaser bubble shown next to the launcher before it's opened |

| `launcher-label` | Accessible label for the launcher button |

| `launcher-color` | Launcher button background (defaults to `primary-color`, else the branded gradient) |

| `consent` | `accepted` (pre-opted-in, no banner) or `hidden` (no banner, no persistent visitor id); omit to show the chat's own consent banner |

| `supports-login` | Require login before the chat is usable (gate handled first-party) |

It also accepts the shared design tokens (`primary-color`, `primary-background`, `text-primary-color`, `text-secondary-color`, `button-text`, `logo-url`, `locale`, …) which are forwarded to the chat surface.


## Height Behavior

| Widget type | Default behavior | Override |

|---|---|---|

| Panel | Auto-resizes to content | Cannot be overridden — `height` and `fullscreen` are ignored |

| Page / map / calendar | Auto-resizes unless overridden | `height="700px"` (fixed) or `fullscreen="true"` (viewport fill) |

| Button widget | No height — opens modal | Modal size controlled by `modal-layout`, `modal-size`, `modal-width` |


## Required Attributes

Every widget needs:

- `base-url` — the anny base URL, e.g. `https://anny.co`

- The entity slug for the widget type (`resource`, `organization`, `service`, `plan`, or `package`)


Utility widgets are the exception:

- `a-login-button` only needs `base-url` unless SSO options are required

- `a-cart-modal-button` only needs `base-url`

```html

<a-resource-booking-panel

base-url="https://anny.co"

resource="my-resource-slug"

></a-resource-booking-panel>

```


### Entity slug mapping

| Widget | Required entity attribute |

|---|---|

| `a-resource-page`, `a-resource-map`, `a-resource-calendar`, `a-resource-booking-panel`, `a-resource-button` | `resource` |

| `a-service-page`, `a-service-booking-panel`, `a-service-button` | `service` |

| `a-organization-page`, `a-organization-map`, `a-organization-calendar`, `a-organization-button` | `organization` |

| `a-subscription-page`, `a-subscription-button` | `plan` |

| `a-package-page`, `a-package-button` | `package` |

| `a-modal-button` | usually `resource` or `organization` |


### Common functional attributes

| Attribute | Meaning |

|---|---|

| `locale` | force widget language |

| `should-login` | require login before booking |

| `idp-uuid` | SSO identity provider |

| `height` | fixed iframe height for page widgets |

| `fullscreen` | full viewport height for page widgets |

| `nav-height` | subtract sticky header height from fullscreen widgets |

| `default-list` | initial tab for organization widgets |

| `view` | initial organization view |

| `calendar-view` | day/week/month/list view where supported |

| `default-category` | preselect category on `a-organization-page` |


### Date parameters

Many widgets support `start` and some also support `end`.


- ISO format: `2025-06-15`

- Relative values for `start`: `today`, `tomorrow`, `this_week`, `next_week`, `this_month`, `next_month`

- `end` should be an ISO date string

- Relative values are **not** supported by `a-resource-calendar` or `a-organization-calendar`

```html

<a-resource-booking-panel

base-url="https://anny.co"

resource="my-resource"

start="tomorrow"

></a-resource-booking-panel>

```

## Design Token Pass-Through

Pass brand colors and styling directly as HTML attributes. No CSS override needed.

```html

<a-organization-page

base-url="https://anny.co"

organization="my-org"

primary-color="#0f766e"

primary-background="#f8fafc"

panel-background="#ffffff"

small-border-radius="8px"

panel-shadow="0 8px 32px rgba(0,0,0,.08)"

text-primary-color="#111827"

></a-organization-page>

```


### Color tokens

| Token | Purpose | Fallback |

|---|---|---|

| `primary-color` | Main accent color | — |

| `primary-color-rgb` | RGB triplet for transparent accent usage, e.g. `15, 118, 110` | Auto-calculated from `primary-color` |

| `primary-color-hover` | Hover state of main accent | — |

| `primary-color-overlay` | Accent overlay surface | — |

| `secondary-color` | Secondary accent | — |

| `primary-stroke-color` | Stroke / outline accent; also sets table borders | — |


### Text tokens

| Token | Purpose |

|---|---|

| `text-primary-color` | Main text color |

| `text-secondary-color` | Secondary text color |

| `text-tertiary-color` | Muted text color |


### Background tokens

| Token | Purpose | Fallback |

|---|---|---|

| `primary-background` | Page/outer background | — |

| `primary-background-rgb` | RGB triplet for layered page background | Auto-calculated from `primary-background` |

| `panel-background` | Panel surface. Also sets `primary-background` if `primary-background` is not provided. | — |

| `panel-background-rgb` | RGB triplet for panel surface | Auto-calculated from `panel-background` or `primary-background` |

| `panel-background-light` | Lighter panel variant | — |

| `panel-background-overlay` | Overlay background | — |

| `panel-background-overlay-dense` | Dense overlay background | — |

| `panel-background-dark` | Dark panel surface | — |

| `input-background` | Input field background | — |


### Border and radius tokens

| Token | Purpose | Notes |

|---|---|---|

| `small-border-radius` | Base radius unit | All larger radii are calculated from this: `1.5×`, `2×`, `2.5×`, `3×`, `4×`, `5×` |

| `panel-border-radius` | Explicit large panel radius | Overrides the scale-up from `small-border-radius` |

| `detail-border-radius` | Explicit detail card radius | Overrides the scale-up from `small-border-radius` |

| `light-border-color` | Light border color | — |

| `table-border-color` | Table and grid border color | — |

| `panel-border-style` | Border style token | — |


### Shadow tokens

| Token | Purpose |

|---|---|

| `panel-shadow` | Panel shadow; also sets dropdown shadow |

| `detail-shadow` | Detail card shadow |


### Button styling tokens

These affect the outer CTA button for button widgets, cart button, and login button.


| Token | Purpose | Fallback |

|---|---|---|

| `button-background` | Trigger fill color | Falls back to `input-background` |

| `button-text` | Trigger text and icon color | Falls back to `text-primary-color` |

| `button-width` | Trigger width | — |

| `button-height` | Trigger height | — |

| `show-icon` | Show or hide icon on login/cart | — |


### Utility tokens

| Token | Purpose |

|---|---|

| `logo-url` | Custom logo shown in the pulsing loading animation only. Does not affect logos inside the booking UI. |


### RGB tokens — auto-calculation

`primary-color-rgb`, `panel-background-rgb`, and `primary-background-rgb` are automatically calculated from their hex counterparts when not explicitly provided. You only need to pass them if you want to supply a custom RGB triplet (e.g. for a non-hex color source).


Design rules:


- Prefer configuring design in admin first. HTML token overrides are for one-off page-specific changes.

- Keep contrast high between primary actions, text, and backgrounds.

- `small-border-radius` is the simplest way to control all corner radii at once. Use `panel-border-radius` and `detail-border-radius` only to override specific levels.

- Do not combine very strong shadows, very round corners, and low-contrast text simultaneously.


## Modal Layout Options

Button widgets open content in a modal. Control the layout with `modal-layout`:


| Value | Description |

|---|---|

| `dialog` | Centered dialog. Size with `modal-size` (`sm`/`md`/`lg`) or `modal-width` |

| `drawer` | Side panel from the right — good for checkout flows |

| `fullscreen` | Full viewport — best for mobile |


```html

<a-resource-button

base-url="https://anny.co"

resource="my-resource"

modal-layout="drawer"

></a-resource-button>

```


### Modal behavior attributes

| Attribute | Purpose |

|---|---|

| `modal-layout` | `dialog`, `drawer`, or `fullscreen` |

| `modal-size` | `sm`, `md`, or `lg` for dialog layout |

| `modal-width` | custom width such as `80vw`; overrides `modal-size` |

| `modal-title` | modal header title |

| `close-on-backdrop` | allow close on outside click |

| `show-close-button` | show close button |

| `aria-label` | accessible label for modal |


## Tracking Events

Booking widgets can emit DOM custom events you can subscribe to on the widget element.

Supported event names:

- `view-page`

- `add-to-cart`

- `start-checkout`

- `complete-checkout`

Common payload fields:

- `event_name`

- `value`

- `gross_value`

- `tax`

- `currency`

- `transaction_id`

- `items`

```html

<a-resource-page

id="booking-widget"

base-url="https://anny.co"

resource="my-resource"

></a-resource-page>

<script>

const widget = document.getElementById('booking-widget')

widget.addEventListener('complete-checkout', (event) => {

const data = event.detail

console.log('Checkout complete', data.transaction_id, data.gross_value)

})

</script>

```

`a-login-button` and `a-cart-modal-button` are utility widgets and are not booking event emitters.


## Common Mistakes to Avoid

| Mistake | Fix |

|---|---|

| Adding padding around a panel inside a card | Apply `border-radius` + `overflow: hidden` to the card wrapper; put the widget element directly inside |

| Wrapping the widget in a flex/grid container with `align-items: stretch` | Use `align-items: start` so the panel can grow freely |

| Loading the script multiple times | One `<script>` tag per page is enough |

| Setting `height` or `fullscreen` on a panel widget | These are ignored on panels — panels always auto-resize |

| Placing `a-login-button` / `a-cart-modal-button` per entity instead of once in the nav | These are page-level shared controls — one set, top-right of the main nav |

| A full-page widget that makes the page scroll *and* the widget scroll | Use `fullscreen="true"` + `nav-height` so only the widget scrolls; don't wrap a fullscreen widget in a tall scrolling container |

| Giving the cart button a text label in the header | Keep it icon + count only (`show-icon="true"`, no `label`) like a standard cart |

| Setting `height`/`fullscreen` on `a-agent-chat` inline mode | Size the **parent element** instead — the inline chat frame fills its container |

| Styling the widget element itself with CSS borders/radius | Style the wrapper element instead; the widget manages its own internal layout |


## Attribution Forwarding

UTM params and click IDs (`gclid`, `fbclid`, `ttclid`, `li_fat_id`) from the page URL are automatically forwarded into the widget iframe. No extra setup needed for conversion tracking.

Hat dies deine Frage beantwortet?