webdevpuneet.com

Hero with Audience Toggle Switcher — Free HTML CSS JS

Hero with Audience Toggle Switcher · Heroes · Plain HTML, CSS & JS · Live preview

CategoryHeroes

Copy & paste

Source Code

<section class="ats-hero" data-audience="dev">
  <div class="ats-bg" aria-hidden="true"><span class="ats-orb ats-orb-a"></span><span class="ats-orb ats-orb-b"></span><span class="ats-grid"></span></div>

  <div class="ats-toggle" id="atsToggle" role="tablist" aria-label="Choose your audience">
    <span class="ats-thumb" aria-hidden="true"></span>
    <button type="button" class="ats-opt active" id="ats-tab-dev" role="tab" aria-selected="true" aria-controls="atsPanel" data-audience="dev" tabindex="0">
      <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m16 18 6-6-6-6"/><path d="m8 6-6 6 6 6"/></svg>Developers
    </button>
    <button type="button" class="ats-opt" id="ats-tab-team" role="tab" aria-selected="false" aria-controls="atsPanel" data-audience="team" tabindex="-1">
      <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M22 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/></svg>Teams
    </button>
    <button type="button" class="ats-opt" id="ats-tab-enterprise" role="tab" aria-selected="false" aria-controls="atsPanel" data-audience="enterprise" tabindex="-1">
      <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/><path d="m9 12 2 2 4-4"/></svg>Enterprise
    </button>
  </div>

  <div class="ats-panel" id="atsPanel" role="tabpanel" aria-labelledby="ats-tab-dev">
    <div class="ats-copy">
      <span class="ats-eyebrow ats-sw" id="atsEyebrow" style="--i:0"><i class="ats-dot"></i><span id="atsEyebrowText">Ship faster, break less</span></span>
      <h1 class="ats-h1 ats-sw" id="atsH1" style="--i:1">An API that gets<br><em>out of your way</em></h1>
      <p class="ats-sub ats-sw" id="atsSub" style="--i:2">Type-safe SDKs, predictable errors and docs that match the code. Go from first request to production in an afternoon, not a sprint.</p>
      <div class="ats-ctas ats-sw" id="atsCtas" style="--i:3">
        <a href="#" class="ats-cta-primary" id="atsCtaPrimary"><span id="atsCtaPrimaryText">Read the docs</span><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M5 12h14"/><path d="m13 6 6 6-6 6"/></svg></a>
        <a href="#" class="ats-cta-secondary" id="atsCtaSecondary">View on GitHub</a>
      </div>
      <ul class="ats-proof ats-sw" id="atsProof" style="--i:4">
        <li>TypeScript, Python and Go SDKs</li><li>p99 latency under 80 ms</li><li>OpenAPI-first docs</li>
      </ul>
    </div>

    <div class="ats-visual" id="atsVisual">
      <div class="ats-card on" data-for="dev">
        <div class="ats-win">
          <div class="ats-bar"><i></i><i></i><i></i><span class="ats-file">checkout.ts</span></div>
          <pre class="ats-code"><code><span class="k">import</span> { Acme } <span class="k">from</span> <span class="s">"@acme/sdk"</span>;

<span class="k">const</span> acme = <span class="k">new</span> <span class="f">Acme</span>({ apiKey: process.env.<span class="p">ACME_KEY</span> });

<span class="k">const</span> order = <span class="k">await</span> acme.orders.<span class="f">create</span>({
  customer: <span class="s">"cus_9f2a"</span>,
  items: [{ sku: <span class="s">"tee-black-m"</span>, qty: <span class="n">2</span> }],
});

<span class="c">// order.status is typed: "paid" | "pending"</span>
console.<span class="f">log</span>(order.status);</code></pre>
          <div class="ats-resp"><span class="ats-ok"></span>201 Created<i>38 ms</i></div>
        </div>
        <div class="ats-float ats-float-a"><span class="ats-fdot"></span>Typed responses, zero guesswork</div>
      </div>

      <div class="ats-card" data-for="team">
        <div class="ats-win">
          <div class="ats-board-head">
            <div><strong>Q4 launch plan</strong><span>9 of 12 tasks done</span></div>
            <div class="ats-avatars"><b style="--c:#f472b6">MC</b><b style="--c:#60a5fa">JO</b><b style="--c:#fbbf24">AK</b></div>
          </div>
          <div class="ats-progress"><i style="width:75%"></i></div>
          <ul class="ats-tasks">
            <li><span class="ats-check done"></span><em>Finalise pricing page</em><b class="ats-pill ok">Done</b></li>
            <li><span class="ats-check done"></span><em>Write the release notes</em><b class="ats-pill ok">Done</b></li>
            <li><span class="ats-check"></span><em>QA sign-off on mobile</em><b class="ats-pill run">In review</b></li>
            <li><span class="ats-check"></span><em>Announce to customers</em><b class="ats-pill todo">Up next</b></li>
          </ul>
        </div>
        <div class="ats-float ats-float-a"><b class="ats-mini" style="--c:#f472b6">MC</b>Maya moved "Pricing page" to Done</div>
      </div>

      <div class="ats-card" data-for="enterprise">
        <div class="ats-win">
          <div class="ats-admin-head">
            <span class="ats-shield"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/><path d="m9 12 2 2 4-4"/></svg></span>
            <div><strong>Security and access</strong><span>Workspace policies</span></div>
            <b class="ats-pill ok">All checks passed</b>
          </div>
          <ul class="ats-rows">
            <li><div><strong>Single sign-on</strong><span>SAML 2.0 with SCIM provisioning</span></div><i class="ats-switch on"></i></li>
            <li><div><strong>Audit log</strong><span>Streams to your SIEM, 365-day retention</span></div><i class="ats-switch on"></i></li>
            <li><div><strong>Data residency</strong><span>Choose EU or US per workspace</span></div><i class="ats-switch on"></i></li>
            <li><div><strong>Role-based access</strong><span>Custom roles down to the project</span></div><i class="ats-switch on"></i></li>
          </ul>
        </div>
        <div class="ats-float ats-float-a"><span class="ats-fdot"></span>SOC 2 Type II and ISO 27001</div>
      </div>
    </div>
  </div>
</section>

Extend with AI

Take This Snippet Further With AI

The AI prompt for this one is for Patreon supporters ($3 a month, 7 days free), along with a site with no ads. Supporters enter the passcode once and it is remembered in this browser.

· Become a supporter

What's included

Features

Three audiences out of the box, and any number supported by the self-measuring thumb
Sliding pill toggle whose position and width are measured from the active tab
Whole-page recolour through registered @property colour variables that animate
A different product card per audience: code window, project board, security settings
Full copy object per audience: eyebrow, headline, subheading, both buttons, proof points
Fade-out, write, staggered fade-in so the text swap never flickers
Aurora background with blurred orbs and a masked grid that follow the palette
Roving-tabindex tab list with arrow keys, Home and End, plus aria-selected and aria-controls
Remembers the last choice in localStorage and opens ?audience=key links directly
Pointer tilt on the card for mouse users, with prefers-reduced-motion respected
Responsive: stacks and centres on tablets and phones
Export as HTML file, React JSX or React + Tailwind CSS

About this UI Snippet

Hero with Audience Toggle Switcher — One Hero, Three Messages

Screenshot of the Hero with Audience Toggle Switcher snippet rendered live

A single hero rarely speaks to every audience equally well. "Type-safe SDKs" reads as noise to a team lead, "one workspace, zero silos" undersells the product to an engineer, and neither answers the question a security reviewer asks first. Instead of building three landing pages, this hero lets visitors pick who they are with a sliding toggle, then rewrites the eyebrow, headline, subheading, both buttons and the proof points in place. It also swaps the card on the right, so a developer sees a code window, a team sees a project board and an enterprise buyer sees their security settings, and the whole page recolours to match.

A sliding thumb that measures itself

The toggle is a row of role="tab" buttons over one absolutely positioned .ats-thumb. On every selection, placeThumb() reads the active tab's offsetLeft and offsetWidth and writes them to two CSS variables, --x and --w; the thumb uses them in a translateX() and a width that are both transitioned with a springy cubic-bezier. Because the thumb is measured rather than assumed to be half the track, tabs can have labels of any length and there can be any number of them. Add a fourth .ats-opt and a fourth key in COPY and nothing else changes.

The whole palette glides, not just the button

The hero sets three custom properties, --a, --a2 and --t (two gradient colours and a light text accent), per data-audience value. They are registered with @property as <color> values, which is what lets a transition on --a interpolate the colour instead of snapping. The background orbs, the thumb, the primary button, the headline highlight, the eyebrow pill and the card glow all read those variables, so one attribute change retints everything together. In a browser without @property support the colours still change, just without the glide.

A full copy object per audience

The COPY object holds a complete set of strings per audience: eyebrow, headline, subheading, both button labels and a list of proof points. select(key) writes every field in one pass rather than swapping a word inside a fixed sentence, so each audience's messaging can be written, reviewed and changed by whoever owns it. The headline is the only field written with innerHTML, because it carries a deliberate <br> and an <em> for the gradient highlight; everything else uses textContent, and the proof list is built with createElement.

Fade out, write, fade in

Before new text is written, the copy elements get an .is-out class that fades them to opacity: 0 with a small upward shift and blur. A 180ms timeout, matched to that transition, waits for the fade to finish, writes the new strings and removes the class. Each element has its own --i index, so the way back in is staggered by 45ms per element. An instant swap would read as a flicker; this makes the change look deliberate.

One visual card per audience

The right-hand side holds three cards stacked in the same CSS grid cell with grid-area: 1 / 1, so the container is always as tall as the tallest card and nothing jumps. Only the card marked .on is visible; the others sit at opacity: 0 and visibility: hidden, with the visibility change delayed so the fade completes. A soft tilt follows the pointer on devices that can hover.

Keyboard and accessibility

The toggle is a proper tab list: aria-selected follows the active tab, aria-controls points at the panel, aria-labelledby on the panel is updated to the active tab, and tabindex roves so the arrow keys, Home and End move between tabs. Animations are switched off for visitors who prefer reduced motion, and the tilt is never enabled for them.

Remembering the choice and deep links

The last choice is saved to localStorage (inside a try/catch, since storage can throw in private browsing), and a link ending in ?audience=enterprise opens that tab first. That makes it easy to send each audience from an ad, an email or a docs page straight to its own version of the hero.

Step by step

How to Use

  1. 1
    Pick an audienceClick Developers, Teams or Enterprise, or focus the toggle and use the arrow keys. The copy, buttons, proof points, card and colours all change together.
  2. 2
    Write your own copyIn the JS panel, edit the strings inside the COPY object for each audience. The headline accepts a <br> and an <em> for the gradient highlight.
  3. 3
    Swap the visual cardsIn the HTML panel, edit the three .ats-card blocks (code window, project board, security settings) or replace them with a screenshot of your product.
  4. 4
    Change the paletteIn the CSS panel, change the --a, --a2 and --t values on .ats-hero[data-audience] to your brand colours for each audience.
  5. 5
    Add or remove an audienceAdd a key to COPY and ORDER, a matching tab button with a data-audience attribute, and a .ats-card with a data-for attribute. The thumb measures itself, so no CSS maths changes.
  6. 6
    Link straight to an audienceAdd ?audience=team (or any key from COPY) to the page address and that tab opens first. Without it, the last choice is restored from localStorage.
  7. 7
    Export in your formatUse Export for a standalone HTML file, a React component or a React + Tailwind version.

Real-world uses

Common Use Cases

Dev-tool and platform landing pages
Speak to the engineer evaluating the API and to the manager signing off on it, without maintaining two separate pages.
B2B SaaS with several buyers
Show the individual, the team lead and the security reviewer the part of the product each one cares about, from one URL.
Marketplaces: buyers and sellers
Swap the hero between "Start shopping" and "Start selling" with the same toggle, card swap and recolour.
Ad and email landing pages
Send each campaign to its audience with ?audience=team so the page opens already speaking to that visitor.
Learn the registered-property trick
Study how @property turns an instant custom-property change into a smooth colour transition across every element that uses it.
Pair with a stats row
Combine with the Hero with Animated Stat Counters for a hero that counts up and adapts to its audience.

Got questions?

Frequently Asked Questions

On every selection, placeThumb() reads the active tab's offsetLeft and offsetWidth and writes them to the --x and --w CSS variables on the toggle. The thumb applies them as a translateX and a width, both with a transition, so it fits any label and any number of tabs. It is measured again on resize and when the fonts finish loading.

The hero defines --a, --a2 and --t per data-audience value, and they are registered with @property as colour values. A registered property can be transitioned, so changing the attribute makes the browser interpolate the colour for every element that uses the variable. Without @property support the colours simply switch instantly.

An instant swap reads as a layout glitch. The copy elements get an .is-out class (opacity 0, a small shift and blur), a 180ms timeout matched to that transition waits, and then the strings are written and the class removed so they fade back in, one after another using their --i delay.

The headline copy holds a <br> for the line break and an <em> for the gradient highlight, which need markup. The eyebrow, subheading and buttons use textContent, and the proof list is built with createElement, which is the safer default when no markup is needed.

Add a key to the COPY object and to ORDER, a tab button with a matching data-audience in the toggle, and a .ats-card with data-for set to the same key. Add a [data-audience] block in the CSS for its colours. The thumb measures the active tab, so there is no width or translate maths to change.

Yes to both. The clicked key is saved to localStorage, and on load the page opens ?audience=key from the address if present, then the saved choice, then the first tab. Storage access is wrapped in try/catch, so private browsing falls back to the first tab.

Yes. Fire a custom event from select(), for example document.dispatchEvent(new CustomEvent("audiencechange", { detail: key })), and let a pricing table or testimonial section listen for it. The data-audience attribute on the hero is also available to your own CSS.