# Kibble installation contract: section 12 of 29 (guide version 3)

> This is **one section only**, not the whole contract. Complete document: https://www.usekibble.dev/AI/guide.md

## 12. Semantic markup {#markup}

All optional, all structural. None of it carries visitor data.

```html
<!-- identify the page's content: stable id and type -->
<meta name="kibble:content" content="room:harbor-king">
<meta name="kibble:type" content="room">
<meta property="og:image" content="https://www.example.com/img/room.jpg">   <!-- this site's hosts or listed image hosts; no signed URLs -->

<!-- sections whose visibility you want to understand (visible at least half for one second) -->
<section data-kibble-section="room-details"> ... </section>

<!-- meaningful clicks -->
<a data-kibble-click="book-now" data-kibble-type="cta"> ... </a>   <!-- cta|nav|product|room|link|filter|other -->

<!-- forms: names and error codes only, never values (details: Forms) -->
<form data-kibble-form="contact" data-kibble-form-type="contact">   <!-- contact|booking|checkout|newsletter|other -->
  <input name="email" data-kibble-field="email">
  <p data-kibble-error="email" data-kibble-error-code="format"> ... </p>   <!-- required|format|invalid|server -->
</form>
```

- Section markup records visibility (at least half of the element visible for one second), once per section per page view. It is not scroll depth.
- Booking forms can also carry `data-kibble-criteria` on explicitly marked fields. See [Booking engines and booking widgets](https://www.usekibble.dev/AI#booking).
- Form markup, validation errors and acceptance are in [Forms](https://www.usekibble.dev/AI#forms).
- If a page has no `kibble:content` tag, its path is used as the content id (`page:{path}`).

### Id rules

These rules apply to content ids and to the values of `data-kibble-click`, `data-kibble-section`, `data-kibble-form` and `data-kibble-field`. The collector enforces them, so follow them in the markup you write.

- Lowercased. `Room:Harbor-King` is stored as `room:harbor-king`.
- Must start with a letter or digit, and use only letters, digits and `_ . : - /`. At most 128 characters.
- Values with spaces are dropped by the browser. Use `book-now`, not `Book now`.
- A run of six or more digits needs a prefix with a colon (`hotel:123456`). Without it the id is rejected. Emails are rejected too.
- An invalid content id falls back to `page:{path}`. For the other attributes the value is dropped and the event is kept without it.
- Use `/` to place an id in Catalog: Catalog → Browse arranges records by the path in the id, so `property:houston/downtown` appears under Houston, and `property_child:houston/downtown/eat-drink` appears under it (a `_child` type belongs to the family of its base type). The same path rule applies to the id of any content type. This changes only where a record is shown in Catalog, not what is measured.
- Product categories with spaces (for example `Spa Hotels`) are dropped by the collector. Use a slug such as `spa-hotels`.

Examples of good content ids: `room:harbor-king` and `product:bath-oil`. Hotel and destination names are not sent; they come from the [Site catalog](https://www.usekibble.dev/AI#site-catalog), keyed by the same ids.

END OF SECTION markup
