diff --git a/docs/superpowers/specs/2026-08-26-badge-polish-design.md b/docs/superpowers/specs/2026-08-26-badge-polish-design.md new file mode 100644 index 0000000..d07664f --- /dev/null +++ b/docs/superpowers/specs/2026-08-26-badge-polish-design.md @@ -0,0 +1,210 @@ +# Badge Polish: Gear Animation + Custom Color Picker — Design Spec + +**Date:** 2026-08-26 +**Status:** Draft +**Parent spec:** `2026-08-26-inline-color-popover-design.md` + +## Overview + +Two improvements to the inline color popover and badge system: + +1. **Gear icon animation** — remove the permanent padding gap on subreddit badges. The badge grows on hover to reveal the gear, then shrinks back. +2. **Custom color picker** — replace all native `` elements (which delegate to the macOS system picker) with an inline SV+Hue picker widget built in vanilla JS/canvas. + +## Improvement 1: Gear Icon Animation + +### Current Behavior + +`.rt-badge--subreddit` has `padding-right: 18px` at all times, reserving space for the gear icon even when it's hidden. This wastes ~12px on every subreddit badge. + +### New Behavior + +The subreddit badge uses `padding-right: 6px` at rest (matching the left padding). On hover, `padding-right` transitions to `18px` over 150ms, smoothly expanding the badge to reveal the gear icon. The gear's opacity transition (0 → 0.6, 150ms) runs in parallel. When the mouse leaves, both transitions reverse — the gear fades out and the badge shrinks. + +The adjacent comments badge shifts right during the expansion. This is natural inline-block flow and at 150ms feels intentional rather than jittery. + +### CSS Changes + +```css +.rt-badge--subreddit { + position: relative; + padding-right: 6px; + transition: padding-right 0.15s; + /* background, border-color, color unchanged */ +} + +.rt-badge--subreddit:hover { + padding-right: 18px; +} +``` + +The gear icon CSS (`.rt-badge-gear`) is unchanged — it remains `position: absolute; right: 3px` and animates opacity on parent hover. + +### File Changes + +| File | Change | +|---|---| +| `content/styles/badges.css` | Change `.rt-badge--subreddit` `padding-right` from `18px` to `6px`, add `transition: padding-right 0.15s`, add `.rt-badge--subreddit:hover { padding-right: 18px }`. | + +No JS changes required. + +--- + +## Improvement 2: Custom Color Picker Widget + +### Problem + +The native `` delegates to the OS-level color picker. On macOS, this opens a separate floating panel that is detached from the popover context, has limited precision, and feels foreign to the extension's UI. + +### Solution + +Replace all `` elements — in both the inline popover and the extension popup — with a custom SV+Hue picker widget. The picker renders inline within the containing UI (popover or popup), provides full color control, and stays visually consistent with the extension's design. + +### Picker Anatomy + +``` +┌──────────────────────────────┐ +│ ┌──────────────────────┐ │ +│ │ │ │ ← SV square (saturation-value) +│ │ [cursor ●] │ │ Click/drag to pick S + V +│ │ │ │ Canvas-rendered for current hue +│ └──────────────────────┘ │ +│ ┌──────────────────────────┐ │ +│ │ ← hue slider → │ │ ← Hue bar (horizontal rainbow) +│ └──────────────────────────┘ │ Click/drag to change hue +└──────────────────────────────┘ +``` + +### Color Model + +The picker uses HSV internally (hue, saturation, value). HSV maps naturally to the SV square (X = saturation, Y = value) and hue bar (X = hue). Colors are converted to/from hex (`#rrggbb`) for storage, display, and sync with the hex text input. + +### SV Square + +A `` element, redrawn whenever the hue changes. The canvas renders a two-dimensional gradient: + +- Horizontal axis: saturation (0% at left → 100% at right) +- Vertical axis: value/brightness (100% at top → 0% at bottom) + +The gradient is drawn by filling the canvas with the current hue at full saturation, then overlaying a white-to-transparent horizontal gradient and a transparent-to-black vertical gradient. A small circular cursor indicates the current position. + +**Size:** The SV square fills the available width minus padding. In the 260px popover, this is ~230px. In the 320px popup, ~290px. Height is fixed at 140px regardless of width — this keeps the popover from growing too tall while still providing a usable picking area. The result is a landscape rectangle, not a true square. + +### Hue Bar + +A horizontal strip below the SV square. The background is a static CSS `linear-gradient` cycling through the hue spectrum (red → yellow → green → cyan → blue → magenta → red). A thin vertical indicator shows the current hue position. Click or drag to change hue, which redraws the SV square. + +**Height:** 14px. Full width of the picker. + +### Interaction + +- **Click** on the SV square sets saturation and value immediately. +- **Drag** on the SV square continuously updates saturation and value (tracks `mousemove` after `mousedown`, stops on `mouseup` or mouse leaving the document). +- **Click/drag** on the hue bar changes hue and redraws the SV square. +- The hex text input stays bidirectionally synced: valid hex input updates the picker cursor and hue position; picker changes update the hex input. + +### Integration with Popover + +Each color row in the popover currently has: + +``` +Label [] [] +``` + +This changes to: + +``` +Label [
] [] +``` + +The swatch `
` is a small colored rectangle (same size as the old color input: 28×20px) showing the current color. Clicking the swatch opens the picker inline, appearing below the clicked row within the popover. The picker slides into view — it's part of the popover DOM, so the popover grows vertically to accommodate it. + +Only one picker is open at a time within the popover. Clicking a different row's swatch closes the current picker and opens the new one. Clicking the same swatch while its picker is already open toggles the picker closed. + +When the picker is open, changes are reflected live in: +- The swatch `
` for that row +- The hex text input for that row +- The popover's live preview badge + +### Integration with Popup + +The popup uses the same picker widget. Each `` in the popup (global colors + per-subreddit overrides) is replaced with a clickable swatch `
`. Clicking opens the picker inline below the control, within the popup's scrollable area. + +The popup loads the picker module via a `` before `popup.js`. | +| `popup/popup.js` | Replace `` creation with swatch `
` + picker integration for global colors and per-subreddit overrides. | +| `popup/popup.css` | Add picker styles for the popup context (same classes, scoped by parent). | +| `manifest.json` | Add `content/tweaks/color-picker.js` to content scripts array (before `color-popover.js`). | + +--- + +## Edge Cases + +- **Picker open + popover dismiss:** If the picker is open when the popover is dismissed (Cancel, Escape, click-outside), the popover hides and the picker is hidden with it. No orphaned state. +- **Picker open + mode toggle:** Switching light/dark mode in the popover closes any open picker and loads the new mode's values into the swatches. +- **Popup scroll:** The picker is inline in the DOM flow, so it scrolls naturally with the popup's controls. +- **Multiple pickers:** Only one picker open at a time in any context (popover or popup). Opening a new one closes the previous. +- **Hex input while picker is open:** Typing a valid hex into the text input updates the picker's cursor and hue position in real time. +- **Invalid hex while picker is open:** The hex input shows the error border (existing behavior). The picker position is not updated until a valid hex is entered. +- **Very small viewport:** The SV square has a minimum size of 100×100px. If the container is too narrow, the picker still works but may feel cramped. +- **Color precision:** HSV→hex conversion rounds to the nearest integer per channel. This can cause minor rounding differences when round-tripping hex→HSV→hex, but the difference is imperceptible (at most ±1 per channel). + +## Testing + +Manual testing additions (extends the existing 20-item checklist): + +1. Hovering over a subreddit badge smoothly expands the badge to reveal the gear (no gap at rest). +2. The comments badge shifts right during the expansion and back when hover ends. +3. The animation is ~150ms and feels snappy, not laggy. +4. Clicking a color swatch in the popover opens the SV+Hue picker inline. +5. Dragging on the SV square updates the hex input and live preview in real time. +6. Dragging on the hue bar changes the SV square's base color. +7. Only one picker is open at a time — clicking another swatch closes the first. +8. Typing a valid hex updates the picker cursor position. +9. Typing an invalid hex shows the error border but doesn't move the picker. +10. Closing the popover while the picker is open works cleanly. +11. Mode toggle closes any open picker. +12. In the popup, all global color pickers use the custom picker. +13. In the popup, per-subreddit color pickers use the custom picker. +14. The picker renders correctly in dark mode. +15. Color values round-trip correctly (pick → save → reopen → same position).