Skip to content

Move feedback into the right side-nav - #3324

Open
enf0rc3 wants to merge 9 commits into
mainfrom
wl/feedback-widget
Open

Move feedback into the right side-nav #3324
enf0rc3 wants to merge 9 commits into
mainfrom
wl/feedback-widget

Conversation

@enf0rc3

@enf0rc3 enf0rc3 commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

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:

Field Sent
entry.336432709 page window.location.href
entry.128617088 scale 1–5 Yes → 5, No → 1
entry.434783109 comment textarea, or a space when empty (the form marks it required)

Also in here:

  • .btn[aria-pressed='true'] added to src/styles/button.css so 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.
  • The styles and the startup script are scoped to Feedback.astro. The widget no longer starts from main.js, so its code ships as its own bundle, requested only by pages that render it.
  • 5 Playwright tests in tests/feedback.spec.ts covering the disclosure, the vote toggle, and both submit outcomes.

Known limits, all worth a follow-up:

  • Hidden below 1130px. The table of contents column restacks above the article at that width, so the widget would ask the question before anything had been read. The old link sat in the article body and so worked on mobile — mobile readers now have no way to give feedback at all. Needs a design call.
  • Google Forms sends no CORS headers, so the POST is no-cors and 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.
  • Nothing is captured until Send is pressed. A reader who votes and leaves submits nothing. Posting on the vote instead would also need the Send path to stop creating a second response, so it is a bit more than a one-liner.

Before (old feedback)

image

After

Before Vote:

image

After Vote:

image

TODO:

  • Test and confirm the new UI actually works (i suspect it may not if you need email or something)
  • Decide what mobile readers get, given the widget is hidden below 1130px

🤖 Generated with Claude Code

@enf0rc3
enf0rc3 force-pushed the wl/feedback-widget branch from a805773 to 595cba4 Compare August 10, 2026 03:47
@enf0rc3
enf0rc3 changed the base branch from wl/remove-feedback to main August 10, 2026 03:47
@enf0rc3 enf0rc3 changed the title Add the feedback widget to the table of contents column Move feedback into the table of contents column Aug 10, 2026
@enf0rc3
enf0rc3 force-pushed the wl/feedback-widget branch from 595cba4 to 6e7ec79 Compare August 10, 2026 05:38
@enf0rc3 enf0rc3 changed the title Move feedback into the table of contents column Move feedback into the right side-nav Aug 10, 2026
@team-marketing-branch-protections

Copy link
Copy Markdown

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.

@enf0rc3

enf0rc3 commented Aug 10, 2026

Copy link
Copy Markdown
Contributor Author

Confirmed the form submission worked:
image
image

@enf0rc3
enf0rc3 force-pushed the wl/feedback-widget branch from 6e7ec79 to 5febadf Compare August 11, 2026 03:08
enf0rc3 and others added 6 commits August 11, 2026 15:52
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
enf0rc3 force-pushed the wl/feedback-widget branch from 5febadf to 796ea58 Compare August 11, 2026 03:59
* @param {string} comment
* @returns {Promise<void>}
*/
async function submit(rating, comment) {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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
enf0rc3 marked this pull request as ready for review August 11, 2026 05:40
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