docs
    site-kit: CTA bar
    v7.2.0.md

    @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:

    BehaviourHow
    Hide while the form it points at is on screenhideOver
    Stay off the hero until the hero CTA scrolls awayshowAfter
    Get out from under the Echo launcherautomatic
    Ride out the iOS toolbar collapsing--sk-vv-layout-gap
    Get out of the way of the keyboardhideWhileTyping
    Tell Sonor which action convertscta_click event

    Use it

    // 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:

      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>

    PropDefault
    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).
    showAfterSelector. 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.
    hideOverSelector or selectors. Steps aside while any match is on screen: the form it points at, the footer.
    hideWhileTypingtrueHidden while a text field has focus, so it never sits on the keyboard.
    compactOnScrolltrueTightens while scrolling down; an icon-bearing secondary action folds to its icon. Scrolling up restores it.
    echoClearancetrueLifts the Echo launcher above the bar while the bar is on screen, below the breakpoint only.
    spacertrueReserves the bar's height at the end of the page.
    tracktrueSends 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>

    PropDefault
    asa with an href, else buttonAny 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.
    iconLeading icon, hidden from assistive tech. Lucide icons are sized to 18px.
    collapsesecondary + iconFold 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.

    :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:

    .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.

    TokenDefault
    --sk-cta-bar-inset12px from the screen edges
    --sk-cta-bar-padding6px between glass and buttons
    --sk-cta-bar-action-height48px (44px tap target minimum; don't go lower)
    --sk-cta-bar-max-width520px
    --sk-cta-bar-z40
    --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-text8% 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.