Element Targeting: Pinpoint Feedback on Any Element
Let users click on specific page elements to attach feedback directly to buttons, forms, or any UI component.
"The button doesn't work" leaves you guessing which button. Element targeting has the reader point at it, and the submission arrives carrying a CSS selector and the element's own text.
What happens after a thumbs click
Targeting mode starts. Your page dims, crosshairs follow the cursor, and whatever sits under them highlights so there is no doubt about what a click will pick.
The click captures three things: a CSS selector that finds the element again, its visible text cut to 100 characters, and any data-comp, data-qa, data-testid or data-id attribute it carries.
All three travel with the feedback.
What the reader sees
Horizontal and vertical lines track the cursor with a circular scope where they cross, which is how somebody knows the page is waiting for a pick rather than broken.
Each element outlines as the cursor passes over it, green for a thumbs-up and red for a thumbs-down, so the reader can still see which way they voted.
The edges of the page darken slightly to pull attention inward.
After the click a marker sits on the chosen element while the modal opens, confirming what was caught.
How the selector gets built
Three strategies, in order, stopping at the first that works.
Data attributes win
If the element or any ancestor carries one, that is the selector:
<button data-testid="submit-payment">Pay Now</button>
<!-- Generates: [data-testid="submit-payment"] -->
It looks for data-comp first, then data-qa, data-testid and data-id.
Then the id
With no data attribute and an id present:
<form id="checkout-form">...</form>
<!-- Generates: #checkout-form -->
Special characters in the id get escaped.
Otherwise, a path
With neither, it counts its way down from body:
<body>
<main>
<div>
<button>Click Me</button> <!-- Third button in this div -->
</div>
</main>
</body>
<!-- Generates: body > main:nth-child(1) > div:nth-child(1) > button:nth-child(3) -->
That is unique today and wrong the moment somebody inserts a <div> above it. Which is the argument for the next section.
Label the elements you care about
Put a data attribute on anything you would want to hear about:
<!-- Before: Hard to identify -->
<button class="btn btn-primary">Submit</button>
<!-- After: Clear identification -->
<button class="btn btn-primary" data-testid="contact-form-submit">Submit</button>
Class names change every time somebody tidies the CSS; these do not. The value says what the element is for, so the selector reads as English in your inbox. Playwright and Cypress already use the same attributes, so you are probably half done. And you can search the inbox by attribute value.
Reading it in the dashboard
The selector sits in the feedback details, ready to paste into a search across your source.
The element text tells you what the reader was looking at without opening the page yourself.
A captured data attribute shows as a badge, which is what makes a long inbox scannable.
Filtering by selector pattern pulls up everything aimed at one component. Search checkout and you get every report about the checkout, whichever page it came from.
Turning it off
One flag skips the whole step:
<script
src="https://unpkg.com/@qaiddev/thumbs-embed/dist/embed.js"
data-endpoint="/api/feedback"
data-skip-targeting="true"
></script>
Or in JavaScript:
import { QaidFeedback } from '@qaiddev/thumbs-embed';
new QaidFeedback({
endpoint: '/api/feedback',
skipTargeting: true
});
A thumbs click now opens the modal directly. You still get the page URL and the console errors, just nothing about which element.
Worth doing when the feedback is about a page rather than a thing on it, or when watching a real user hesitate at the crosshairs tells you the step is costing you more than it returns.
Styling the buttons that start it
Colors
marker is the colour of the ring around a selected element:
new QaidFeedback({
endpoint: '/api/feedback',
colors: {
positive: '#22c55e', // Green for thumbs up
negative: '#ef4444', // Red for thumbs down
marker: '#6366f1' // Purple outline on selected elements
}
});
Button size
Three sizes:
new QaidFeedback({
endpoint: '/api/feedback',
buttonSize: 'small' // 'small' | 'medium' | 'large'
});
Icons
Swap the thumbs for anything:
new QaidFeedback({
endpoint: '/api/feedback',
positiveIcon: '<svg>...</svg>',
negativeIcon: '<svg>...</svg>'
});
Your own classes
Take over the styling:
new QaidFeedback({
endpoint: '/api/feedback',
buttonClass: 'my-custom-button'
});
With buttonClass set, the embed keeps only display and cursor and hands you the rest.
Position
Which corner they sit in:
new QaidFeedback({
endpoint: '/api/feedback',
position: 'bottom-left', // 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left'
offset: { x: 24, y: 24 } // Distance from screen edge
});
Getting out of targeting mode
Escape cancels at any point and returns everything to idle. Somebody who hit a thumb by accident is not trapped.
On a phone
Tap instead of click, and elements highlight on touch. The crosshair UI is the same. Below 640px the modal becomes a bottom sheet, which is what a phone user expects a panel to do.
Where to start
Add data-qa attributes to the twenty elements you would most want feedback on. Everything else falls out of that: readable selectors, a searchable inbox, and reports that land next to the code.