diff --git a/docs/superpowers/specs/2026-08-26-inline-color-popover-design.md b/docs/superpowers/specs/2026-08-26-inline-color-popover-design.md new file mode 100644 index 0000000..dcbd410 --- /dev/null +++ b/docs/superpowers/specs/2026-08-26-inline-color-popover-design.md @@ -0,0 +1,219 @@ +# 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 `
` 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 (``) gets a `` appended as a child, containing a gear icon character (⚙). The gear is hidden by default and shown via CSS when the badge is hovered: + +```css +.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 ``, 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 `
` 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 `` swatch, and a `` for hex value. The two inputs are synced bidirectionally. +- **Live preview:** A `` 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 `` 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.