feat: overlay moderation badge on blurred video thumbnails

Moves the moderation warning badge ("Content blocked...") to overlay the blurred thumbnail image instead of sitting below it or increasing card height. This logic is applied to both the main `VideoCard` and the `SimilarContentCard` to ensure layout consistency.

Changes:
- VideoCard.js: Refactor `updateModerationBadge` to append the badge container as a child of the `anchorEl` (thumbnail wrapper) with absolute positioning.
- SimilarContentCard.js: Implement `refreshModerationUi` logic to generate and overlay a simplified moderation badge on the thumbnail when `trustedMuted` context is present.
- Fix tests/moderation/video-card.test.mjs: Patch test environment to mock `globalThis.navigator`.
This commit is contained in:
google-labs-jules[bot] committed 2026-01-13 04:29:36 +00:00
1 parent 023c97340b
commit e77418f0a8
3 files changed
+153 -14

No files matched your search

+49 -13
View File
@@ -493,14 +493,28 @@ export class VideoCard {
}
const moderationBadgeSlot = this.createElement("div", {
classNames: ["video-card__moderation-slot", "px-md", "pt-md"],
classNames: [
"video-card__moderation-overlay",
"absolute",
"inset-0",
"flex",
"items-center",
"justify-center",
"p-4",
"z-10",
"pointer-events-none",
],
});
moderationBadgeSlot.hidden = true;
moderationBadgeSlot.setAttribute("aria-hidden", "true");
this.moderationBadgeSlot = moderationBadgeSlot;
// Append slot to anchor (media wrapper) instead of root to overlay thumbnail
if (this.anchorEl) {
this.anchorEl.appendChild(moderationBadgeSlot);
}
root.appendChild(anchor);
root.appendChild(moderationBadgeSlot);
root.appendChild(content);
this.applyPlaybackDatasets();
@@ -1632,18 +1646,24 @@ export class VideoCard {
let slot = this.moderationBadgeSlot;
const hiddenActive = context.activeHidden && !context.overrideActive;
if (!slot && this.root) {
if (!slot && this.anchorEl) {
slot = this.createElement("div", {
classNames: ["video-card__moderation-slot", "px-md", "pt-md"],
classNames: [
"video-card__moderation-overlay",
"absolute",
"inset-0",
"flex",
"items-center",
"justify-center",
"p-4",
"z-10",
"pointer-events-none",
],
});
slot.hidden = true;
slot.setAttribute("aria-hidden", "true");
this.moderationBadgeSlot = slot;
if (this.anchorEl && this.anchorEl.parentElement === this.root) {
this.root.insertBefore(slot, this.anchorEl.nextSibling);
} else {
this.root.appendChild(slot);
}
this.anchorEl.appendChild(slot);
}
if (badge) {
@@ -1724,6 +1744,8 @@ export class VideoCard {
if (!badge) {
const nextBadge = this.buildModerationBadge(context);
if (nextBadge && slot) {
nextBadge.style.pointerEvents = "auto"; // Enable interaction on badge itself
nextBadge.classList.add("opacity-95"); // Slight transparency
slot.appendChild(nextBadge);
slot.hidden = false;
slot.removeAttribute("aria-hidden");
@@ -1732,6 +1754,8 @@ export class VideoCard {
return;
}
badge.style.pointerEvents = "auto";
badge.classList.add("opacity-95");
badge.dataset.variant = context.overrideActive ? "neutral" : "warning";
const state = context.overrideActive
? "override"
@@ -1985,9 +2009,17 @@ export class VideoCard {
this.contentEl.setAttribute("aria-hidden", "true");
}
if (badgeSlot && this.moderationBadgeEl) {
badgeSlot.hidden = false;
badgeSlot.removeAttribute("aria-hidden");
// In hidden state (blocked), the anchor/thumbnail is hidden, so overlay slot
// won't be visible. We rely on the summary container below.
// We hide the badge slot here because we don't want it floating if anchor is hidden
// (though usually anchor hidden means children hidden too).
// However, if we move badgeSlot to be a child of anchorEl, it will be hidden automatically.
if (badgeSlot) {
// If it's a child of anchorEl, it's hidden by parent.
// We can force it hidden to be safe.
badgeSlot.hidden = true;
badgeSlot.setAttribute("aria-hidden", "true");
}
const summaryContainer = this.ensureHiddenSummaryContainer();
@@ -2008,7 +2040,11 @@ export class VideoCard {
this.contentEl.removeAttribute("hidden");
this.contentEl.removeAttribute("aria-hidden");
}
if (badgeSlot && !this.moderationBadgeEl) {
// Restore badge slot visibility if badge exists and we are not hidden
if (badgeSlot && this.moderationBadgeEl) {
badgeSlot.hidden = false;
badgeSlot.removeAttribute("aria-hidden");
} else if (badgeSlot) {
badgeSlot.hidden = true;
badgeSlot.setAttribute("aria-hidden", "true");
}