WP Manifestindependent plugin directory
manifest / analytics / ask-first-analytics

Ask First Analytics releasesself-updates

Wordpress Plugin to empower site users to consent first to GA4 analytics

by Jay Karr · github.com/jaykarr/ask-first-analytics · website

0stars
3release downloads
0forks

Install

The author publishes release zips, so WP-CLI can install straight from GitHub:

wp plugin install https://github.com/jaykarr/ask-first-analytics/releases/download/v1.2.1/ask-first-analytics-1.2.1.zip

Ships its own WordPress updater (Plugin Update Checker), so new versions show up under Dashboard → Updates.

Readme

Ask First Analytics

Consent-first Google Analytics 4 for WordPress.

Ask First Analytics keeps the Google tag out of the initial document, asks visitors before optional analytics loads, stores the choice only in that browser's first-party storage, and suppresses analytics on configured sensitive pages.

It does not claim legal compliance. Each site owner remains responsible for provider configuration, sensitive-page classification, public policy language, and release testing.

Screenshots

Consent prompt Preference panel
Consent prompt Preference panel
Admin settings Content inventory
Admin settings Content inventory

Why Ask First Analytics?

Most consent plugins are designed for advertising networks and retargeting — they manage dozens of cookie categories, load external vendor scripts, and add visual complexity. Ask First Analytics is different:

Ask First Analytics Typical consent plugins
Purpose Ethical site analytics only Multi-vendor ad/tracking consent
External requests before consent Zero Vendor scripts load immediately
Storage Browser localStorage only Cookies + vendor cloud
Dependencies None jQuery, vendor SDK, external CSS
Consent categories One: optional analytics Dozens: necessary, functional, performance, targeting…
Configuration One measurement ID Cookie audits, vendor mapping, legal templates
Sensitive page protection Built-in Not available
WP Consent API Built-in (provider + listener) Varies
Designed for Therapists, healthcare, any privacy-focused site E-commerce, advertising-driven sites

If you run a therapy practice, healthcare office, or any site where visitor privacy is paramount and you just want to understand which pages people visit — this plugin is built for you.

Requirements

  • WordPress 6.8 or newer
  • PHP 8.1 or newer
  • A GA4 web-stream measurement ID beginning with G-
  • (Optional) WP Consent API for interoperability with other consent-aware plugins

Install

  1. Inventory every existing source of Google Analytics or Google Tag Manager code.
  2. Install the versioned ZIP through Plugins -> Add Plugin -> Upload Plugin.
  3. Activate Ask First Analytics.
  4. Open Settings -> Analytics Privacy.
  5. Add the site's GA4 measurement ID and review the automatically suggested content exclusions.
  6. Add the persistent control to the site's footer.
  7. Disable every other tag source. If Site Kit is connected, turn off Place Google Analytics code while leaving reporting connected.
  8. Run the rendered-page check and resolve any competing tag evidence.
  9. Enable the plugin's analytics switch.
  10. Purge page and CDN caches.
  11. Run the unknown, denied, granted, sensitive-page, and withdrawal tests in a private browser before production use.

Persistent control

In a block theme, add the Analytics privacy choices block to a footer or other persistent template part. Its sidebar settings provide a label and button-or-link presentation without code.

The equivalent shortcode is:

[ask_first_analytics_privacy_choices]

Themes may render the same button directly:

<?php
if (function_exists('ask_first_analytics_render_privacy_choices_button')) {
    ask_first_analytics_render_privacy_choices_button([
        'label'   => 'Cookie preferences',
        'class'   => 'site-footer__privacy-link',
        'element' => 'a',
    ]);
}
?>

The function echoes by default and also returns the generated HTML. Pass 'echo' => false to return without echoing. Supported arguments are label, class (a string or array), element (button or a), and an optional anchor href. Existing no-argument calls remain compatible. The final markup passes through ask_first_analytics_launcher_html with the normalized arguments as the second filter parameter.

If analytics is configured and the theme does not place a control, the plugin adds a fixed fallback button at wp_footer. Its corner is selectable under Visitor experience and filterable per site:

add_filter(
    'ask_first_analytics_fallback_position',
    static fn (): string => 'bottom-left'
);

The initial prompt defaults to a full-width bottom banner (banner) that reserves page space. Alternate floating card layouts (card-right, card-center, card-left) float above the page without altering the page layout. Preferences always open in the centered panel.

Sensitive pages

Analytics is always disabled for:

  • administration, REST, feeds, previews, error pages, and other non-public requests;
  • logged-in users when that setting is enabled;
  • configured page IDs, path prefixes, post types, and block names;
  • content marked Sensitive page: disable analytics; and
  • requests denied by the site filter.

The filter is deny-only:

add_filter(
    'ask_first_analytics_allowed',
    static function (bool $allowed, array $context): bool {
        if (str_starts_with($context['path'], '/private-area')) {
            return false;
        }

        return $allowed;
    },
    10,
    2
);

It cannot override a built-in denial.

Theme design system

The plugin's stylesheet lives in the ask-first-analytics cascade layer. Normal, unlayered site CSS therefore wins without specificity escalation. Only visibility, fixed positioning, stacking, and visually hidden live-status rules retain deliberate !important safeguards.

The plugin ships accessible neutral styles and inherits the active theme's typography. Map a theme's semantic tokens through --afa-* custom properties:

:root {
  --afa-font-body: var(--site-font-body, inherit);
  --afa-font-heading: var(--site-font-display, inherit);
  --afa-text: var(--site-text, #1d2327);
  --afa-muted: var(--site-muted, #50575e);
  --afa-surface: var(--site-surface, #fffdf8);
  --afa-border: var(--site-border, #8c8f94);
  --afa-link: var(--site-link, #135e96);
  --afa-primary-bg: var(--site-action, #1d2327);
  --afa-primary-text: var(--site-on-action, #ffffff);
  --afa-focus: var(--site-focus, #2271b1);
  --afa-radius: var(--site-radius, 0.375rem);
  --afa-shadow: var(--site-shadow, 0 0.75rem 2.5rem rgb(0 0 0 / 18%));
  --afa-prompt-shadow: var(--site-prompt-shadow, 0 -0.5rem 1.5rem rgb(0 0 0 / 12%));
  --afa-control-height: 2.75rem;
  --afa-control-padding: 0.75rem 1rem;
  --afa-control-font-size: 0.9375rem;
  --afa-launcher-height: var(--afa-control-height);
  --afa-launcher-padding: var(--afa-control-padding);
  --afa-launcher-font-size: var(--afa-control-font-size);
}

Use the --afa-control-* values for all controls, then override only the --afa-launcher-* subset when a footer launcher needs different physical dimensions. Use --afa-prompt-shadow to customize top-edge elevation on full-width banners. The theme owns appearance only. Do not hide either choice or change the component's behavior.

[!NOTE] Cascade Layers & Theme Typography Resets: Because the plugin stylesheet lives in @layer ask-first-analytics, any unlayered CSS rules in active themes win over layered rules regardless of selector specificity. Consumer themes with global unlayered tag selectors (e.g. h1, h2, p { color: ... }) will override the plugin's --afa-text/--afa-muted color mapping on bare heading or paragraph elements inside the prompt unless explicitly scoped or re-asserted on .ask-first-analytics-root.

Stable styling parts

data-afa-component and data-afa-part are the semver-stable integration selectors. Class names are implementation details and may change in a major release. Current parts are:

  • [data-afa-component="prompt"], preferences, and launcher for complete components;
  • [data-afa-part="title"], description, actions, choice, policy, status, and launcher for styling component regions.

Site adapter pattern

Keep brand and site policy outside the reusable plugin. A minimal adapter enqueues unlayered token mappings and adds only reviewed site-specific denials:

add_action('wp_enqueue_scripts', static function (): void {
    $path = get_stylesheet_directory() . '/ask-first-analytics.css';

    if (file_exists($path)) {
        wp_enqueue_style(
            'site-ask-first-analytics',
            get_stylesheet_directory_uri() . '/ask-first-analytics.css',
            ['ask-first-analytics-consent'],
            (string) filemtime($path)
        );
    }
}, 110);

add_filter(
    'ask_first_analytics_allowed',
    static function (bool $allowed, array $context): bool {
        if (is_page_template('template-intake.php') || is_page('client-portal')) {
            return false;
        }

        return $allowed;
    },
    10,
    2
);

The companion CSS should primarily map tokens. Unlayered selector overrides are appropriate for site-specific typography or interaction details:

:root {
  --afa-font-body: var(--site-font-body, system-ui, sans-serif);
  --afa-surface: var(--site-surface, #fff);
  --afa-primary-bg: var(--site-action, #1d2327);
  --afa-primary-text: var(--site-on-action, #fff);
}

.site-footer {
  --afa-launcher-height: 2.25rem;
  --afa-launcher-padding: 0.625rem 1.125rem;
  --afa-launcher-font-size: 0.6875rem;
}

Hooks and filters

Hook Type Purpose
ask_first_analytics_allowed Filter Deny-only page policy extension. Receives bool $allowed and array $context. Cannot override built-in denials.
ask_first_analytics_launcher_html Filter Modify the final launcher button/link markup. Receives string $html and array $args.
ask_first_analytics_consent_config Filter Modify the JSON config passed to the browser script. Receives array $config, array $settings, array $decision.
ask_first_analytics_settings_defaults Filter Override default settings from a must-use plugin or managed host. Receives array $defaults.
ask_first_analytics_footer_priority Filter Change the wp_footer priority for the consent component. Default 1. Also configurable via the ASK_FIRST_ANALYTICS_FOOTER_PRIORITY constant.
ask_first_analytics_fallback_position Filter Change the fallback launcher corner. Returns bottom-left or bottom-right.

Theme adapter examples

The examples/ directory contains complete adapter templates for three common WordPress setups:

Directory Theme type Approach
examples/theme-adapter-block/ Block themes (Twenty Twenty-Five, etc.) Maps --wp--preset--* tokens
examples/theme-adapter-classic/ Classic PHP themes Direct hex/font-family values
examples/theme-adapter-builder/ Divi, Elementor, Beaver Builder Adds defensive rules for builder CSS resets

Each adapter includes a README.md with step-by-step setup instructions and an adapter.css you can copy into your child theme. The adapter pattern keeps brand and site-policy decisions out of the reusable plugin.

Data behavior

  • Consent key: ask_first_analytics_consent
  • Consent stays in localStorage on the current browser origin.
  • No consent choice is posted to WordPress or a consent vendor.
  • Unknown, malformed, unavailable, or outdated storage blocks analytics.
  • The consent generation advances automatically after material changes to the analytics destination, visitor wording, policy, or eligibility rules.
  • The Google tag is not present in initial HTML.
  • Basic Consent Mode v2 grants only analytics_storage; all advertising purposes remain denied.
  • Automatic page views and Google Signals are disabled.
  • The plugin sends one reviewed page_view containing title, origin plus path, and a minimized referrer. Query strings and fragments are removed.
  • Withdrawal disables the measurement ID, updates consent when possible, removes accessible _ga cookies, and reloads into a tag-free document.

Development

From the repository root:

php tests/page-policy-test.php
php tests/admin-contract-test.php
php tests/launcher-api-test.php
bash bin/build-zip.sh

The dependency-free PHP suite exercises the server-side deny policy without changing a WordPress site. A separate staging smoke test should confirm one approved view in GA4 Realtime.

Changelog

1.2.0

  • Moved public component CSS into a cascade layer and removed blanket armor
  • Added shared control and launcher-specific physical-dimension tokens
  • Added a configurable launcher API with link, label, class, return, and filter support
  • Added bottom-banner and corner-card prompt layouts plus fallback-corner controls
  • Added a dynamic Privacy Choices block for block-theme footers
  • Documented stable component parts and the reusable site-adapter pattern
  • Added extensibility filters for consent config, settings defaults, and footer priority
  • Added generic theme adapter examples for block, classic, and builder themes

1.1.0

  • Added a four-tab administration screen with an at-a-glance readiness summary
  • Added read-only Site Kit configuration detection and same-origin HTML checks
  • Added a searchable content inventory with explainable exclusion suggestions
  • Automated consent-generation renewal after material configuration changes
  • Prevented enabling the loader while known duplicate-tag blockers remain

1.0.0

  • Initial consent-first GA4 loader
  • Versioned browser-local preference
  • Sensitive-page policy and editor override
  • Accessible prompt, preference panel, shortcode, and fallback control
  • Theme-token adapter contract
  • Deterministic browser and release-package tests

Read the full README on GitHub →

Releases

TagPublishedAssetDownloads
v1.2.1 Aug 8, 2026 ask-first-analytics-1.2.1.zip.sha256 0
v1.2.1 Aug 8, 2026 ask-first-analytics-1.2.1.zip 1
v1.2.0 Aug 8, 2026 ask-first-analytics-1.2.0.zip 2
v1.2.0 Aug 8, 2026 ask-first-analytics-1.2.0.zip.sha256 0