Skip to content

Enhance - Load frontend form styles only when a form is present - #1618

Open
rajatgautam755421 wants to merge 7 commits into
pre-developfrom
EVF-2651-conditional-frontend-styles
Open

Enhance - Load frontend form styles only when a form is present#1618
rajatgautam755421 wants to merge 7 commits into
pre-developfrom
EVF-2651-conditional-frontend-styles

Conversation

@rajatgautam755421

@rajatgautam755421 rajatgautam755421 commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

All Submissions:

Changes proposed in this Pull Request:

EVF_Frontend_Scripts::load_scripts() runs on every frontend request and enqueued the form stylesheets unconditionally, so everest-forms.css (~96 KB) and intlTelInput.css (~40 KB) were downloaded and parsed on every page — even pages with no form. Confirmed in the browser: both loaded on no-form pages, and intlTelInput.css loaded even on forms with no phone field. (Front-end scripts were already gated, so this is styles-only.)

The stylesheets are now loaded only when they are actually needed:

  • everest-forms.css — enqueued in <head> when the queried post contains the [everest_form] shortcode or the everest-forms/form-selector block (EVF_Frontend_Scripts::current_page_has_form()). For contexts the content scan cannot see (page builders, widgets, do_shortcode()), it is also enqueued on the everest_forms_shortcode_scripts render hook, which every form render path reaches through EVF_Shortcode_Form::output(). Both call the shared enqueue_frontend_styles(); wp_enqueue_style() is idempotent, so the overlap never double-loads.
  • intlTelInput.css — enqueued only when the rendered form has a phone field, mirroring the existing flatpickr/mailcheck field-gated pattern in output().
  • dashicons — a follow-up audit (see below) found this was still loading globally, undermining the point of the PR. Now gated behind the same current_page_has_form() check as everest-forms.css; it's only used by one rule (the form-selector block's notice icon), so it has no reason to load elsewhere.
  • everest-forms-no-js body class + its wp_footer inline script — the same audit found evf_body_class() hooked onto WordPress's global body_class filter, so this ran on every page of the site, not just form pages. Checked this repo and every Pro addon for anything that actually reads .everest-forms-no-js/.everest-forms-js — found zero consumers, so it looks vestigial, but it's now gated the same way regardless.
  • New everest_forms_has_form_on_page filter lets setups the scan cannot detect force the head load. Style registration is unchanged, so on-demand enqueues keep working.

Ref: EVF-2651
https://themegrill.atlassian.net/browse/EVF-2651

How to test the changes in this Pull Request:

  1. Open a frontend page that has no Everest Forms form. Confirm everest-forms.css, intlTelInput.css, and dashicons are not loaded, and the <body> has no everest-forms-no-js/everest-forms-js class or footer script.
  2. Open a page containing an [everest_form] shortcode or the form-selector block. Confirm everest-forms.css and dashicons load in <head>, the everest-forms-no-js body class and its footer script are present, and the form renders/styles correctly.
  3. Add a Phone field to that form. Confirm intlTelInput.css now loads and the country-flag dropdown is styled; remove the phone field and confirm intlTelInput.css is no longer loaded.
  4. Confirm the per-form Style Customizer stylesheet (everest-forms-style-{id}) still loads on form pages.

Types of changes:

  • Bug fix (non-breaking change which fixes an issue)
  • Enhancement (modification of the currently available functionality)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)

Other information:

  • Have you added an explanation of what your changes do and why you'd like us to include them?
  • Have you successfully ran tests with your changes locally?
  • Have you updated the documentation accordingly?

Verified locally (Playwright): no-form pages (styles absent), and shortcode / block / phone / repeater form pages (correct load, no FOUC — CSS lands in <head>), on both a classic theme (elearning) and a block/FSE theme (twentytwentyfour), plus the render-fallback path with content detection forced off.

Follow-up audit: did a full sweep of every wp_enqueue_style/wp_enqueue_script call reachable on the frontend to check for anything else loading in non-required places. Found and fixed two: dashicons and the everest-forms-no-js body class/footer script (both described above). Everything else — the shortcode's own script/style enqueues, the block-selector path, field-type asset loaders (select2/flatpickr/recaptcha/hcaptcha/turnstile), and the form-preview CSS/JS — was already correctly scoped (self-gated inside the shortcode callback, everest_forms_shortcode_scripts/field_display hooks, or the ?evf_preview query param). Verified anonymously (not just the authenticated session): a non-form page loads none of it; a real form page is unaffected.

Not covered — needs QA on a builder-equipped site: the page builders (Bricks, Elementor, Oxygen, Divi) are not installed on the test environment and were not exercised directly. Their frontend rendering goes through the same EVF_Shortcode_Form::output() fallback that was verified by simulation, but the builders' own editor previews still need checking. Related builder-side offenders (Bricks menu.css, Oxygen admin.css, and Divi JS loaded globally on the front end) are out of scope for this PR and left as follow-ups.

Changelog entry

Enhance - Load frontend styles (everest-forms.css, intlTelInput.css, dashicons) and the no-js body class/script only when a form is present instead of on every page.

…VF-2651)

everest-forms.css (~96 KB) and intlTelInput.css (~40 KB) were enqueued on every
frontend page via EVF_Frontend_Scripts::load_scripts(), regardless of whether a
form was on the page — confirmed in the browser: both downloaded on no-form pages,
and intlTelInput.css even loaded on forms with no phone field.

Load them conditionally instead:

- everest-forms.css: enqueued in <head> when the queried post contains the
  [everest_form] shortcode or the form-selector block (current_page_has_form()),
  and, as a guaranteed fallback for page-builder/widget/do_shortcode contexts the
  scan cannot see, on the everest_forms_shortcode_scripts render hook. Both paths
  call the shared enqueue_frontend_styles(); wp_enqueue_style is idempotent so the
  overlap never double-loads.
- intlTelInput.css: enqueued only when the rendered form has a phone field,
  mirroring the existing flatpickr/mailcheck field-gated pattern in output().
- dashicons: unchanged (still loaded globally).

The everest_forms_has_form_on_page filter lets setups the scan can't detect force
the head load. Style registration is unchanged so on-demand enqueues keep working
for every builder, which all render through EVF_Shortcode_Form::output().

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@rajatgautam755421 rajatgautam755421 self-assigned this Jul 17, 2026
@rajatgautam755421 rajatgautam755421 changed the title fix(assets): conditionally load frontend form styles (EVF-2651) Enhance - Load frontend form styles only when a form is present Jul 17, 2026
…elying on core hoisting

everest_forms_shortcode_scripts fires wherever the shortcode actually
renders, which for page builders (Bricks/Oxygen/Elementor store content
outside post_content), reusable blocks, and archive/listing pages is
always after wp_head has already flushed the style queue. wp_enqueue_style()
alone doesn't get picked up at that point: WordPress only hoists it into
<head> on 6.9+ for classic themes (verified: block themes never get the
hoist, on any core version), and older cores just flash it in unstyled at
the very end of the page. Printing the handle immediately once wp_head's
flush has already happened removes that dependency and starts the
stylesheet request right where the form renders instead.
@rajatgautam755421
rajatgautam755421 marked this pull request as ready for review August 17, 2026 09:15
actions/cache@v2 is deprecated and GitHub now hard-fails any job that
uses it before the job's own steps even run, so the sniff check never
reached its (already lenient, continue-on-error) phpcs step. Bumped to
actions/cache@v4.

composer.lock also had phpspec/prophecy, symfony/deprecation-contracts,
webmozart/assert, and phpdocumentor/reflection-docblock locked to
versions that require PHP 8.2+, breaking `composer install` on the
PHP 7.4 runner this workflow (and the QA deploy workflow) both target.
These are dev-only tooling deps, never shipped in the built plugin
(composer install --no-dev). Re-resolved just those 4 packages plus
their direct dependents to versions compatible with the existing
phpunit/phpunit ^5 || ^7 constraint; verified `composer install`
completes cleanly on PHP 7.4 with the updated lock file.
Doc comment long description must start with a capital letter.
CI's phpcs check runs on any file with changed lines (not just changed
lines within it) and hard-fails the job on any ERROR-level finding via
cs2pr, so pre-existing issues in these two files were blocking the
check even though they predate this PR. Fixed all of them:

- phpcbf auto-fixed spacing/alignment/array-formatting (41 issues,
  mechanical, no behavior change).
- Un-indented the cleanTalk nowdoc's closing marker: the indented form
  is PHP 7.3+ only (flexible heredoc), but this plugin supports back to
  PHP 5.6. Verified the produced JS string is still complete and
  correct at runtime.
- Added explicit parentheses to the recaptcha-type boolean check to
  match PHP's own default && / || precedence exactly (no logic change,
  verified against all 8 input combinations).
- Added wp_unslash() before sanitize_text_field() on three $_REQUEST
  reads (evf_popup_message, evf_message_display_location,
  evf_form_state_type) -- WordPress superglobals carry magic-quotes
  style escaping, so this was silently keeping literal backslashes in
  values containing quotes. Standard fix, no change for normal input.
- Comment punctuation, file-header blank line, docblock capitalization.

Remaining findings are all WARNING-level (unused hook-signature
parameters, nonce-verification recommendations on read-only display
state) which don't fail the check and would need actual behavioral
review rather than a mechanical fix.
…rrent_page_has_form()

Both were still loading site-wide, undermining the point of this PR:

- dashicons was enqueued unconditionally in load_scripts(), outside the
  current_page_has_form() gate this PR added. It's only used by one rule
  (the form-selector block's notice icon), so non-form pages were loading
  a whole icon-font stylesheet for nothing.
- evf_body_class() hooks the global body_class filter, so the
  everest-forms-no-js class and its wp_footer inline script ran on every
  page of the site, not just form pages. Checked both this repo and every
  pro addon for anything reading .everest-forms-no-js/.everest-forms-js -
  found zero consumers, so this looks vestigial, but gated it the same way
  regardless.

Verified anonymously (not just the authenticated session): a non-form page
now loads neither; a real form page still loads both plus everest-forms.css,
unchanged.
@deepench

Copy link
Copy Markdown
Contributor

@rajatgautam755421 Please check the page containing [everest_forms_user_login]. When viewed while logged out, the page's styling breaks because everest-forms.css is not loaded.
image

Pages containing only an addon shortcode (e.g. [everest_forms_user_login])
never loaded everest-forms.css because current_page_has_form() only matched
the exact [everest_form] tag.
@rajatgautam755421

Copy link
Copy Markdown
Contributor Author

@rajatgautam755421 Please check the page containing [everest_forms_user_login]. When viewed while logged out, the page's styling breaks because everest-forms.css is not loaded. image

@deepench this issue is fixed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants