Appearance
Known Issues: @click.self and Click Event Handling
Last updated: 2026-04-27
Background
PR #716 ("Analytics fixes", McSneaky, merged into v6.8.2) changed multiple click handlers from @click to @click.self and removed .stop modifiers across the codebase. The intent was to make background clicks opt-in instead of opt-out, avoiding unintended bg click triggers from child elements.
This introduced two bugs that are still present in production (v6.8.5).
Bug 1: Clickable Background Broken by Visual Elements
Status: Broken since v6.8.2. Reported by Gediminas 2026-04-27.
What: Creatives with clickable background enabled no longer click through when a visual element (graphic, text, HTML block) covers the background area.
Root cause: CreativeBody.vue line 16 uses @click.self="handleBgClick". The .self modifier only fires when event.target === event.currentTarget (the body element itself). When a graphic block sits on top of the body, the click target is the graphic element, not the body, so .self never matches and handleBgClick never runs.
Before PR #716: @click="handleBgClick" -- clicks from child elements bubbled up and triggered handleBgClick. Visual elements didn't block background clicks.
After PR #716: @click.self="handleBgClick" -- only direct clicks on the body element itself trigger handleBgClick. Any child element absorbs the click.
Workaround: Add pointer-events: none in Custom CSS on the graphic block. This lets clicks pass through to the body.
Previous fix attempts:
- PR #692 ("Fix blocks blocking bg click") -- added dynamic
pointer-eventsbased onhasClickthrough. This was before PR #716 changed to.self. - PR #711 ("Fix hover on visual elements") -- reverted #692 because
pointer-events: nonebroke hover animations on visual elements. Set all visual elements back topointer-events: auto.
Why PR #692/#711 are no longer the core issue: Those PRs tried to fix clickthrough by toggling pointer-events on visual elements. But the real problem is now @click.self on CreativeBody (introduced later in PR #716). Even with pointer-events: auto on visual elements, if CreativeBody used @click instead of @click.self, clicks would bubble through and trigger handleBgClick.
Bug 2: clicks Counter Effectively Always Zero
Status: Broken since v6.8.2. Not yet reported externally.
What: The clicks Bunny metric (raw click count on the creative) is almost never incremented.
Root cause: Creative.vue line 16 uses @click.self="creativeContainerClicked". CreativeBody covers the entire #creative-container, so event.target is never the container itself. The .self modifier prevents the handler from firing.
Impact: clicks in Bunny analytics is near-zero for all creatives. This was the "broadest click metric" intended to count every click anywhere. Currently useless.
Note: creativeClickedOnce (first interaction) was fixed in PR #735 by adding a separate @click.capture="handleFirstInteraction" handler. But clicks++ was left inside the .self handler and remains broken.
PR History
| PR | What | Effect |
|---|---|---|
| #692 | Dynamic pointer-events on visual elements | Fixed bg click passthrough, but broke hover animations |
| #711 | Reverted #692, back to pointer-events: auto | Restored hover, bg click still worked (pre-#716) |
| #716 | Changed @click to @click.self on CreativeBody and Creative.vue, removed .stop modifiers | Broke clickable background AND clicks counter |
| #735 | Added @click.capture for creativeClickedOnce | Fixed first-interaction detection, but clicks still broken |
The .self Debate (PR #716 Review)
In the PR #716 review, Nicolay proposed using capture-phase counting instead of removing .stop everywhere:
js
// Capture phase listener fires before any .stop can block it
this.$el.addEventListener('click', (e) => {
if (!e.isTrusted) return
Analytics.clicks++
}, { capture: true })Kevin went with .self instead, dismissing concerns from both Nicolay and Copilot about bubbling issues with "Nope, doesn't cause issues" / "Nope, no double data". He tested with creatives where visual elements didn't fully cover the background, so the issues weren't visible.
In PR #735 review (2026-04-23), Kevin acknowledged the .self issue for the clicks counter: "I didn't notice anything broken with current click.self, but now when you point it out, it might be good to test it over again."
Proposed Fixes
clicks counter (same fix regardless of approach)
Move clicks++ to a capture-phase handler, same pattern as handleFirstInteraction in PR #735:
js
@click.capture="handleAnyClick"
handleAnyClick(event: MouseEvent) {
if (!event.isTrusted) return
Analytics.clicks++
}Counts every real click regardless of .stop or .self. Kevin already approved this pattern in PR #735.
Clickable background: three approaches
Approach A: Remove .self, use .stop on interactive elements
Revert CreativeBody to @click="handleBgClick". Clicks bubble naturally. Visual elements without clickthrough do nothing in handleClick(), so the click bubbles to body and triggers bg click. Visual elements WITH clickthrough call event.stopPropagation() to prevent double-firing. Interactive elements (choices, form, close button, etc.) get .stop back.
js
// VisualElementMixin.ts
handleClick(event: MouseEvent): void {
if (!this.clickthroughUrl) return // bubbles to handleBgClick
event.stopPropagation() // has own clickthrough, block bg click
this.trackClick()
}html
<!-- CreativeBody.vue -->
@click="handleBgClick"
<!-- Interactive elements get .stop back -->
<Choice @click.stop="..." />
<FormSubmitButton @click.stop="..." />| Summary | Standard DOM event model. Clicks bubble up by default, interactive elements opt out with .stop. |
| Pros | Correct event model. No pointer-events hacks. Hover animations preserved. Visual elements just work -- no special wiring needed. |
| Cons | Kevin opposed this in PR #716 ("devs must remember .stop"). New interactive components must add .stop or they trigger bg click. |
| Complexity | Small: revert .self, restore .stop on ~6 interactive components, add event param to handleClick. |
| Risk | Low. This is how it worked before PR #716. The .stop list is known and stable. |
Approach B: Keep .self, emit bgClick from visual elements
Keep Kevin's .self on CreativeBody. Visual elements without clickthrough manually emit bgClick upward. CreativeBody already listens for @bg-click on conversation, video, and form blocks -- extend this to visual elements.
js
// VisualElementMixin.ts
handleClick(): void {
if (this.clickthroughUrl) {
this.trackClick()
return
}
this.$parent?.$emit('bgClick')
}html
<!-- CreativeBody.vue: add @bg-click to visual element templates -->
<CreativeGraphicBlock @bg-click="handleBgClick" ... />
<CreativeTextBlock @bg-click="handleBgClick" ... />| Summary | Works within .self constraint. Visual elements explicitly forward clicks to bg handler via $emit, same pattern conversation/video/form already use. |
| Pros | No changes to .self or .stop. Kevin's model stays intact. Hover animations preserved. Consistent with existing @bg-click pattern already used by 3 block types. |
| Cons | Requires adding @bg-click listeners in CreativeBody for visual element types. Slightly more wiring than Approach A. |
| Complexity | Small-medium: modify handleClick to $emit('bgClick'), add @bg-click="handleBgClick" on graphic/text/HTML blocks in CreativeBody template. |
| Risk | Low. Uses the same $emit/@bg-click pattern that conversation, video, and form blocks already use successfully. |
Approach C: Keep .self, dynamic pointer-events
Keep .self. Make visual elements set pointer-events: none when bg click is enabled and they have no own clickthrough. Clicks pass through to body, .self matches.
js
// In visual element styles() computed
'pointerEvents': (DataStore.creativeSettings.creativeDelivery.backgroundClickthrough
&& !this.clickthroughUrl) ? 'none' : 'auto'| Summary | CSS-level fix. Visual elements become transparent to clicks when bg click is active. |
| Pros | Declarative. No event forwarding. Works with .self. |
| Cons | Breaks hover animations and CSS :hover states (tried and reverted in PR #692/#711). Users must use custom CSS for hover effects on affected elements. |
| Complexity | Small: add one computed check to each visual element's styles. |
| Risk | High. Already tried and reverted once. Hover is a real feature people use. |
Recommendation
Both A and B are good options with low risk.
Approach A uses standard DOM semantics and is the simplest mental model -- clicks bubble unless stopped. It was the working model before PR #716.
Approach B works within the existing .self model and extends a pattern already established for conversation, video, and form blocks. No need to revert Kevin's changes.
Approach C should be avoided -- it was already tried and reverted due to broken hover animations.
Key Files
| File | Role |
|---|---|
Creative-Engine/src/components/creative/Creative.vue | Container click handlers (@click.self, @click.capture) |
Creative-Engine/src/components/conversationflow/CreativeBody.vue | Background click handler (@click.self="handleBgClick") |
Creative-Engine/src/mixins/VisualElementMixin.ts | Visual element click handler (handleClick) |
Creative-Engine/src/components/creative/VisualElements/CreativeGraphicBlock.vue | Graphic block with pointerEvents: 'auto' |
Creative-Engine/src/components/creative/VisualElements/CreativeTextBlock.vue | Same pattern |
Creative-Engine/src/components/creative/VisualElements/CreativeHtmlBlock.vue | Same pattern |