# @sonordev/site-kit/cta-bar

The Liquid Glass mobile CTA bar (6.1.0). A floating frosted capsule that
keeps a site's one or two highest-intent actions a thumb away on phones.

It replaces a hand-rolled sticky mobile bar, and handles what each of those
ends up solving on its own:

| Behaviour                                         | How                  |
| ------------------------------------------------- | -------------------- |
| Hide while the form it points at is on screen     | `hideOver`           |
| Stay off the hero until the hero CTA scrolls away | `showAfter`          |
| Get out from under the Echo launcher              | automatic            |
| Ride out the iOS toolbar collapsing               | `--sk-vv-layout-gap` |
| Get out of the way of the keyboard                | `hideWhileTyping`    |
| Tell Sonor which action converts                  | `cta_click` event    |

## Use it

```tsx
// app/layout.tsx (a server component)
import Link from 'next/link'
import { Phone, ClipboardCheck } from 'lucide-react'
import { CtaBar, CtaBarAction } from '@sonordev/site-kit/cta-bar'

<SiteKitLayout>
  <Header />
  <main>{children}</main>
  <Footer />
  <CtaBar label="Call or request a quote" hideOver="#quote">
    <CtaBarAction href="tel:+15555550100" variant="secondary" icon={<Phone />}>
      Call now
    </CtaBarAction>
    <CtaBarAction as={Link} href="/quote" icon={<ClipboardCheck />}>
      Free quote
    </CtaBarAction>
  </CtaBar>
</SiteKitLayout>
```

Rules:

- **Render it once per page:** at the layout root for a site-wide bar, or
  inside the page for a page-specific one. It's a labelled `<aside>`, valid
  at either depth; the kit's axe gate covers both placements.
- **Never inside a blurred header.** `backdrop-filter` on an ancestor becomes
  the containing block for this fixed bar and clips it.
- **Delete the site's own bar and its compensating `padding-bottom`.** The
  kit renders a spacer that reserves the bar's height at the end of the page.
- **Better: pad the footer instead of adding a strip after it.** A spacer
  after a dark footer is a blank band in the page colour. Pass
  `spacer={false}` and let the footer's own background run under the bar:

  ```css
  footer { padding-bottom: calc(2rem + var(--sk-cta-bar-space, 0px)); }
  ```

  `--sk-cta-bar-space` is set on `<html>` only while a bar is present and
  below its breakpoint, so desktop and bar-less pages get `0px`.
- **Delete any `--sk-echo-offset-bottom` rule written for the old bar.** The
  kit sets it while the bar is on screen.

`CtaBar` and `CtaBarAction` are plain components with no hooks, so a server
layout can pass `as={Link}` and Link children without crossing a client
boundary. The only client code is a childless behaviour island the bar
mounts itself (about 4.4 KB gzipped for the whole module, glass and
analytics included).

## `<CtaBar>`

| Prop              | Default           |                                                                                                                                                                            |
| ----------------- | ----------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `label`           | `"Quick actions"` | Accessible name of the landmark.                                                                                                                                           |
| `breakpoint`      | `"lg"`            | Hidden at this width and up: `sm` 640, `md` 768, `lg` 1024, `xl` 1280, `none` = every width.                                                                               |
| `layout`          | `"fill"`          | `fill` stretches actions across the capsule. `fit` hugs a single action in a centred capsule (the old floating "Request a quote" button).                                  |
| `showAfter`       |                   | Selector. Hidden until that element scrolls off the top (the hero CTA). Server-rendered hidden, so it never slides in over the hero during hydration. No match = shown.    |
| `hideOver`        |                   | Selector or selectors. Steps aside while any match is on screen: the form it points at, the footer.                                                                        |
| `hideWhileTyping` | `true`            | Hidden while a text field has focus, so it never sits on the keyboard.                                                                                                     |
| `compactOnScroll` | `true`            | Tightens while scrolling down; an icon-bearing secondary action folds to its icon. Scrolling up restores it.                                                               |
| `echoClearance`   | `true`            | Lifts the Echo launcher above the bar while the bar is on screen, below the breakpoint only.                                                                               |
| `spacer`          | `true`            | Reserves the bar's height at the end of the page.                                                                                                                          |
| `track`           | `true`            | Sends `cta_click` (`category: engagement`, `label`, `properties.href`, `properties.variant`, `properties.location = "cta_bar"`) through the standalone analytics dispatch. |

Client navigation re-finds `showAfter` and `hideOver` targets on each route.

## `<CtaBarAction>`

| Prop       | Default                           |                                                                                                                                                                                                                                                              |
| ---------- | --------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `as`       | `a` with an `href`, else `button` | Any element or component, e.g. Next's `Link`. Polymorphic since 6.1.3: the action then takes that component's own props (required ones included), so `<CtaBarAction as={ScheduleTourButton} values={unit}>` type-checks and a missing required prop doesn't. |
| `variant`  | `"primary"`                       | `primary`: solid brand. `secondary`: tinted glass. `plain`: layout only, bring your own classes.                                                                                                                                                             |
| `icon`     |                                   | Leading icon, hidden from assistive tech. Lucide icons are sized to 18px.                                                                                                                                                                                    |
| `collapse` | secondary + icon                  | Fold to icon-only while compact. The label stays in the accessible name.                                                                                                                                                                                     |

Everything else (`href`, `onClick`, `target`, `aria-*`, `data-*`) passes
through. A `button` without an explicit `type` gets `type="button"`.

## Theme it

Colours and geometry are custom properties. Set them on `:root` so the bar,
its spacer and the Echo clearance all read the same values.

```css
:root {
  --sk-cta-primary-bg: var(--brand-primary);   /* default: --sk-primary, then #2563eb */
  --sk-cta-primary-text: #fff;
  --sk-cta-bar-text: var(--ink-900);           /* secondary label colour */
}
```

**A dark bar: scope the tint to the bar, not `:root`.** The Echo window
reads the same `--sk-glass-tint`, and its text follows `--sk-text-primary`
(dark by default). A dark tint on `:root` puts that dark text on dark glass.
Either scope the bar's colours to the bar:

```css
.sk-cta-bar {
  --sk-glass-tint: #0b0a09;
  --sk-cta-bar-text: #fff;
  --sk-cta-primary-bg: #fff;
  --sk-cta-primary-text: #0b0a09;
}
```

or theme the whole kit dark with `--sk-bg` and `--sk-text-primary` on
`:root`, which both surfaces (and site-kit forms) read.

| Token                                               | Default                                                                                                     |
| --------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- |
| `--sk-cta-bar-inset`                                | `12px` from the screen edges                                                                                |
| `--sk-cta-bar-padding`                              | `6px` between glass and buttons                                                                             |
| `--sk-cta-bar-action-height`                        | `48px` (44px tap target minimum; don't go lower)                                                            |
| `--sk-cta-bar-max-width`                            | `520px`                                                                                                     |
| `--sk-cta-bar-z`                                    | `40`                                                                                                        |
| `--sk-cta-bar-text`                                 | `--sk-text-primary`, then `#111827`                                                                         |
| `--sk-cta-primary-bg` / `--sk-cta-primary-text`     | `--sk-primary` / `#fff`                                                                                     |
| `--sk-cta-secondary-bg` / `--sk-cta-secondary-text` | 8% of the text colour / the text colour                                                                     |
| `--sk-glass-*`                                      | the shared glass recipe (`--sk-glass-tint`, `--sk-glass-opacity`, `--sk-glass-blur`, `--sk-glass-saturate`) |

## The glass

The surface is the kit's one Liquid Glass recipe (`src/shared/glass.tsx`),
the same material as the Echo launcher and chat window, so the bar and the
chat always match. The tint is mostly opaque (72%): the tint carries the
contrast, and the blur only softens what shows through. Browsers without
`backdrop-filter`, and visitors who ask their OS for reduced transparency
or more contrast, get the same surface solid. Reduced motion turns the
transitions off. JS-off visitors get the bar, visible, through
`@media (scripting: none)`.

On phones where the layout viewport runs taller than the visible one
(in-app browsers, toolbar animations), mount `VisualViewportGap` from
`@sonordev/site-kit/client`; the bar already adds `--sk-vv-layout-gap` to
its `bottom`.
