Files
reddit-tweaks-ffe/docs/superpowers/specs/2026-08-26-inline-color-popover-design.md

11 KiB
Raw Blame History

Inline Subreddit Color Popover — Design Spec

Date: 2026-08-26 Status: Draft Parent spec: 2026-08-26-reddit-badge-tweaks-design.md

Overview

Add an inline color-override popover to subreddit badges, letting users set per-subreddit colors directly on the page without opening the extension popup. A gear icon appears on hover over a subreddit badge; clicking it opens a floating popover with color pickers, a live preview, and Save/Cancel/Reset controls. The popover writes to the same subredditColors storage used by the popup, keeping both UIs in sync.

Approach

Single shared popover. One <div class="rt-color-popover"> element is injected into document.body once when the badge tweak initializes. Opening the popover for any subreddit re-positions and re-populates this single element. This avoids creating hidden popover elements per post and ensures only one popover is open at a time.

Gear Icon — Trigger

Each subreddit badge (<a class="rt-badge rt-badge--subreddit">) gets a <span class="rt-badge-gear"> appended as a child, containing a gear icon character (⚙). The gear is hidden by default and shown via CSS when the badge is hovered:

.rt-badge-gear {
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.15s;
}
.rt-badge--subreddit:hover .rt-badge-gear {
  opacity: 0.6;
  pointer-events: auto;
}
.rt-badge-gear:hover {
  opacity: 1 !important;
}

Because the gear is a child of the badge <a>, hovering over the gear maintains the parent's :hover state — no gap or flicker when moving the cursor from the text to the gear.

Gear icon properties:

  • display: inline always, visibility controlled via opacity + pointer-events (allows CSS transition).
  • Font size: 10–11px, matching badge text proportion.
  • Color: inherits badge text color. At rest (parent hovered): opacity: 0.6. On direct gear hover: opacity: 1.
  • Left margin: 4px to separate from subreddit text.
  • cursor: pointer.

Click behavior:

  • event.preventDefault() — prevents navigating the subreddit link.
  • event.stopPropagation() — prevents bubbling to the badge click handler.
  • Calls openPopover() with the badge element, subreddit name, and current settings.

Only subreddit badges get the gear icon. Comments badges do not.

Popover — Structure

The popover is a <div class="rt-color-popover"> appended to document.body with position: absolute and a high z-index (e.g., 10000).

Contents

┌──────────────────────────┐
│ r/javascript         [×] │  ← header with subreddit name and close button
├──────────────────────────┤
│ Background  [■] #fde8e0  │  ← color swatch + hex text input
│ Border      [■] #c84a20  │
│ Text        [■] #c84a20  │
├──────────────────────────┤
│ Preview: [ r/javascript ]│  ← live preview badge
├──────────────────────────┤
│ [Reset Default]    [Save]│  ← action buttons
│                  [Cancel]│
└──────────────────────────┘
  • Header: Subreddit name as a label (e.g., "r/javascript"), truncated with ellipsis if long. Close button (×) on the right.
  • Color rows: Three rows, one per property (background, border, text). Each row has a label, an <input type="color"> swatch, and a <input type="text"> for hex value. The two inputs are synced bidirectionally.
  • Live preview: A <span> styled as an rt-badge rt-badge--subreddit, with the subreddit name as text content. Inline styles update in real time as the user adjusts any color input.
  • Footer: "Reset to Default" button (left), "Save" and "Cancel" buttons (right).

Positioning

On open, the popover is positioned below the clicked badge, aligned to its left edge:

  1. Get the badge's getBoundingClientRect().
  2. Calculate top = rect.bottom + window.scrollY + 6px (6px gap).
  3. Calculate left = rect.left + window.scrollX.
  4. If the popover would overflow the viewport bottom, flip above: top = rect.top + window.scrollY - popoverHeight - 6px.
  5. Clamp left so the popover doesn't overflow left or right edges.

A small CSS arrow (::before pseudo-element) points toward the badge.

Lifecycle

  • Open: Clicking a gear icon calls openPopover(). If the popover is already open for a different subreddit, it re-positions and re-populates (no stacking). If the popover is already open for the same subreddit, the click toggles it closed (cancel behavior).
  • Close without saving: Clicking Cancel, clicking the × button, clicking outside the popover, pressing Escape, or toggling the same gear icon.
  • Close with saving: Clicking Save.
  • Dark mode toggle while open: If dark mode changes while the popover is open (e.g., user toggles RES night mode), the popover's visual theme updates via the existing dark mode observer, but the color values in the inputs are not changed — they reflect the user's in-progress edits regardless of mode.

Color Editing and Preview

Initial values

When the popover opens for a subreddit:

  • If an override exists in settings.subredditColors[subredditName], those values populate the inputs.
  • If no override exists, the inputs show the current global subreddit badge colors for the active mode (light or dark), derived from settings[prefix + ".subredditBgColor"] etc. This way the user starts from the "current appearance" and can adjust from there.

Input syncing

  • Changing the <input type="color"> updates the text input's value.
  • Changing the text input updates the color picker if the value is a valid hex color (/^#[0-9a-f]{6}$/i). Invalid values show a subtle red border on the text input but do not update the picker.

Live preview

  • As any color input changes (via input event), the preview badge's inline styles update immediately.
  • The preview badge is styled identically to a real subreddit badge (same classes), with inline style overrides for the three color properties.
  • Actual page badges are not updated until Save is clicked.

Save, Cancel, and Reset

Save

  1. Writes {bg, border, text} into settings.subredditColors[subredditName].
  2. Calls RedditTweaks.saveSettings(settings) → writes to browser.storage.local.
  3. The existing storage.onChanged listener in main.js fires, calling updateColors(), which re-applies per-subreddit overrides to all badges on the page — all instances of that subreddit update, not just the one that was clicked.
  4. Closes the popover.

Cancel

  • Dismisses the popover without writing anything.
  • Triggered by: clicking Cancel, clicking ×, clicking outside the popover, pressing Escape.
  • Page badges remain unchanged.

Reset to Default

  1. Deletes settings.subredditColors[subredditName] (removes the key entirely).
  2. Saves the updated settings.
  3. The badge reverts to global colors via the updateColors() flow.
  4. The popover inputs update to show the global defaults so the user sees what "default" means.
  5. The popover stays open so the user can confirm or adjust further.

Sync with popup

Both the inline popover and the popup's per-subreddit overrides section read/write the same subredditColors object in browser.storage.local. Changes made inline appear in the popup next time it's opened, and vice versa. No additional sync logic is needed.

Styling

Popover

  • White background (#fff), 1px solid #ccc border, border-radius: 6px.
  • box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15) for depth.
  • Padding: 12px.
  • Max-width: 260px.
  • Font: same stack as the extension (-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif), 13px base.
  • CSS arrow (via ::before pseudo-element) pointing toward the anchor badge.

Dark mode variant

The popover detects the current mode using the same isDarkMode(settings) logic in badges.js. When dark:

  • Background: #2a2a2a.
  • Border: #555.
  • Text: #e0e0e0.
  • Input borders adjusted for contrast.
  • Applied via .rt-dark class on the popover element, consistent with the existing badge dark mode approach.

Color input rows

  • Flex layout: label (left), color swatch + hex text (right).
  • Color swatches: 28px × 20px, 1px solid #ccc border, border-radius: 3px.
  • Hex text inputs: 70px wide, monospace font, font-size: 12px.

Buttons

  • Save: Accent blue background (#4a90d9), white text, border-radius: 3px.
  • Cancel: Text-only or light secondary style, same as popup's .btn-secondary.
  • Reset to Default: Secondary style, positioned on the left side of the footer to avoid accidental clicks near Save.

All classes use the rt- prefix

Consistent with the rest of the extension: rt-color-popover, rt-popover-header, rt-popover-row, rt-popover-preview, rt-popover-footer, etc.

File Changes

File Change
content/tweaks/badges.js Add gear icon injection in processPost(). Add popover creation, open/close/save/reset functions.
content/styles/badges.css Add styles for .rt-badge-gear, .rt-color-popover and its children, light and dark variants.
lib/settings.js No changes.
content/main.js No changes.
popup/popup.js No changes.
popup/popup.html No changes.
manifest.json No changes.

Two files modified, zero new files.

Edge Cases

  • Subreddit badge absent: No gear icon added, no popover trigger. Follows existing behavior where the subreddit badge is skipped.
  • Popover open when page scrolls: Popover uses position: absolute and scrolls with page content. No repositioning needed.
  • Popover open when RES loads new posts: New posts get processed normally. The open popover stays in place.
  • Multiple rapid gear clicks: Single popover instance re-positions and re-populates. No stacking.
  • Invalid hex input: Text input shows subtle error border. Save uses the color picker's last valid value.
  • Very long subreddit names: Header truncates with text-overflow: ellipsis.
  • Extension disabled mid-session: Page reloads (existing behavior), removing all injected elements including the popover.

Testing

Manual testing additions:

  1. Hovering over a subreddit badge shows the gear icon; moving off hides it.
  2. Moving from badge text to gear icon does not flicker/hide the gear.
  3. Clicking the gear opens the popover positioned near the badge.
  4. Clicking the gear does not navigate the subreddit link.
  5. Popover shows current override values (or global defaults if no override exists).
  6. Changing colors updates the live preview badge in real time.
  7. Actual page badges do not change until Save is clicked.
  8. Clicking Save persists the override and updates all badges for that subreddit on the page.
  9. Clicking Cancel (or outside, or Escape) dismisses without saving.
  10. Reset to Default removes the override, reverts badge to global colors, and shows global values in the popover.
  11. Inline changes appear in the popup's per-subreddit overrides section.
  12. Popup changes to per-subreddit overrides are reflected when the inline popover is next opened.
  13. Gear icon does not appear on comments badges.
  14. Popover dark mode variant works when RES night mode is active.
  15. Only one popover open at a time — clicking a different gear re-positions the popover.