🩹 QuizWizz v1.10.0 — Merge-Ready Patch Spec
Do not use as a patch source
These 2026-07-24 diffs no longer apply to live source; current status is in Program Masterplan. Current handoff: 2026-08-27 canonical handoff.
Provenance
These fixes were authored + verified on the wrdp bench, which has no QuizWizz source repo. Per
BUILDING.md(locked, signed, SLSA-provenance release pipeline) andSOURCE-PROVENANCE.md, bench edits are development artifacts only — never a shippable release. The release owner MUST re-apply these diffs against the canonical source and rebuild through the signed pipeline. All four are additive/deletion-only and were smoke-verified live (see the Roast and Frontend audit for evidence).
Summary
| Fix | Severity | Files | Nature |
|---|---|---|---|
| FE-1 primary button invisible | 🔴 a11y | assets/css/qwizz-finetune.css | +4 lines (append) |
| FE-8 unstyled loading state | 🟡 UX | assets/css/qwizz-player.css, includes/Frontend/QuizEmbed.php | +6 CSS lines, +1 class attr |
BE-3 dead allow() wrapper | 🟠 correctness | includes/RateLimiter.php | −8 lines (delete) |
BE-7 dead QUIZWIZZ_VER alias | 🟡 hygiene | quizwizz.php | −2 lines (delete) |
FE-1 — restore primary-button fill
Root cause: qwizz-finetune.css is enqueued after qwizz-builder.css (includes/Frontend/Shortcode.php:143) and re-declares a bare global .qwizz-btn--primary{background:var(--qe-violet)}. --qe-violet is scoped only to .qwizz-editor, so for any primary button outside the editor the var() is invalid → transparent background wins by source order → white text on a transparent fill (invisible). Fix co-locates a higher-specificity corrective rule at the end of the same file (it loads only when the leak can occur).
--- a/assets/css/qwizz-finetune.css
+++ b/assets/css/qwizz-finetune.css
@@ -308,3 +308,7 @@
font-size: 21px;
}
}
+
+/* FE-1: restore primary-button fill where editor-scoped var(--qe-violet) would leak (invalid outside .qwizz-editor). */
+.qwizz-builder .qwizz-btn--primary,
+.qwizz-player .qwizz-btn--primary{background:linear-gradient(135deg,var(--qw-v),var(--qw-v2))!important;border:0!important;color:#fff!important}Optional cleaner alternative (root-cause scope instead of override): change finetune.css’s two bare rules from .qwizz-btn--primary… to .qwizz-editor .qwizz-btn--primary… so they can never leak. Equivalent result; the append is preferred for a built artifact because it avoids editing the minified line.
FE-8 — visible loading state (spinner)
.qwizz-player__loading was styled only as {padding:42px;text-align:center} (no spinner); the class is otherwise referenced only in qwizz-player.js. Fix adds a reduced-motion-safe spinner and applies the class to the server-rendered initial loader so the pre-hydration flash matches. Failure/retry already exist (qwizz-player.js renderFailure() + Retry-After) and are untouched.
--- a/assets/css/qwizz-player.css
+++ b/assets/css/qwizz-player.css
@@ -58,3 +58,9 @@
/* Keep every question compact enough to read as a single decision. */
.qwizz-player{max-width:740px;margin-block:clamp(20px,4vw,48px)}...
+
+/* FE-8: visible loading state with reduced-motion-safe spinner. */
+.qwizz-player__loading{display:grid;place-items:center;gap:14px;padding:clamp(40px,8vw,72px) 24px;color:var(--qw-muted);font-size:.95rem;text-align:center}
+.qwizz-player__loading::before{content:"";width:34px;height:34px;border-radius:50%;border:3px solid var(--qw-line);border-top-color:var(--qw-v);animation:qwizz-player-spin .8s linear infinite}
+@keyframes qwizz-player-spin{to{transform:rotate(360deg)}}
+@media(prefers-reduced-motion:reduce){.qwizz-player__loading::before{animation:none}}--- a/includes/Frontend/QuizEmbed.php
+++ b/includes/Frontend/QuizEmbed.php
@@ -36 +36 @@ public static function render( int $quiz_id ): string {
- return '<div ' . implode( ' ', $serialized ) . '><div class="qwizz-player__card"><p>' . esc_html__( 'Loading quiz…', 'quizwizz' ) . '</p></div><div class="qwizz-player__live" role="status" aria-live="polite" aria-atomic="true"></div></div>';
+ return '<div ' . implode( ' ', $serialized ) . '><div class="qwizz-player__card"><p class="qwizz-player__loading">' . esc_html__( 'Loading quiz…', 'quizwizz' ) . '</p></div><div class="qwizz-player__live" role="status" aria-live="polite" aria-atomic="true"></div></div>';BE-3 — delete dead RateLimiter::allow()
Zero callers plugin-wide; the wrapper returns true on storage error (the footgun its own doc comment warns about). consume() is the real API and stays.
--- a/includes/RateLimiter.php
+++ b/includes/RateLimiter.php
@@ -108,15 +108,7 @@
delete_option( 'qwizz_rate_limit_last_error' );
return true;
}
-
- /**
- * Compatibility wrapper. New code should use consume() so storage failures
- * remain distinguishable from actual throttling.
- */
- public static function allow( string $scope, int $limit, int $window = HOUR_IN_SECONDS ): bool {
- return true === self::consume( $scope, $limit, $window );
- }
-
/** @return bool|WP_Error */
private static function increment( string $bucket, int $limit, int $window ) {BE-7 — delete dead QUIZWIZZ_VER alias
Used only at its own definition. QUIZWIZZ_VERSION (line 22) is the real constant and stays.
--- a/quizwizz.php
+++ b/quizwizz.php
@@ -20,8 +20,6 @@
define( 'QUIZWIZZ_URL', plugin_dir_url( __FILE__ ) );
define( 'QUIZWIZZ_VERSION', '1.10.0' );
-// Backward-compatible internal alias retained for older integrations.
-define( 'QUIZWIZZ_VER', QUIZWIZZ_VERSION );
require QUIZWIZZ_PATH . 'vendor/autoload.php';Verification performed on bench
- FE-1: live builder page — CTA “Online spielen” computed
backgroundImage: linear-gradient(135deg, rgb(109,40,217), rgb(91,…)), width 286px (wasrgba(0,0,0,0)/border:0 none, invisible). - FE-8: served
qwizz-player.cssrendered —.qwizz-player__loading::before= 34px,border-top-color: rgb(109,40,217)(--qw-v),border-radius:50%,animation-name: qwizz-player-spin. - BE-3/BE-7:
wp plugin deactivate/activate quizwizz→ Success (no fatal);defined("QUIZWIZZ_VER")→false,defined("QUIZWIZZ_VERSION")→true;method_exists(RateLimiter,"allow")→false,consume→true; rate-limit REST endpoint still HTTP 200.
Deferred to release owner (not executed)
- BE-1 prune
vendor/mpdflocale/font payload; BE-2 lower PHP floor to 8.1 + syntax gate; BE-4/5GenerateControllerDI refactor; BE-6 collapse AI settle/release duplication; BE-8 WP compat CI + real “Tested up to”; redundantrandom_compat. Studio design-shell work (FE-3-chrome, FE-6 settings rhythm) is a separate design pass.