Move feedback into the right side-nav - #3324
Open
enf0rc3 wants to merge 9 commits into
Open
Conversation
enf0rc3
force-pushed
the
wl/feedback-widget
branch
from
August 10, 2026 03:47
a805773 to
595cba4
Compare
enf0rc3
force-pushed
the
wl/feedback-widget
branch
from
August 10, 2026 05:38
595cba4 to
6e7ec79
Compare
|
Pull request environment is available at https://stoctodocspr3324.z22.web.core.windows.net. You can view the ephemeral environment status in Octopus Deploy. This environment will be automatically deprovisioned when the pull request is closed, or after 7 days of inactivity. |
Contributor
Author
enf0rc3
force-pushed
the
wl/feedback-widget
branch
from
August 11, 2026 03:08
6e7ec79 to
5febadf
Compare
The Google Forms feedback prompt is being replaced with something new, so drop it from the default layout rather than leave it rendering. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Yes/No vote, an optional comment, and a thank you, built to the Figma design and sitting under the article navigation. Submissions go to the existing Google Form. It has no yes/no field, so Yes maps to 5 and No to 1 on its 1-5 scale, and the comment field is marked required there, so an empty box sends a space. Google Forms sends no CORS headers, so the POST goes out as no-cors and the response is opaque - the thank you is optimistic. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Three things were off against the Figma component and its placement in the table of contents column: The comment box, the send button and the thank you were all on screen at once. `[hidden]` is a user agent rule, so the component's own `display: flex` was winning and no state was ever hidden. The widget sat flush against the last navigation link and against the left edge of the column. The design gives it 32px of air and puts it on the same 16px inset the navigation title and links share. The thumbs down glyph faced the same way as the thumbs up. The design mirrors it so the pair face each other.
The submission goes out as no-cors, so an HTTP rejection is unreadable and there is no way to tell an accepted response from a refused one. A request that never leaves the browser is a different case: fetch rejects, and that was being caught and discarded on the way to the thank you. Being offline does it. So does an ad blocker, and `formResponse` is on several blocklists, so this is not a rare path. The reader was told their feedback had been sent while it went nowhere. The thank you now only shows once the request has actually gone out. On a rejection the form stays up with send live again, which is as far as the design goes - it has no error state to show. The rating values move into the module beside the field ids so the component and the form contract test share one source for them.
The widget cannot read whether Google accepted a submission, so the failure it is most exposed to is silent: someone edits the form, an entry id changes or a new required question appears, and every submission is refused with nothing on the page to show it. Three checks read the form definition off the live viewform page and assert what the widget depends on - that all three fields it posts to still exist, that the rating scale still accepts the two values it sends, and that no required question has appeared that it does not answer. The ids come from the module itself, so the test cannot drift away from the code. Five more cover the widget: the progressive disclosure the design asks for, the rating moving between the buttons, the thank you on a submission that went out, and the form staying up on one that did not. These need a scheduled run to catch form drift before a reader hits it. No workflow here runs Playwright yet.
It read as part of the navigation above it rather than as its own block. space16 on all four sides of the widget, and a further space16 above to separate it from the navigation. The two compose to the 32px the design puts between the last navigation link and the question, and the left inset still lands the question on the same line as the "On this page" title.
enf0rc3
force-pushed
the
wl/feedback-widget
branch
from
August 11, 2026 03:59
5febadf to
796ea58
Compare
enf0rc3
commented
Aug 11, 2026
| * @param {string} comment | ||
| * @returns {Promise<void>} | ||
| */ | ||
| async function submit(rating, comment) { |
Contributor
Author
There was a problem hiding this comment.
This is really a "hack" to get the UI in before actually wiring up the real deal.
We should look at GTM or something to publish on click of thumbs up / down etc and maybe look at a different mechanism for the content rather than forms.
Astro scopes a component style block to the component, so the widget carries its own styles rather than spreading them through the global sheet. The three icon rules need :global. Button.astro renders the span that takes the mask, so the scope attribute on it belongs to Button, not to this component - a plain scoped selector would not reach it and the thumbs would lose their glyphs. Scoping them under .feedback keeps them off every other button on the page.
main.js ran enhanceFeedback() on every page in the site, whether a feedback widget was on it or not, and bundled the module into the entry point to do it. Astro already has a place for behaviour that belongs to one component. The module now wires itself up on import, so the component script is a bare import with nothing to call. That means it can no longer be imported for its constants - the import would run it, and Astro frontmatter and the Playwright spec both evaluate in Node where there is no document. The form id, field ids and rating values move to feedback-form.js, which is data and safe to import anywhere; the component and the spec read them from there. The widget code now ships as its own bundle, requested only by pages that render it.
They were the only tests in the repo that left localhost, reaching docs.google.com to read the live form definition. That buys nothing today: no workflow here runs Playwright, so nothing was checking them automatically, and wiring them into CI would mean a suite that fails when Google is slow or serves a bot interstitial to a runner IP. The drift they guarded against is unlikely on a form nobody is editing, and this widget may not stay on Google Forms. The field ids stay documented in feedback-form.js for whoever does change it. The five widget tests stay. They run against the built site on localhost like every other spec here.
enf0rc3
marked this pull request as ready for review
August 11, 2026 05:40
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.


Replaces the Google Forms link at the bottom of the article with the widget from the Figma design: Yes/No vote → optional comment → thank you, sitting under the article nav.
This is a stop-gap, front-end only ux change rather than a wholesale change in the way we collect this data. There are benefits to improving the way we collect data e.g. splitting the questions and having the submit solely for the qualitative data, and having the yes / no on helpful use a different mechanism.
Results
Submissions go to the same Google Form as before:
entry.336432709pagewindow.location.hrefentry.128617088scale 1–55, No →1entry.434783109commentAlso in here:
.btn[aria-pressed='true']added tosrc/styles/button.cssso the chosen vote holds the pressed background. That file is new from Move button styles to a shared file that can be imported #3331, so it is worth a look.Feedback.astro. The widget no longer starts frommain.js, so its code ships as its own bundle, requested only by pages that render it.tests/feedback.spec.tscovering the disclosure, the vote toggle, and both submit outcomes.Known limits, all worth a follow-up:
no-corsand the response is opaque. An HTTP rejection is unreadable, so anything Google answers at all counts as accepted. A request that never leaves the browser — offline, or blocked by an extension — does reject, and the widget keeps the form up with Send live again rather than thanking the reader for nothing.Before (old feedback)
After
Before Vote:
After Vote:
TODO:
🤖 Generated with Claude Code