Differentiate color picker icons by type (text / fill / background / stroke / border) #158

Open
opened 2026-05-06 10:46:57 +00:00 by AhmedHanafy725 · 3 comments
Member

The selection toolbar exposes several color pickers — Text color, Fill color, Stroke color, Background color, Border color — but every picker renders as the same circular swatch (wb-pt-color-swatch). Only the hover tooltip distinguishes them. Users have to hover each swatch to know what it controls.

Expected

Each color picker should be visually distinguishable at a glance by a small type indicator overlaid on (or next to) the swatch:

  • Text — an A glyph (or bi-type icon) on top of the swatch.
  • Fill / Background — a filled-square indicator (or bi-square-fill).
  • Stroke / Border — a square-outline indicator (or bi-square).

The tooltip stays as a fallback.

Actual

All color pickers render as identical circular swatches, only differentiated by tooltip text on hover.

Notes

The current helper is _colorTriggerWithTitle(value, onChange, palette, title, hasNoFill) in crates/hero_whiteboard_ui/static/web/js/whiteboard/selection_toolbar.js. Adding a kind parameter (text / fill / stroke) and rendering a small icon overlay inside the swatch would give every call site a distinct visual without breaking the existing API.

Sweep all call sites and pass the right kind:

  • Text colortext
  • Fill color, Background colorfill
  • Stroke color, Border colorstroke
The selection toolbar exposes several color pickers — Text color, Fill color, Stroke color, Background color, Border color — but every picker renders as the same circular swatch (`wb-pt-color-swatch`). Only the hover tooltip distinguishes them. Users have to hover each swatch to know what it controls. ## Expected Each color picker should be visually distinguishable at a glance by a small type indicator overlaid on (or next to) the swatch: - **Text** — an `A` glyph (or `bi-type` icon) on top of the swatch. - **Fill / Background** — a filled-square indicator (or `bi-square-fill`). - **Stroke / Border** — a square-outline indicator (or `bi-square`). The tooltip stays as a fallback. ## Actual All color pickers render as identical circular swatches, only differentiated by tooltip text on hover. ## Notes The current helper is `_colorTriggerWithTitle(value, onChange, palette, title, hasNoFill)` in `crates/hero_whiteboard_ui/static/web/js/whiteboard/selection_toolbar.js`. Adding a `kind` parameter (`text` / `fill` / `stroke`) and rendering a small icon overlay inside the swatch would give every call site a distinct visual without breaking the existing API. Sweep all call sites and pass the right `kind`: - `Text color` → `text` - `Fill color`, `Background color` → `fill` - `Stroke color`, `Border color` → `stroke`
Author
Member

Implementation Spec

Audit (selection_toolbar.js)

  • _buildColorTrigger(initial, onPick, palette, allowTransparent) ~L554 — creates the toolbar trigger button (wb-pt-color-trigger) with an inner <span class="swatch">. The swatch we want to decorate.
  • _colorTriggerWithTitle(initial, onPick, palette, tooltip, allowTransparent) ~L732 — wraps _buildColorTrigger with title + aria-label. Add kind here.
  • _applySwatchVisual(swatchEl, value) ~L462 — toggles is-no-fill and sets style.background. The new overlay icon is a sibling of the style.background, so this repaint path doesn't wipe it.

Call sites and their semantic kind

Line Context Kind
~818 sticky color fill
~864 text fill text
~907 shape stroke stroke
~916 shape fill (allowTransparent) fill
~934 shape text text
~964 drawing line stroke stroke
~1035 document text text
~1041 document background (allowTransparent) fill
~1050 document border stroke
~1133 kanban column color (direct _buildColorTrigger) fill
~1203 mindmap tree color fill
~1333 connector line color stroke

Visual choice: Bootstrap Icon overlay

Bootstrap Icons (bi-type, bi-square-fill, bi-square) are already loaded by both base.html and web/base_web.html and the rest of the toolbar uses the same icon vocabulary. The overlay sits inside the 18×18 swatch with mix-blend-mode: difference so it stays legible against any color, and falls back to var(--wb-text) on the is-no-fill checker state. No theme branching needed.

Not chosen: CSS pseudo-glyphs (loses icon-vocabulary alignment) or different swatch shapes (muddles the "this is a color chip" affordance).

Files to Modify

  • crates/hero_whiteboard_ui/static/web/js/whiteboard/selection_toolbar.js — extend helpers, sweep call sites.
  • crates/hero_whiteboard_ui/static/web/css/selection_toolbar.css — add overlay rules (the swatch styles live in this file, not whiteboard.css).

No template changes (Bootstrap Icons already loaded).

Implementation Plan

Step 1 — Extend the helpers

Files: selection_toolbar.js

  • New signature: _buildColorTrigger(initial, onPick, palette, allowTransparent, kind). kind optional, one of 'text' | 'fill' | 'stroke'. Default 'fill'. Unknown values fall back to 'fill'.
  • Inside _buildColorTrigger, after creating the inner .swatch, append:
    var overlay = _el('i', 'wb-pt-color-kind wb-pt-color-kind--' + kind + ' bi ' + iconClassFor(kind), { 'aria-hidden': 'true' });
    swatch.appendChild(overlay);
    
    where iconClassFor: 'text''bi-type', 'fill''bi-square-fill', 'stroke''bi-square'.
  • Also extend _colorTriggerWithTitle to take kind as a 6th arg and forward it.
  • kind placed last in both signatures so existing callers that don't pass it keep working (default 'fill').

Step 2 — Sweep call sites

Update each call to pass the right kind from the audit table above.

Step 3 — CSS in selection_toolbar.css

Add next to existing .wb-pt-color-trigger .swatch rules:

.wb-pt-color-trigger .swatch { position: relative; }
.wb-pt-color-kind {
  position: absolute; inset: 0;
  display: flex; align-items: center; justify-content: center;
  font-size: 12px; line-height: 1;
  color: #ffffff; mix-blend-mode: difference;
  pointer-events: none;
  text-shadow: 0 0 1px rgba(0, 0, 0, 0.35);
}
.wb-pt-color-kind--stroke { font-size: 13px; }
/* No-fill checker pattern — switch off blend so the icon stays readable. */
.wb-pt-color-trigger .swatch.is-no-fill .wb-pt-color-kind {
  mix-blend-mode: normal;
  color: var(--wb-text);
  text-shadow: none;
}

The popover grid swatches (.wb-pt-color-swatch inside _buildColorPopover) are NOT decorated — those represent specific color choices, not the picker's purpose.

Acceptance Criteria

  • Every Text trigger shows A (bi-type).
  • Every Fill / Background / Sticky / Tree / Kanban column trigger shows a filled square.
  • Every Stroke / Border / Connector trigger shows an outlined square.
  • Icon is centered in the 18×18 swatch, ~12 px tall.
  • Readable against arbitrary swatch colors (mix-blend-mode: difference).
  • Readable against the no-fill checker pattern (uses var(--wb-text)).
  • Theme switch (light ↔ dark) keeps it legible without re-running JS.
  • Hover / focus / click on the trigger behave identically to today.
  • The popover (color grid + custom hex row) is unchanged.
  • Existing callers that don't pass kind get bi-square-fill and otherwise identical DOM/behavior.

What NOT to break

  • Public-ish API of _colorTriggerWithTitle and _buildColorTrigger: backwards compatible (new arg appended at the end).
  • _applySwatchVisual repaint path: only mutates style.background and the is-no-fill class on the swatch; the overlay is a sibling and survives.
  • Click-to-open: overlay is pointer-events: none.
  • Accessibility: tooltip / aria-label on the trigger unchanged; overlay is aria-hidden="true".
  • The popover (_buildColorPopover) DOM and its grid: untouched.
  • Bootstrap Icons asset wiring: already in place.
## Implementation Spec ### Audit (selection_toolbar.js) - `_buildColorTrigger(initial, onPick, palette, allowTransparent)` ~L554 — creates the toolbar trigger button (`wb-pt-color-trigger`) with an inner `<span class="swatch">`. The swatch we want to decorate. - `_colorTriggerWithTitle(initial, onPick, palette, tooltip, allowTransparent)` ~L732 — wraps `_buildColorTrigger` with title + aria-label. Add `kind` here. - `_applySwatchVisual(swatchEl, value)` ~L462 — toggles `is-no-fill` and sets `style.background`. The new overlay icon is a sibling of the `style.background`, so this repaint path doesn't wipe it. #### Call sites and their semantic kind | Line | Context | Kind | |---|---|---| | ~818 | sticky color | `fill` | | ~864 | text fill | `text` | | ~907 | shape stroke | `stroke` | | ~916 | shape fill (allowTransparent) | `fill` | | ~934 | shape text | `text` | | ~964 | drawing line stroke | `stroke` | | ~1035 | document text | `text` | | ~1041 | document background (allowTransparent) | `fill` | | ~1050 | document border | `stroke` | | ~1133 | kanban column color (direct `_buildColorTrigger`) | `fill` | | ~1203 | mindmap tree color | `fill` | | ~1333 | connector line color | `stroke` | ### Visual choice: Bootstrap Icon overlay Bootstrap Icons (`bi-type`, `bi-square-fill`, `bi-square`) are already loaded by both `base.html` and `web/base_web.html` and the rest of the toolbar uses the same icon vocabulary. The overlay sits inside the 18×18 swatch with `mix-blend-mode: difference` so it stays legible against any color, and falls back to `var(--wb-text)` on the `is-no-fill` checker state. No theme branching needed. Not chosen: CSS pseudo-glyphs (loses icon-vocabulary alignment) or different swatch shapes (muddles the "this is a color chip" affordance). ### Files to Modify - `crates/hero_whiteboard_ui/static/web/js/whiteboard/selection_toolbar.js` — extend helpers, sweep call sites. - `crates/hero_whiteboard_ui/static/web/css/selection_toolbar.css` — add overlay rules (the swatch styles live in this file, not `whiteboard.css`). No template changes (Bootstrap Icons already loaded). ### Implementation Plan #### Step 1 — Extend the helpers Files: `selection_toolbar.js` - New signature: `_buildColorTrigger(initial, onPick, palette, allowTransparent, kind)`. `kind` optional, one of `'text' | 'fill' | 'stroke'`. Default `'fill'`. Unknown values fall back to `'fill'`. - Inside `_buildColorTrigger`, after creating the inner `.swatch`, append: ```js var overlay = _el('i', 'wb-pt-color-kind wb-pt-color-kind--' + kind + ' bi ' + iconClassFor(kind), { 'aria-hidden': 'true' }); swatch.appendChild(overlay); ``` where `iconClassFor`: `'text'`→`'bi-type'`, `'fill'`→`'bi-square-fill'`, `'stroke'`→`'bi-square'`. - Also extend `_colorTriggerWithTitle` to take `kind` as a 6th arg and forward it. - `kind` placed last in both signatures so existing callers that don't pass it keep working (default `'fill'`). #### Step 2 — Sweep call sites Update each call to pass the right `kind` from the audit table above. #### Step 3 — CSS in `selection_toolbar.css` Add next to existing `.wb-pt-color-trigger .swatch` rules: ```css .wb-pt-color-trigger .swatch { position: relative; } .wb-pt-color-kind { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; font-size: 12px; line-height: 1; color: #ffffff; mix-blend-mode: difference; pointer-events: none; text-shadow: 0 0 1px rgba(0, 0, 0, 0.35); } .wb-pt-color-kind--stroke { font-size: 13px; } /* No-fill checker pattern — switch off blend so the icon stays readable. */ .wb-pt-color-trigger .swatch.is-no-fill .wb-pt-color-kind { mix-blend-mode: normal; color: var(--wb-text); text-shadow: none; } ``` The popover grid swatches (`.wb-pt-color-swatch` inside `_buildColorPopover`) are NOT decorated — those represent specific color choices, not the picker's purpose. ### Acceptance Criteria - [ ] Every Text trigger shows `A` (bi-type). - [ ] Every Fill / Background / Sticky / Tree / Kanban column trigger shows a filled square. - [ ] Every Stroke / Border / Connector trigger shows an outlined square. - [ ] Icon is centered in the 18×18 swatch, ~12 px tall. - [ ] Readable against arbitrary swatch colors (mix-blend-mode: difference). - [ ] Readable against the no-fill checker pattern (uses `var(--wb-text)`). - [ ] Theme switch (light ↔ dark) keeps it legible without re-running JS. - [ ] Hover / focus / click on the trigger behave identically to today. - [ ] The popover (color grid + custom hex row) is unchanged. - [ ] Existing callers that don't pass `kind` get `bi-square-fill` and otherwise identical DOM/behavior. ### What NOT to break - Public-ish API of `_colorTriggerWithTitle` and `_buildColorTrigger`: backwards compatible (new arg appended at the end). - `_applySwatchVisual` repaint path: only mutates `style.background` and the `is-no-fill` class on the swatch; the overlay is a sibling and survives. - Click-to-open: overlay is `pointer-events: none`. - Accessibility: tooltip / aria-label on the trigger unchanged; overlay is `aria-hidden="true"`. - The popover (`_buildColorPopover`) DOM and its grid: untouched. - Bootstrap Icons asset wiring: already in place.
Author
Member

Validation

Check Result
Files changed 2 (selection_toolbar.js +27/-12, selection_toolbar.css +20)
cargo check --workspace pass
cargo test --workspace --lib pass
## Validation | Check | Result | |---|---| | Files changed | 2 (selection_toolbar.js +27/-12, selection_toolbar.css +20) | | `cargo check --workspace` | pass | | `cargo test --workspace --lib` | pass |
Author
Member

Implementation summary

Changes

  • crates/hero_whiteboard_ui/static/web/js/whiteboard/selection_toolbar.js

    • _buildColorTrigger now takes a 5th kind parameter ('text' | 'fill' | 'stroke', default 'fill') and appends a Bootstrap-icon <i> overlay (bi-type / bi-square-fill / bi-square) inside the swatch span.
    • _colorTriggerWithTitle takes the same kind as a 6th parameter and forwards it.
    • All 13 call sites (12 _colorTriggerWithTitle + 1 direct _buildColorTrigger for kanban columns) now pass an explicit kind: text inputs use 'text'; sticky / shape fill / document background / mindmap tree / kanban column use 'fill'; shape stroke / drawing stroke / document border / connector all use 'stroke'.
  • crates/hero_whiteboard_ui/static/web/css/selection_toolbar.css

    • Added .wb-pt-color-trigger .swatch { position: relative; } plus .wb-pt-color-kind rules (absolute-positioned overlay, mix-blend-mode: difference so the glyph stays legible against any swatch color, with a fallback path for the is-no-fill checker pattern that switches to var(--wb-text)).

No template changes (Bootstrap Icons already loaded). The popover grid swatches are intentionally left undecorated — those represent specific color choices, not the picker's purpose.

API is backwards-compatible: any caller that doesn't pass kind keeps working and gets the bi-square-fill default.

Validation

  • cargo check --workspace: pass
  • cargo test --workspace --lib: pass
  • Diff scope: 2 files, +43/-15

Notes

UI-only change. Verify visually: select a sticky → background swatch shows a filled square. Select text → swatch shows A. Select a shape → fill swatch is filled square, stroke swatch is hollow square. Switch theme — icons stay legible thanks to mix-blend-mode.

## Implementation summary ### Changes - `crates/hero_whiteboard_ui/static/web/js/whiteboard/selection_toolbar.js` - `_buildColorTrigger` now takes a 5th `kind` parameter (`'text' | 'fill' | 'stroke'`, default `'fill'`) and appends a Bootstrap-icon `<i>` overlay (`bi-type` / `bi-square-fill` / `bi-square`) inside the swatch span. - `_colorTriggerWithTitle` takes the same `kind` as a 6th parameter and forwards it. - All 13 call sites (12 `_colorTriggerWithTitle` + 1 direct `_buildColorTrigger` for kanban columns) now pass an explicit `kind`: text inputs use `'text'`; sticky / shape fill / document background / mindmap tree / kanban column use `'fill'`; shape stroke / drawing stroke / document border / connector all use `'stroke'`. - `crates/hero_whiteboard_ui/static/web/css/selection_toolbar.css` - Added `.wb-pt-color-trigger .swatch { position: relative; }` plus `.wb-pt-color-kind` rules (absolute-positioned overlay, `mix-blend-mode: difference` so the glyph stays legible against any swatch color, with a fallback path for the `is-no-fill` checker pattern that switches to `var(--wb-text)`). No template changes (Bootstrap Icons already loaded). The popover grid swatches are intentionally left undecorated — those represent specific color choices, not the picker's purpose. API is backwards-compatible: any caller that doesn't pass `kind` keeps working and gets the `bi-square-fill` default. ### Validation - `cargo check --workspace`: pass - `cargo test --workspace --lib`: pass - Diff scope: 2 files, +43/-15 ### Notes UI-only change. Verify visually: select a sticky → background swatch shows a filled square. Select text → swatch shows `A`. Select a shape → fill swatch is filled square, stroke swatch is hollow square. Switch theme — icons stay legible thanks to mix-blend-mode.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
lhumina_code/hero_whiteboard#158
No description provided.