slop audit
Blog post 42 / Content and user flows

Website CTA checklist: test labels and destinations

Use this website CTA checklist to test call-to-action labels, destinations, accessibility, mobile behavior, and safe first-task paths before launch.

By Slop Audit · 10 min read

A website CTA checklist should prove that a visitor can understand and complete the page's intended next step. Before launch, test the rendered call to action as a path: its visible label, HTML semantics, accessible name, destination or state change, mobile presentation, and result. A polished button that says “Learn more” and points to # is still a broken primary task.

This guide gives developers a bounded CTA testing workflow for public pages. It focuses on evidence you can collect safely before release, while keeping production purchases, form submissions, authentication, and other side effects out of an automated audit.

Website CTA checklist

Run these checks against the deployed page and its important responsive states:

  • Name one primary task for the page.
  • Give the CTA a specific label that describes the next step.
  • Use a link for navigation and a button for an in-page command.
  • Confirm the destination or state-change target exists.
  • Check the computed accessible name, keyboard path, focus state, and tap target.
  • Test mobile and desktop layouts after scripts and content load.
  • Verify safe routes without submitting forms, buying, signing in, or downloading files.
  • Save the expected result in CI and rerun it after release.
  • Measure clicks and completed outcomes separately from functional QA.

The checklist begins with clarity because users need to predict what an action will do. WCAG's link-purpose guidance says a link's purpose should be determinable from its text or programmatically associated context. That makes a specific label useful for everyone and especially important when someone navigates a page as a list of links.

Define the primary task before testing the CTA

Write the expected path as one sentence before changing button copy:

From the pricing page, a visitor can choose the Pro plan, reach checkout, and understand what happens next.

This sentence gives the test a start, an action, and an observable result. It also exposes page-level confusion. If the same page treats “Start free,” “Book a demo,” “View pricing,” and “Contact sales” as equally primary, a test cannot decide which path represents success.

Secondary actions are valid, but label the hierarchy intentionally. A documentation link can support a primary “Start a scan” action without competing with it. For broader copy review, use the website content audit checklist to align the headline, audience, proof, and next step before you test the control itself.

Do not infer importance from color alone. In code and tests, identify the primary action through a stable contract such as its accessible role and name. A class like primary-action can also provide audit context, but it should not be the only way a user can distinguish the control.

Write a call-to-action label that names the outcome

Start with a verb, then add the object or result. The second part is what turns a generic command into a useful instruction.

  • “Start a website scan” is clearer than “Get started.”
  • “View the audit report” is clearer than “View more.”
  • “Download the launch checklist” is clearer than “Download.”
  • “Request project access” is clearer than “Submit.”

Avoid changing every CTA into promotional copy. The goal is prediction, not hype. “Create free account” is useful only if the destination actually creates or begins creating an account under the stated terms.

Visible text should normally supply the accessible name. The W3C's accessible-name practice recommends visible labels and native HTML where possible because they are easier to understand and maintain. If an aria-label replaces visible button text with a different promise, sighted and screen-reader users may receive conflicting instructions.

<!-- Too vague: neither the result nor destination is clear. -->
<a class="primary-action" href="#">Learn more</a>

<!-- Specific: the label and route describe the same next step. -->
<a class="primary-action" href="/reports/example">
  View an example audit report
</a>

Repeat the same label for the same destination unless the surrounding context genuinely changes its purpose. If two links have different destinations, give them labels that make the difference apparent.

Use links for destinations and buttons for commands

A visual button can be either a link or a button in HTML. Choose the element from behavior, not appearance:

  • Use <a href="…"> when activation navigates to another URL or document.
  • Use <button type="button"> when activation changes the current interface.
  • Use <button type="submit"> when activation submits a form.

The HTML Standard defines a button as a command, with its type controlling submit, reset, or button behavior. Styling an anchor and a button to look alike does not make their browser or accessibility behavior interchangeable.

Always set the button type. A <button> inside a form can submit by default, turning a harmless interface control into a side effect.

<a href="/pricing">Compare plans</a>

<button
  type="button"
  aria-expanded="false"
  aria-controls="pricing-details"
>
  Show plan details
</button>

<section id="pricing-details" hidden>
  <!-- The button reveals this content in place. -->
</section>

<script>
  const trigger = document.querySelector('[aria-controls="pricing-details"]');
  const details = document.querySelector("#pricing-details");

  trigger.addEventListener("click", () => {
    const expanded = trigger.getAttribute("aria-expanded") === "true";
    trigger.setAttribute("aria-expanded", String(!expanded));
    details.hidden = expanded;
  });
</script>

For an in-page command, expose a testable relationship such as aria-controls, a native popover target, or a dialog association. A click handler with no declared target is harder to audit safely because a crawler cannot know whether activating it opens help text or deletes data.

Test the CTA destination and resulting state

Inspect the raw destination before clicking anything. Placeholder hashes, empty href values, javascript: URLs, missing fragment IDs, and routes that return errors should fail release QA.

Classify the action so the test stays safe:

  • Safe navigation: A known public HTTP(S) route intended for a read-only request.
  • Safe in-page action: A fragment, disclosure, popover, tab, or dialog with an observable target and no server write.
  • Side-effecting action: A form submission, checkout, sign-in, download, email, telephone link, destructive command, or arbitrary script.

Automated public-page checks can inspect the first two categories. Test side effects in a controlled environment with dedicated accounts, fixtures, and cleanup. Do not make a production audit prove a purchase by placing a real order.

For links, verify more than the existence of an href. Resolve relative URLs against the final page URL, follow the expected redirect policy, and confirm that the final route returns the intended content. The broken-link testing guide covers response classification, redirects, fragments, and CI evidence in more detail.

For in-page actions, assert the state transition. A disclosure should update aria-expanded and reveal its controlled region. A fragment should match a rendered element. A modal trigger should open the intended dialog and provide a working close path.

Check accessibility, keyboard use, and mobile layout

CTA button testing is incomplete when it only checks a mouse click at desktop width. Review the rendered control as a user encounters it:

  • The accessible role matches the behavior.
  • The accessible name includes the visible label.
  • Tab reaches the control in a logical order.
  • Focus remains visible before and after activation.
  • Enter activates a link; Enter or Space activates a button.
  • The control is not disabled or covered by another element.
  • The label wraps without clipping at a narrow viewport.
  • The target remains large enough to activate without hitting a neighboring control.
  • Sticky banners or chat widgets do not cover the CTA.

Use the accessible name testing guide when an icon, aria-label, or aria-labelledby contributes to the name. Then include the result in website regression testing so a later component or layout change cannot silently remove the path.

Add a Playwright CTA test without forcing the click

Playwright's role locators use the role and accessible name that users and assistive technology perceive. That makes them a stronger contract for a CTA than a generated CSS class.

import { test, expect } from "@playwright/test";

test("primary CTA exposes the report destination", async ({ page }) => {
  await page.goto("https://example.com/", { waitUntil: "networkidle" });

  const cta = page.getByRole("link", {
    name: "View an example audit report",
  });

  await expect(cta).toBeVisible();
  await expect(cta).toHaveAccessibleName("View an example audit report");
  await expect(cta).toHaveAttribute("href", "/reports/example");

  await cta.focus();
  await expect(cta).toBeFocused();
});

This test proves the rendered label, role, destination contract, and keyboard focus without causing a server-side action. Add a separate navigation assertion only when the route is known to be safe in that environment.

For a disclosure, test the declared state change:

test("plan-details CTA reveals its controlled region", async ({ page }) => {
  await page.goto("https://example.com/pricing");

  const cta = page.getByRole("button", { name: "Show plan details" });
  const details = page.locator("#pricing-details");

  await expect(cta).toHaveAttribute("aria-expanded", "false");
  await cta.click();
  await expect(cta).toHaveAttribute("aria-expanded", "true");
  await expect(details).toBeVisible();
});

Do not add { force: true } to make a failing test green. Playwright's actionability checks verify conditions such as visibility, stability, event reception, and enabled state before a click. Bypassing them can hide the overlay, animation, or disabled-state defect that a real user would hit.

Separate functional CTA testing from conversion experiments

Release QA and conversion optimization answer different questions.

  • Functional testing asks whether the action is understandable, reachable, operable, and connected to the expected result.
  • Analytics asks how often people start and complete the path.
  • An A/B test asks which reviewed variant performs better for a defined audience and outcome.

Run them in that order. Click-through data cannot rescue a destination that returns an error, and a working link does not prove that its message is persuasive. Track the complete outcome rather than treating a click as a conversion when the task finishes after a form, signup, or checkout.

Keep experiment variants inside the same release contract. Each variant still needs a specific label, an accessible name, a valid destination, and safe behavior at supported viewports.

Run the website CTA checklist with Slop Audit

Run a Slop Audit against the public URL after deployment. Its bounded checks select a primary rendered link or button, record why it was selected, judge whether the label names a concrete next step, and inspect its declared destination or safe in-page target. For a safe public link, the report can record whether the route probe was reachable; placeholder hashes and missing fragment targets become reviewable evidence.

Slop Audit does not submit forms, enter checkout, authenticate, trigger downloads, or invoke arbitrary button scripts. Those actions are marked as not tested when a safe result cannot be observed without risking a side effect. It also does not prove conversion performance or certify accessibility. Use the report to catch a broken first task, then complete the controlled flow and human review for your release.

Website CTA checklist release sign-off

  • The page has one named primary task and an intentional secondary-action hierarchy.
  • The primary label states a concrete action and result.
  • Navigation uses links; in-page commands and submissions use the correct button type.
  • The destination, fragment, or controlled region exists and produces the expected result.
  • The accessible name agrees with the visible label.
  • Keyboard focus, activation, mobile layout, target size, and overlays are reviewed.
  • Automated checks avoid production side effects and save reproducible evidence.
  • Analytics measures the completed task separately from the click.
  • The deployed URL is rescanned after fixes and covered by regression checks.

A reliable CTA is not merely visible. It tells the visitor what happens next, exposes the right browser semantics, and reaches a result that the team can verify before launch.