Add design spec for inline subreddit color popover

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-08-26 15:44:31 -04:00
parent cca7940dd9
commit 49b7d471b6

View File

@@ -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 `<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:
```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 `<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.