# Amili Interface Guidelines

> One calm, honest default for every Amili surface: the app, amili.ai and every site built with the Amili Kit. Brands change the accent and a few details; the rest stays, so every Amili-built product is fast, readable and fair by default.

Version 1.0, 2026-10-21. Human page: https://amili.ai/guidelines. Every rule has a stable id (append-only); cite it, e.g. "a11y-1". The page anchor is https://amili.ai/guidelines#<id>.
Token values below are read from the Amili Kit (kit/brand-amili.mjs) when this file is built.

## Principles

- **p-1 Calm.** The interface serves the content. One accent colour, quiet surfaces, motion only as feedback. Nothing blinks, autoplays or covers what someone is reading.
- **p-2 One voice, an honest one.** Every surface looks and sounds like the same product: the same tokens, the same mark, the same word for the same action. No dark patterns: saying no is as easy as saying yes, nothing is pre-ticked, prices and affiliate links are labelled, and nothing is called live before it ships.
- **p-3 One clear next step.** Each screen says what it is for, puts the answer first and ends with one next step for the reader. One primary button per view; everything else is quieter.
- **p-4 Fast and solid.** Rendered on the server, no framework, no font download, no layout shift. The default state is already in the HTML; scripts only add to it.
- **p-5 For everyone.** Targets of at least 44px, a visible focus ring, real labels, contrast checked against WCAG 2.2 AA in every theme, usable at 200% zoom and with reduced motion.

## Tokens

Include the Kit brand CSS first (`import { BRAND_CSS } from "./kit/brand-amili.mjs"`, written as `<style data-vendored="amili-kit">`). Page CSS uses only the short names. Brands override the `--ak-*` values, never the short names.

### Colour roles

| Short name | Kit token | Role | Light | Dark | Warm night |
|---|---|---|---|---|---|
| `--bg` | `--ak-s0` | Page background | `#F5F5F4` | `#0B0B0B` | `#1A1512` |
| `--panel` | `--ak-s1` | Cards, menus, raised surfaces | `#FFFFFF` | `#161616` | `#231C17` |
| `--panel2` | `--ak-s2` | Bands, quiet fills | `#EBEBEA` | `#1F1F1F` | `#201915` |
| `--elev` | `--ak-s3` | The most raised surface | `#E2E2E0` | `#2A2A2A` | `#2C231D` |
| `--line` | `--ak-line` | Hairlines and borders (1px) | `rgba(18,18,18,.12)` | `rgba(255,255,255,.12)` | `rgba(232,217,192,.13)` |
| `--txt` | `--ak-ink` | Text and icons | `#121212` | `#FFFFFF` | `#E8D9C0` |
| `--dim` | `--ak-dim` | Secondary text, labels, meta | `#5E5E5A` | `#A7A7A7` | `#A8957B` |
| `--accent` | `--ak-acc` | Actions, links, current item, selection | `#106F4F` | `#3ED598` | `#E0A04A` |
| `--on-acc` | `--ak-on` | Text and icons on the accent | `#FFFFFF` | `#0B0B0B` | `#1A1512` |
| `--acc-soft` | `--ak-soft` | Selected and highlighted fills | `rgba(16,111,79,.11)` | `rgba(62,213,152,.14)` | `rgba(224,160,74,.16)` |
| `--err` | `--ak-err` | Errors only, always with words | `#A8433B` | `#E39A92` | `#EF8F7F` |
| `--input-bg` | `--ak-typing` | Text field background | `#FFFFFF` | `#161616` | `#231C17` |
| `--shadow` | `--ak-shadow` | Only for things that float | `0 1px 0 rgba(18,18,18,.04),0 10px 26px -18px rgba(18,18,18,.34)` | `0 1px 0 rgba(0,0,0,.2),0 10px 26px -18px rgba(0,0,0,.7)` | `0 1px 0 rgba(0,0,0,.2),0 10px 26px -18px rgba(0,0,0,.7)` |

Accent picker (`data-acc` on `<html>`; default evergreen; not applied under Warm night):

| data-acc | --ak-acc light | --ak-acc dark | --ak-soft light | --ak-soft dark |
|---|---|---|---|---|
| (none, evergreen) | `#106F4F` | `#3ED598` | `rgba(16,111,79,.11)` | `rgba(62,213,152,.14)` |
| blue | `#2E4A6B` | `#8FB0D4` | `rgba(46,74,107,.11)` | `rgba(143,176,212,.16)` |
| graphite | `#3A3F45` | `#C9CDD2` | `rgba(58,63,69,.11)` | `rgba(201,205,210,.16)` |
| amber | `#965900` | `#F2B45C` | `rgba(150,89,0,.11)` | `rgba(242,180,92,.16)` |

Themes: light and dark follow `prefers-color-scheme`; `data-theme="light|dark|warm"` on `<html>` forces one (the app's setting).

### Contrast (measured from the values above)

| Pair | Light | Dark | Warm night | Needed |
|---|---|---|---|---|
| Text on page | 17.2:1 | 19.7:1 | 13.0:1 | 4.5:1 |
| Secondary text on page | 6.0:1 | 8.2:1 | 6.3:1 | 4.5:1 |
| Secondary text on card | 6.5:1 | 7.5:1 | 5.8:1 | 4.5:1 |
| Accent (links) on page | 5.6:1 | 10.5:1 | 8.0:1 | 4.5:1 |
| Text on accent | 6.2:1 | 10.5:1 | 8.0:1 | 4.5:1 |
| Error text on page | 5.5:1 | 8.7:1 | 7.7:1 | 4.5:1 |

### Scale tokens

| Token | Value | Use |
|---|---|---|
| `--r-hair` | `2px` | hairline marks |
| `--r-xs` | `6px` | focus ring, small tags |
| `--r-sm` | `10px` | small controls |
| `--r-tab` | `12px` | tabs |
| `--r-md` | `14px` | menus, popovers, inner panels |
| `--r-card` | `20px` | cards |
| `--r-pill` | `999px` | buttons, fields, chips |
| `--s-1` | `4px` |  |
| `--s-2` | `8px` |  |
| `--s-3` | `12px` |  |
| `--s-4` | `16px` | gutter on phones |
| `--s-5` | `24px` | card padding |
| `--s-6` | `32px` |  |
| `--t-xs` | `12px` | meta only |
| `--t-sm` | `13px` | captions, kickers |
| `--t-md` | `15px` | secondary text, menus |
| `--t-lg` | `17px` | body |
| `--t-xl` | `22px` | lead, card titles |
| `--stroke` | `1.75` | icon stroke width |
| `--dur-fast` | `.15s` | hover, press |
| `--dur` | `.2s` | open, confirm |
| `--ease` | `ease-in-out` | all transitions |
| `--serif` | `"Iowan Old Style",Charter,Georgia,"Times New Roman",serif` | headings, wordmark, Amili's voice |
| `--sans` | `ui-sans-serif,system-ui,-apple-system,"Segoe UI",Roboto,Helvetica,Arial,sans-serif` | body, UI |

## Foundations

### Colour (#colour)

Colours are roles, not values. A page names the role (page, card, text, action) and the Kit decides the value for light, dark and Warm night.

- **col-1** Use the short role names in page CSS: --bg, --panel, --panel2, --line, --txt, --dim, --accent, --on-acc, --acc-soft, --err, --input-bg. Never write a colour value in a page; the build refuses a page-local colour token.
- **col-2** The accent means "you can act here" or "you are here": the primary button, links, the current tab, the selected card. Do not use it for decoration or to emphasise plain text.
- **col-3** Text and icons on an accent fill use --on-acc. Never put --dim or --txt on --accent.
- **col-4** --err is for errors only, and always comes with words that say what is wrong. Colour alone never carries meaning.
- **col-5** Show state by shade, not by loud blocks: selected is an --acc-soft fill with an accent edge; hover is a small opacity or border change.
- **col-6** Surfaces step: --bg for the page, --panel for cards, menus and fields, --panel2 for a band or a quiet fill, --line (1px) to separate. Cards carry no shadow at rest; only things that float (a menu, a sheet) get --shadow.
- **col-7** Check every colour pair in light, dark and Warm night, and with each accent from the picker.
- **col-8** Amili's own pages use the evergreen accent. Blue, graphite, amber and Warm night are the user's choice in the app, never the default of a page.

- Do: background:var(--accent); color:var(--on-acc)
  Don't: background:var(--accent); color:var(--txt)
- Do: Error: "Enter an email address, like name@company.nl" in --err
  Don't: A red border with no words
- Do: A selected card: --acc-soft fill and an accent edge
  Don't: A selected card painted fully in the accent

### Typography (#typography)

Two families with clear jobs and a short scale. The serif speaks; the sans works.

- **typ-1** --serif is for headings, the wordmark and Amili's own voice (an answer, a quote). --sans is for everything people read to operate the page: body, labels, buttons, menus, tables.
- **typ-2** Use the scale: --t-xs for meta only (never body), --t-sm for captions and kickers, --t-md for secondary text and menus, --t-lg for body, --t-xl for a lead or a card title. Display headings above that use clamp() at page level.
- **typ-3** Body is --t-lg at line-height 1.6. Text in a form field is at least 16px so phones do not zoom in.
- **typ-4** Two weights: 400 and 600. Serif headings at 400, buttons and labels in the sans at 600. No light or thin weights.
- **typ-5** Sentence case everywhere. Uppercase only for a short kicker in --t-sm with letter-spacing .08em.
- **typ-6** Keep running text between 34 and 44em wide; balance headings with text-wrap:balance.
- **typ-7** System fonts only (the --serif and --sans stacks). No web-font download on Amili sites.

- Do: h2 in --serif at 400, body in --sans at --t-lg
  Don't: Buttons in the serif, or body text in --t-xs
- Do: A kicker: HOW IT WORKS in --t-sm, 600, spaced
  Don't: A whole heading in capitals

### Layout and spacing (#layout)

Designed at 390px first, then widened. Space does most of the grouping.

- **lay-1** Design at 390px first. There is never horizontal scroll at 390px; wide tables and chip rows scroll inside their own box.
- **lay-2** Spacing comes from --s-1 to --s-6 (4px, 8px, 12px, 16px, 24px, 32px). Gaps between page sections are multiples of 8 (48, 56, 72).
- **lay-3** The side gutter is --s-4 (16px) and respects the safe area: padding-left:max(16px, env(safe-area-inset-left)), the same on the right.
- **lay-4** Widths: running text 34 to 44em, page content up to 1080px, header and footer up to 1240px.
- **lay-5** Most important first: the intro says in one sentence what the page is for; the answer comes before the detail.
- **lay-6** Group with space and hairlines first. Use a card only for a unit people act on or compare (a plan, a product, a contact).
- **lay-7** Break by content, not by device: one column on phones, two from about 760px, a side table of contents from about 960px, the full menu from 48rem.
- **lay-8** Anything fixed to the bottom (a consent bar, a buy bar) adds env(safe-area-inset-bottom) and pads the page so it never hides the end of it.

- Do: gap:var(--s-3); padding:var(--s-5)
  Don't: gap:13px; padding:22px
- Do: A list of facts separated by hairlines
  Don't: Every paragraph in its own card

### Shape (#shape)

A small set of corner radii, each with one job.

- **shp-1** Every border-radius is a --r-* token or 0. The design lint refuses anything else.
- **shp-2** Buttons, text fields and chips are pills (--r-pill). Cards use --r-card; menus, popovers and inner panels --r-md; tabs --r-tab; small controls in dense parts --r-sm; the focus ring and tiny tags --r-xs.
- **shp-3** Nested corners get smaller inside: an inner panel in a card uses --r-md, not --r-card.
- **shp-4** A card is --panel, a 1px --line border, --r-card corners and --s-5 padding. No shadow at rest.
- **shp-5** Keep a control group consistent: do not mix pill and square corners in one row of buttons.

- Do: A card with the --r-card corner token
  Don't: A card with a typed 18px corner

### Motion (#motion)

Motion confirms that something happened. It is short, it never moves the layout, and it disappears when people ask for less.

- **mot-1** Animate state, not decoration: colour and opacity changes over --dur-fast (hover, press) or --dur (open, copied) with --ease.
- **mot-2** Never animate size or position of controls. Buttons never grow; a text field may get taller, the buttons next to it stay 44px.
- **mot-3** Under prefers-reduced-motion: reduce, changes happen at once. Smooth scrolling only under no-preference.
- **mot-4** Nothing autoplays. Video plays on tap. Nothing flashes more than three times a second.
- **mot-5** The mark's asterisk may breathe by opacity only; it stops under reduced motion.
- **mot-6** Do not animate transform on text or the logo: it blurs on some screens.

- Do: transition:opacity var(--dur-fast) var(--ease)
  Don't: transition:all .6s; transform:scale(1.05) on hover

### Accessibility (#accessibility)

Built in from the first line, then checked. We say "checked against WCAG 2.2 AA", never "compliant": an automated check finds part of the problems, not all.

- **a11y-1** Every target is at least 44 by 44px. Primary buttons are 52px tall; booking and buy buttons may be 56px. Inline links in running text get a 44px tap area with padding, without moving the lines.
- **a11y-2** Contrast: at least 4.5:1 for text, 3:1 for large text and for the edges of controls, in every theme and accent.
- **a11y-3** Focus is always visible: :focus-visible gets outline:2px solid var(--accent), outline-offset:2px, radius --r-xs. Never remove an outline without a replacement.
- **a11y-4** Every control has a visible label or an aria-label. A placeholder is never the label. An icon inside a labelled control is aria-hidden.
- **a11y-5** Status and results are announced: put them in an element with role="status" or aria-live="polite". An error names the field and the fix, and focus moves to it.
- **a11y-6** Structure: html lang set, header, nav with aria-label, main, footer; one h1; headings in order; the current page marked with aria-current="page".
- **a11y-7** The page works at 200% zoom and with larger text: nothing is clipped and nothing needs horizontal scroll.
- **a11y-8** Check with axe-core at 390 and 1280px in light and dark, plus a keyboard pass. Fix every critical and serious issue before launch, and say "checked against WCAG 2.2 AA".

- Do: <label for="email">Email</label><input id="email" type="email">
  Don't: <input placeholder="Email">
- Do: "Checked against WCAG 2.2 AA"
  Don't: "Fully accessible" or "WCAG compliant"

### Writing (#writing)

Plain words, said once, in the voice of someone who is helping.

- **wri-1** Plain words, active voice, short sentences. Read it aloud; cut every word that does not change the meaning.
- **wri-2** Buttons start with a verb and say what will happen: "Request a site", "Save choice", "Copy". Not "Submit", "OK" or "Let's go!".
- **wri-3** End a page or an answer with one next step for the reader, said to them: "For you: …" (Dutch: "Voor jou: …").
- **wri-4** No hype, no superlatives, no invented numbers. A number is measured and dated, or it is left out.
- **wri-5** What is not built yet is tagged "Coming". Never claim an integration, a result or a price before it exists.
- **wri-6** An error says what happened, whose side it is on and what to do: "That didn't save. The problem is on our side. Try again in a moment?"
- **wri-7** One language per page, never mixed. English is the base; Dutch uses je and jij.
- **wri-8** The same word for the same thing everywhere: Say, Do and Know; Decline and Allow; Request a site.
- **wri-9** Sentence case for headings, buttons and menu items. No emoji as icons or bullets.

- Do: "Request a site"
  Don't: "Get started now!"
- Do: "Coming: sending tasks to your calendar"
  Don't: "Works with your calendar" (when it does not yet)
- Do: "For you: pick a plan, we reply within one working day."
  Don't: Three equal buttons at the end of the page

### Icons (#icons)

Simple line icons that sit next to words, drawn the same way everywhere.

- **ico-1** Inline SVG on a 24 by 24 grid: fill none, stroke currentColor, stroke-width var(--stroke) (1.75 as an SVG attribute, where CSS variables do not work), round caps and joins.
- **ico-2** Sizes: 18px inside text and table cells, 22px in lists, 26px on cards and in the side rail. Match the weight of the text next to it.
- **ico-3** An icon next to words is aria-hidden="true". An icon-only button has an aria-label and a 44px target.
- **ico-4** No emoji as icons, no icon fonts, no third-party logos as decoration.
- **ico-5** One style per page: do not mix filled and outlined icons.

- Do: <svg aria-hidden="true" stroke-width="1.75">… + the word "Prices"
  Don't: A price-tag emoji with no text

### Dark mode and Warm night (#dark-mode)

Websites follow the system setting. The app adds a choice: Light, Dark, Warm night and an accent.

- **drk-1** Pages follow prefers-color-scheme. Websites get no theme toggle of their own; the app's settings offer Light, Dark, Warm night and the accent (data-theme and data-acc on html).
- **drk-2** Dark is not inverted light. Surfaces get lighter as they come forward (--bg, then --panel, --panel2, --elev); the accent gets lighter and text on it turns dark.
- **drk-3** Every page sets color-scheme:light dark and the two theme-color metas (light and dark page colour) from the Kit.
- **drk-4** Images and screenshots work on both: supply a dark version or choose one without a white box around it.
- **drk-5** Warm night is a dark theme with warm ink and an amber accent; the accent picker does not apply under it.

- Do: Colours from tokens, so dark comes for free
  Don't: A @media (prefers-color-scheme:dark) block with page-local colours

## Patterns

### Navigation (#navigation)

People should always know where they are and get back in one move.

- **nav-1** A short, fixed menu (four to five items), a language switch and one call to action. The current page has aria-current="page" and an accent underline, in normal weight so the row does not jump.
- **nav-2** On phones the header is one row: the mark, the language, a Menu button that opens a panel, and the one call to action.
- **nav-3** A header that slides away while reading down comes back as soon as the reader scrolls up a little. It carries no background block, shadow or blur of its own and never covers content; jump targets use scroll-margin-top.
- **nav-4** Pages deeper than one level show breadcrumbs (Kit breadcrumbs, with BreadcrumbList data).
- **nav-5** Long pages get a table of contents: a sticky list beside the text on desktop, a row of chips that scrolls sideways on phones, and the current section marked.
- **nav-6** Search lives in one place. On phones it opens as a full-screen sheet with a 44px field and a 44px close button.
- **nav-7** Use a dialog or sheet only for a short task that needs a decision. It has an obvious close, Escape closes it, and it never opens another dialog on top.

- Do: Menu: Home, Pricing, How we work, Contact
  Don't: A menu with eight items and a second row

### Forms and entering data (#forms)

Ask for little, explain it clearly, and never lose what someone typed.

- **frm-1** The label sits above the field and stays visible. Help text goes under the label. A placeholder is only an example.
- **frm-2** Fields are pills, at least 52px tall, 16px text, background var(--input-bg), a 1px --line border and an accent focus outline.
- **frm-3** Ask only what is needed now. Use the right type, inputmode and autocomplete; offer a choice instead of typing when the answers are known.
- **frm-4** Check on submit or when leaving a field, not on every key. Keep what was typed, put the message next to the field and move focus there.
- **frm-5** The submit button says what happens and reads "One moment…" while sending. It is disabled only while sending.
- **frm-6** Say "done" only after the server confirmed it. If it failed, say so and keep the form filled.
- **frm-7** A text area grows with its text; the buttons next to it keep their size.

- Do: type="email" autocomplete="email" inputmode="email"
  Don't: type="text" for an email address
- Do: "You're on the list." after the server said 200
  Don't: "Thanks!" before the request returned

### Feedback (#feedback)

Every action gets an answer, in place and sized to the action.

- **fbk-1** Confirm in place: the button that copied says "Copied ✓", the field that saved says "Saved". No toast for a small thing, no alert for something that is not critical.
- **fbk-2** "Was this page helpful?" offers Yes and No, the same size and weight. After a choice, an optional text field ("What would have helped?") with Send and Skip. Thank once and do not ask again on that page.
- **fbk-3** Ask for confirmation only before something that cannot be undone, and say exactly what will be lost.
- **fbk-4** When something cannot be done, say why and what the person can do instead.

- Do: Yes and No as two equal buttons
  Don't: A big green Yes and a small grey "no thanks"

### Loading and empty states (#loading)

Something useful is on screen at once, and nothing jumps when the rest arrives.

- **lod-1** Render the default state on the server: a calculator shows its default result, a list shows its first items. Scripts add to the page; they do not build it.
- **lod-2** Reserve space for what arrives later: width and height on images, a min-height on result areas. No layout shift.
- **lod-3** A wait of more than about a second says so in words ("One moment…"); a long one says what is happening.
- **lod-4** An empty state says what is missing and offers the one next step. Never an empty box.
- **lod-5** Keep the rest of the page usable while something loads.

- Do: "No answers shared yet. Ask Amili something and share the answer."
  Don't: A blank area

### Consent (#consent)

The Kit consent bar: one sentence, two equal buttons, nothing loads before a yes.

- **cns-1** Nothing that measures or tracks loads before Allow. Use the Kit consent bar (Consent Mode defaults to denied where the law asks).
- **cns-2** Decline comes first. Decline and Allow have the same size, style and weight. Nothing is pre-selected or focused when the bar appears.
- **cns-3** One plain sentence that names the tools (Google Analytics, Microsoft Clarity) and links to the privacy page.
- **cns-4** A "Cookie settings" button in the footer reopens the bar. The choice is kept for 12 months; Escape means Decline.
- **cns-5** While open, the bar pads the page so it never hides the end of it.

- Do: [Decline] [Allow], equal
  Don't: [Accept all] big and coloured, "manage" as a small link

### Buying and affiliate links (#buying)

A buy button is labelled as one, kept apart from advice, and never pushes.

- **buy-1** An affiliate link says so next to the button ("Affiliate link") and in the footer, and carries rel="sponsored nofollow noopener".
- **buy-2** Never place a paid or affiliate link inside an editorial list or ranking.
- **buy-3** A price is live or absent. Never a stale or invented price.
- **buy-4** The sticky buy bar (Kit buybar) sits in the flow, is 56px tall with a 44px button, respects the safe area, and appears once per page.
- **buy-5** No fake scarcity, countdowns or confirmshaming ("No, I don't like saving money").

- Do: "Check price on Amazon" + "Affiliate link"
  Don't: "Only 2 left!" with no source

### Onboarding and choosing a plan (#plans)

Let people see what they get, side by side, and choose without pressure.

- **pln-1** Plan choice uses the Kit plan-compare: the same labelled rows, in the same order, in every card.
- **pln-2** Mark at most one advised plan with a ribbon. The visitor chooses; no add-on is pre-ticked.
- **pln-3** Each price shows the currency, the period and whether VAT is included. A struck-through old price only for a real discount.
- **pln-4** Minimum term and how to cancel are visible before paying. Cancelling is as easy as signing up. A trial that turns paid is announced 3 days before.
- **pln-5** Let people try before asking for setup; ask for a permission or a detail only when the feature needs it; every onboarding step can be skipped.
- **pln-6** Rows past the fold hide behind "Show all" but stay in the HTML.

- Do: "EUR 59 per month, excl. VAT, 12 months minimum"
  Don't: "From EUR 59*" with the terms in a footnote

## Components (kit/*.mjs)

Use a Kit module before writing your own. Every module exports render functions plus its CSS and JS strings.

| Module | What it is for | Import | Demo |
|---|---|---|---|
| `brand-amili` (Brand tokens and mark) | Colour roles (light, dark, Warm night, accent picker), type, radius, spacing, motion, the wordmark and the icon. Every page includes it first. | `TOKEN_CSS, TOKEN_SOURCE, BRAND_CSS, MARK_HTML, ICON_SVG, THEME_COLOR, BRAND_JS` | https://amili.ai/guidelines#colour |
| `footer` (Footer) | The site footer: purpose line, links, business details, legal links, language switch, Cookie settings. | `AMAZON_TM, validateFooterConfig, creditHref, CLIENT_CREDIT_URL, clientCreditHref, renderFooter, FOOTER_CSS, CLIENT_CREDIT_CSS` | https://amili.ai/kit#kit-footer |
| `consent` (Consent bar) | Cookie choice before any measuring tag; Decline and Allow equal. | `EEA_UK_CH_CA, ASK_ZONES, KEEP_MS, STRINGS, langOf, settingsLabel, consentHead, renderConsent, CONSENT_CSS, CONSENT_JS` | https://amili.ai/kit#kit-consent |
| `search` (Amili Search) | Site search over a prebuilt index; full-screen sheet on phones. | `engine, brandMark, renderSearch, BRAND_CSS, SEARCH_CSS, LAUNCHER_CSS, searchScript, SEARCH_JS` | https://amili.ai/kit#kit-search |
| `search-build-index` (Search index builder) | Build step: writes the search index from the built pages. | `pageRow, buildIndex` | https://amili.ai/kit#kit-search |
| `search-inject` (Search injector) | Build step: adds search to every built page. | `strip, injectHtml, run` | https://amili.ai/kit#kit-search |
| `toc` (Table of contents) | Jump list on long pages, chips on phones, current section marked. | `slugify, shortLabel, brandMark, collectHeadings, renderToc, addToc, BRAND_CSS, TOC_CSS, TOC_JS` | https://amili.ai/kit#kit-toc |
| `breadcrumbs` (Breadcrumbs) | Where you are on deep pages, with BreadcrumbList data. | `jsonForScript, validateBreadcrumbs, breadcrumbJsonLd, renderBreadcrumbs, BREADCRUMBS_CSS` | https://amili.ai/kit#kit-breadcrumbs |
| `faq` (FAQ) | Questions and answers that fold, with FAQPage data. | `jsonForScript, brandMark, validateFaq, faqJsonLd, renderFaq, BRAND_CSS, FAQ_CSS` | https://amili.ai/kit#kit-faq |
| `related` (Related links) | "Keep reading": one lead link and a few smaller ones, rendered on the server. | `brandMark, pickRelated, renderRelated, BRAND_CSS, RELATED_CSS` | https://amili.ai/kit#kit-related |
| `compare` (Comparison table) | Products or plans in a table with a sticky header and pinned first column. | `validateCompare, bestCells, renderCompare, COMPARE_CSS, COMPARE_JS` | https://amili.ai/kit#kit-compare |
| `calculator` (Calculator shell) | Labelled inputs and an announced result, default result already in the HTML. | `brandMark, formatNumber, validateCalculator, renderCalculator, BRAND_CSS, CALC_CSS, CALC_JS` | https://amili.ai/kit#kit-calculator |
| `buybar` (Sticky buy bar) | One labelled buy action that stays in reach on long product pages. | `validateBuyBar, renderBuyBar, BUYBAR_CSS, BUYBAR_JS` | https://amili.ai/kit#kit-buybar |
| `amazon-ad` (Amazon product card) | Product image, live price and a clear, labelled affiliate button. | `VARIANTS, CACHE_TTL_S, MAX_AGE_MS, DISCLAIMER, hash32, utcDay, candidatesFor, pickProducts, validatePool, parseItem, API_RESOURCES, makeItemsHandler, makeGateHandler, validateAdConfig, renderAd, renderAdSlots, AD_HEAD_JS, RANK_JS, AD_JS, AD_CSS` | https://amili.ai/kit#kit-ad |
| `beacon` (Click beacon) | Cookie-free click counts per position, for choosing placements. | `beaconScript, makeBeaconHandler, kvCounter` | https://amili.ai/kit#kit-beacon |
| `plan-compare` (Plan choice) | The choose-a-plan step of onboarding: cards with the same rows, one advised. | `renderPlanCompare, PLAN_COMPARE_CSS, PLAN_COMPARE_JS` | https://amili.ai/guidelines#plans |
| `copy-block` (Copy block) | A ready-to-paste text with one copy button that confirms in place. | `renderCopyBlock, COPY_BLOCK_CSS, COPY_BLOCK_JS` | https://amili.ai/guidelines#llm |
| `heading-fold` (Heading fold) | A section that folds behind its heading, with a count of what is inside. | `renderHeadingFold, HEADING_FOLD_CSS, HEADING_FOLD_JS` | none yet |
| `contact-card` (Contact card) | An organisation as a card with big call, mail and open buttons. | `HALF_MAX, renderContactCard, renderContactCards, CONTACT_CARD_CSS, CONTACT_CARD_JS` | none yet |
| `page-md` (Page from Markdown) | Markdown with Kit blocks (folds, copy blocks, contact cards, checklists, tables) as one page; used by amili.ai/p/. | `keyOf, inline, renderPageMarkdown, PAGE_MD_CSS, PAGE_MD_JS, PAGE_MD_ALL_CSS, PAGE_MD_ALL_JS` | none yet |
| `side-rail` (Side rail) | Sticky actions at the right edge (request, prices, examples, WhatsApp); steps aside over text. | `ICONS, renderSideRail, SIDE_RAIL_CSS, SIDE_RAIL_JS` | Live on every indexable amili.ai page |
| `session-bar` (Session bar) | "Signed in as …" and a real Log out, for pages behind a sign-in. | `renderSessionBar, SESSION_BAR_CSS` | none yet |
| `showcase` (Showcase) | The agency layout of amili.ai: display hero, device frames, work grid, plan columns. | `pic, browser, phone, sitePreview, sitePreviews, eyebrow, offerBadge, factChips, vsRow, SHOWCASE_CSS, SHOWCASE_JS` | https://amili.ai/ |
| `say-deck` (Say deck) | The app's Say view: one message at a time, Amili's answer in the serif, and the composer. | `ICON, sayIcon, sayDeckModel, SAY_DECK_CSS, sayDeckHtml, sayNavHtml, sayToolsHtml` | none yet |

### Gaps (no Kit module yet)

- **gap-1** Buttons and text fields: no Kit module yet. Pages write them from tokens (pill, 52px, --accent / --input-bg), as shown on this page.
- **gap-2** Header and menu: amili.ai's header is written by scripts/chrome.mjs, not a Kit module, and it does not yet slide away and come back on scroll-up (nav-3); the fleet header does.
- **gap-3** "Was this page helpful?": no Kit module and no endpoint yet. The demo on this page sends nothing.
- **gap-4** Dialog or sheet, status message (toast), tabs or segmented control, switch and checkbox: no Kit module yet.
- **gap-5** Empty state and loading placeholder: no Kit module yet (pattern only).
- **gap-6** Display heading sizes are clamp() values per page, not tokens; a --t-display token is still to come.
- **gap-7** No Dutch edition of these guidelines yet.

## Theming for brands

A brand may change:

- **thm-1** The accent family, in light and dark: --ak-acc, --ak-soft, --ak-on (text on the accent), --ak-mine, --ak-wash. Text on the accent must reach 4.5:1 and the accent on the page 3:1.
- **thm-2** The surfaces --ak-s0 to --ak-s3 and --ak-line, if text keeps 4.5:1 on every one of them, in light and dark.
- **thm-3** The two font families --serif and --sans (system stacks or one self-hosted family; no layout-shifting web-font swap).
- **thm-4** Corner radii as a set: --r-card, --r-md and --r-sm may become squarer or rounder together. --r-pill stays a pill for buttons and fields.
- **thm-5** The mark: a brand uses its own logo in place of the Amili mark.

Stays the same:

- **thm-6** The spacing scale --s-1 to --s-6 and the type scale --t-xs to --t-xl.
- **thm-7** The role names (--bg, --panel, --txt, --accent …): override the --ak-* values, never rename or bypass the roles.
- **thm-8** 44px targets, the focus ring rule, the contrast minimums and reduced motion.
- **thm-9** --stroke (1.75), --dur-fast (.15s), --dur (.2s) and --ease (ease-in-out).
- **thm-10** The honest patterns: consent, affiliate labels, plan choice and "Coming" tags.

Example override (after the Kit CSS):

```css
/* Brand override: put it AFTER the Kit's brand CSS. Example values for an imaginary bakery. */
:root{
  --ak-acc:#8A3B12; --ak-soft:rgba(138,59,18,.11); --ak-on:#FFFFFF; --ak-mine:#8A3B12; --ak-wash:#C46A3A;
  --serif:"Iowan Old Style",Charter,Georgia,serif;
  --r-card:12px; --r-md:10px; --r-sm:8px;
}
@media (prefers-color-scheme:dark){:root:where(:not([data-theme=light])){
  --ak-acc:#F0A57A; --ak-soft:rgba(240,165,122,.16); --ak-on:#1A0F08; --ak-mine:#5A2A10; --ak-wash:#A8552A;
}}
:root:where([data-theme=dark]){
  --ak-acc:#F0A57A; --ak-soft:rgba(240,165,122,.16); --ak-on:#1A0F08; --ak-mine:#5A2A10; --ak-wash:#A8552A;
}
```

## System prompt block

Paste into the system prompt of an LLM that writes Amili UI:

```text
Build UI with the Amili Kit and follow the Amili Interface Guidelines (https://amili.ai/guidelines.md).
Tokens
- Include the Kit brand CSS (kit/brand-amili.mjs BRAND_CSS) first. Use only its short names in page CSS:
  colour --bg --panel --panel2 --elev --line --txt --dim --accent --on-acc --acc-soft --err --input-bg --shadow;
  space --s-1 4px, --s-2 8px, --s-3 12px, --s-4 16px, --s-5 24px, --s-6 32px;
  type --t-xs 12px, --t-sm 13px, --t-md 15px, --t-lg 17px, --t-xl 22px;
  radius --r-hair 2px, --r-xs 6px, --r-sm 10px, --r-tab 12px, --r-md 14px, --r-card 20px, --r-pill 999px;
  motion --dur-fast .15s, --dur .2s, --ease ease-in-out; icons stroke var(--stroke) = 1.75; fonts --serif, --sans.
- Never write a hex colour, a raw radius or another stroke width in page CSS. Brands override --ak-* values only.
- Light, dark and Warm night come from the tokens; never add page-local dark-mode colours.
Look
- Calm: one accent, used only for actions, links, the current item and the selection. Text on accent = --on-acc.
- Serif (400) for headings and Amili's own voice; sans for body (--t-lg/1.6), labels and buttons (600).
- Sentence case. No emoji as icons. Inline SVG line icons, 24 grid, currentColor, aria-hidden next to text.
- Cards only for units people act on: --panel, 1px --line, --r-card, padding --s-5, no shadow at rest.
- Phone first at 390px, gutter max(16px, env(safe-area-inset-left/right)), no horizontal scroll.
- Text 34-44em wide; content max 1080px; header/footer max 1240px.
Behaviour
- Every target at least 44x44px; primary buttons 52px tall, pill shaped (--r-pill).
- :focus-visible = 2px solid var(--accent), offset 2px. Never remove focus.
- Real <label> above every field; placeholder is only an example; inputs 16px, background var(--input-bg).
- Status in role="status"/aria-live="polite". Errors: what happened, whose side, what to do.
- Render the default state in HTML; reserve space; no layout shift; no framework, no web fonts.
- Motion only as feedback (opacity/colour); none under prefers-reduced-motion; nothing autoplays.
- A header that hides on scroll-down returns on scroll-up and never covers content.
Honesty (no dark patterns)
- Decline and Allow equal (Kit consent bar); nothing tracks before Allow.
- Affiliate links labelled "Affiliate link", rel="sponsored nofollow noopener", never inside editorial lists.
- No fake scarcity, countdowns, confirmshaming or pre-ticked add-ons. Cancel as easy as sign-up.
- Prices show currency, period and VAT; never invent a price, a number or an integration. Unbuilt = "Coming".
Writing
- Plain, active, short. Buttons start with a verb ("Request a site"). One language per page.
- End with one next step for the reader ("For you: …" / "Voor jou: …"). No hype.
Components (kit/*.mjs): amazon-ad, beacon, brand-amili, breadcrumbs, buybar, calculator, compare,
consent, contact-card, copy-block, faq, footer, heading-fold, page-md, plan-compare, related,
say-deck, search, search-build-index, search-inject, session-bar, showcase, side-rail, toc.
Use a Kit module before writing your own.
Check: axe-core at 390 and 1280px, light and dark; say "checked against WCAG 2.2 AA", never "compliant".
Cite rules by id (e.g. col-3, a11y-1) from https://amili.ai/guidelines.md.
```

## Sources

- Tokens: kit/brand-amili.mjs (copied from the Amili app's own tokens).
- Inspired by the structure and principles of Apple's Human Interface Guidelines (https://developer.apple.com/design/human-interface-guidelines/); written for Amili in our own words.
- Accessibility: checked against WCAG 2.2 level AA (https://www.w3.org/TR/WCAG22/), never claimed as "compliant".
