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.
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 absolutely positioned within the badge to avoid layout shift — it overlaps the badge's right padding area rather than pushing the badge wider. The gear is hidden by default and shown via CSS when the badge is hovered:
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. The gear is absolutely positioned so it never shifts adjacent elements.
During `processPost()`, the subreddit name (without the `r/` prefix) is stored as a `data-rt-subreddit` attribute on the badge container element. All code that needs the subreddit name (popover, `updateColors()`) reads from this attribute instead of parsing `textContent`. This avoids a bug where the gear icon's text content (⚙) would be included in the extracted name, breaking override lookups.
- **Mode toggle:** A `<select>` dropdown with "Light" and "Dark" options. Defaults to the currently active mode. Switching the toggle swaps the color inputs to show the values for the selected mode. Both modes are saved independently on Save.
- **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:
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. The click-outside handler checks `event.target` against the popover via `contains()` and also ignores clicks when a color input inside the popover has focus (prevents dismissal when Firefox's native color picker dialog fires document-level click events).
- **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.
- The mode toggle defaults to the currently active mode (light or dark).
- If a mode-aware override exists in `settings.subredditColors[subredditName][mode]`, those values populate the inputs.
- If no override exists for the selected mode, the inputs show the current global subreddit badge colors for that mode, derived from `settings[prefix + ".subredditBgColor"]` etc. This way the user starts from the "current appearance" and can adjust from there.
- Switching the mode toggle swaps the inputs to show values for the other mode (override if it exists, otherwise global defaults). In-progress edits for the previous mode are held in memory until Save or Cancel.
- 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.
1. Writes the in-progress colors for **both** modes into `settings.subredditColors[subredditName]` using the mode-aware schema: `{ light: {bg, border, text}, dark: {bg, border, text} }`. If the user only edited one mode, the other mode's values are preserved from the existing override (or omitted if none existed, so the global defaults continue to apply).
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.
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.
The existing `.rt-badge:hover { opacity: 0.8; }` rule is replaced with `filter: brightness(0.9)` for all badges. CSS `opacity` creates a stacking context that multiplicatively reduces child element opacity (the gear icon would appear washed out). `filter: brightness()` achieves a similar visual dimming without affecting child element opacity.
On `loadSettings()`, if a subreddit entry has the old flat shape (has a `bg` key at the top level instead of `light`/`dark`), it is migrated in-place: the flat values are copied into both `light` and `dark` sub-objects, and the migrated settings are saved back. This is a one-time, non-destructive migration.
### Impact on `applyColorsToContainer()`
`applyColorsToContainer()` changes to read from `overrides[prefix]` (where `prefix` is `"light"` or `"dark"`) instead of directly from `overrides`. If the mode-specific sub-object doesn't exist, it falls back to global defaults as before.
### Impact on Popup
The popup's per-subreddit overrides section (`popup.js`) is updated to read/write the new schema. Each subreddit entry in the popup shows two sets of color pickers (light and dark), or a mode toggle — matching the inline popover's approach. The `collectSubredditOverrides()` and `addSubredditEntry()` functions are updated accordingly.
| `content/tweaks/badges.js` | Add `data-rt-subreddit` attribute in `processPost()`. Add gear icon injection. Update `updateColors()` to read subreddit name from data attribute. Update `applyColorsToContainer()` for mode-aware overrides. Replace badge hover `opacity` with `filter: brightness()`. |
| `content/tweaks/color-popover.js` | **New file.** Popover DOM creation, positioning, open/close, save/reset, color input syncing, click-outside handling, mode toggle. |
| `content/styles/badges.css` | Add styles for `.rt-badge-gear` (absolute positioning, opacity transitions), `.rt-color-popover` and its children (layout, colors, light/dark variants). Replace `.rt-badge:hover` opacity with filter. |
| `lib/settings.js` | Add v1 → v2 migration logic in `loadSettings()` for the `subredditColors` schema change. |
- **Native color picker dialog:** The click-outside handler ignores clicks when a color input inside the popover has focus, preventing dismissal while Firefox's native color picker is open.
- **Old schema migration:** Existing per-subreddit overrides in the flat `{bg, border, text}` format are migrated to `{light: {...}, dark: {...}}` on first load. The migration copies the flat values into both modes, preserving the user's existing customizations.