Skip to content

Color Management

I admit it, my early code was a nightmare to tweak because i designed it as i went along. With AI, i crafted a centralized system. One place to confine the mess. Styles computes all colors from state snapshots. Remarkably simple code.

Table of Contents

Overview

Centralized color management system driven by the Hits manager and hit targets. Colors are computed based on component type, state (hover/selected/none), and interaction context.

Known Issues / Future Work

  • Glow buttons have some click handling issues (separate from color management)
  • Consider adding tests for Styles computation functions
  • Could further simplify S_Element helper getters if needed
  • Centralize set_forHovering calls - See "Analyze set_forHovering" section above

Core Components

  • widget - Main widget container
  • drag - Drag handle dot
  • reveal - Reveal/expand dot
  • button - UI buttons

States

  • hover - Mouse is over the element
  • selected - grab
  • editing -
  • focus -
  • mouse down -
  • none - Default/base state

Driver

  • Hits Manager (Hits.ts) - Tracks hover state via w_s_hover store
  • Hit Target (S_Hit_Target) - Base class for all interactive elements, provides hover state

Color Properties

Per Component

  • fill - Background/fill color
  • stroke - Border/text color
  • svg_outline_color - SVG outline color
  • border - CSS border style

State Logic

  • Hover state from hits.w_s_hover
  • Selected state from isSelected
  • Inversion logic via isInverted and color_isInverted

Architecture

Current Implementation ✅

  • Colors computed centrally in Styles utility class
  • S_Element and S_Widget getters call Styles methods
  • Components remain reactive via getter pattern
  • set_forHovering() sets hover color and cursor (unchanged)

Implementation Status

  • [x] Define color schema per component/state
  • [x] Centralize color computation logic
  • [x] Create S_Snapshot state object class
  • [x] Migrate widget, drag, reveal, button components
  • [x] Standardize state transitions
  • [x] Document color inheritance rules
  • [ ] Analyze set_forHovering

Define color schema per component/state

Common Fill/Stroke Pattern (Drag, Reveal, Button)

Base setup:

  • element_color - Set via set_forHovering(thing.color, cursor)
  • hoverColor - Computed via colors.hover_special_blend(element_color)
  • color_background - From w_background_color

Fill/Stroke computation (all states except selected/disabled):

  • fill: color_isInverted ? hoverColor : color_background
  • stroke: color_isInverted ? color_background : element_color

Dots (Drag, Reveal)

Properties:

  • fill, stroke - Use common pattern above
  • svg_outline_color - SVG path outline

States:

  • none/hover: svg_outline_color = thing_color
  • grabbed: svg_outline_color = color_background
  • editing: svg_outline_color = black (if light thing_color) or hoverColor (if dark)

Special:

  • Drag: parents_go = stroke, related_color = thing.color
  • Reveal: bulkAlias_go = stroke

Widget

Properties:

  • color - Text/stroke (via colorFor_grabbed_andEditing())
  • background_color - Background fill
  • border - CSS border style

States:

  • none: color = thing_color, background_color = transparent, border = transparent
  • hover: background_color = w_background_color, border = solid ${colors.ofBackgroundFor(thing_color)} 1px
  • grabbed: color = black/white (based on thing_color luminance), background_color = thing_color
  • editing: background_color = w_background_color, border = dashed ${thing_color} 1px
  • focus (radial): background_color = w_background_color, border = solid ${thing_color} 1px

Button

Properties:

  • fill, stroke - Use common pattern above
  • border - CSS border style

Special states:

  • selected: fill = lightblue
  • disabled: fill = transparent, stroke = disabledTextColor
  • editing/focus: border uses thing_color (widget context only)

Centralize color computation logic

Design Summary

Current State:

  • Color logic scattered across S_Element, S_Widget getters
  • Components call update_colors() reactively with local variables
  • Duplication of fill/stroke inversion logic
  • State combinations computed inline

Implemented Architecture:

Created Styles utility class (src/lib/ts/utilities/Styles.ts) with static methods:

  • Takes inputs: S_Snapshot state object, base colors
  • Returns computed color values based on schema
  • Encapsulates all conditional logic in one place
  • Supports all component types (widget, drag, reveal, button)

Implementation Details:

  1. Single computation function per component type

    • Styles.computeWidgetColors(state, thing_color, background_color){ color, background_color, border }
    • Styles.computeDotColors(state, element_color, thing_color, background_color, hoverColor){ fill, stroke, svg_outline_color }
    • Styles.computeButtonColors(state, element_color, background_color, hoverColor, disabledTextColor, border_thickness, has_widget_context, thing_color){ fill, stroke, border }
  2. State object pattern

    • S_Snapshot class (src/lib/ts/state/S_Snapshot.ts) - "state of go" - transient state of user attention/intention
    • Encapsulates all state flags needed for color computation
    • Constructor accepts S_Hit_Target | Identifiable | undefined plus optional flags
    • Getters provide: hover, grabbed, editing, focus, thing_color
    • Special hover logic: for widgets, also checks if title with same ancestry is hovered
  3. Getters are thin wrappers

    • S_Element/S_Widget getters create S_Snapshot instance and call Styles methods
    • Components remain reactive via getter pattern
    • Logic centralized in Styles, state collected in S_Snapshot
  4. Preserve reactive behavior

    • Getters still reactive - Svelte tracks dependencies
    • No changes to component reactive blocks needed
    • All color logic testable in isolation

Benefits:

  • Single source of truth for color computation
  • Easier to modify color behavior globally
  • Testable in isolation
  • Reduces code duplication
  • Clearer separation of concerns

Impact on State Objects

S_Hit_Target (base class):

  • Keep: hoverColor, element_color, set_forHovering() - these provide inputs to Styles
  • Keep: isHovering getter - needed for state collection
  • No change: Hit testing and cursor logic remain here

S_Element (Implemented):

  • Kept as inputs: color_background, isDisabled, isSelected, isInverted, subtype
  • Getters now call Styles: fill, stroke, svg_outline_color, border create S_Snapshot and call Styles.computeDotColors() or Styles.computeButtonColors()
  • Helper getters: s_go, thing_color, dotColors_forElement, buttonColors_forElement reduce duplication
  • Removed: Old inline color logic from getters
  • Actual implementation:
    typescript
    get fill(): string {
      if (this.asTransparent) {
        return 'transparent';
      } else if (this.isADot) {
        return this.dotColors_forElement.fill;
      } else if (this.isAControl) {
        return this.buttonColors_forElement.fill;
      } else {
        return this.color_isInverted ? this.hoverColor : this.isSelected ? 'lightblue' : this.color_background;
      }
    }

S_Widget (Implemented):

  • Getters now call Styles: color, background_color, border create S_Snapshot and call Styles.computeWidgetColors()
  • Removed: colorFor_grabbed_andEditing(), isFilled, shows_border, isRadial_focus helper methods
  • Actual implementation:
    typescript
    get color(): string {
      const state = new S_Snapshot(this, this.isDisabled, this.isSelected, this.isInverted, this.subtype);
      return Styles.computeWidgetColors(state, this.thing_color, get(colors.w_background_color)).color;
    }

State Collection Pattern:

  • Each getter collects relevant state from this, this.ancestry, and stores
  • Passes complete S_Snapshot state object to Styles
  • Styles handles all conditional logic
  • State objects become data containers + thin computation wrappers

S_Snapshot (Implemented):

The S_Snapshot class (src/lib/ts/state/S_Snapshot.ts) - "state of go for style" - represents the transient state of user attention/intention. It's a snapshot of flags and relevances needed for style (for now, color) computation:

typescript
export default class S_Snapshot {
  hit_target?: S_Hit_Target;      // Hit target for hover detection (includes type + identifiable)
  identifiable?: Identifiable;     // Provides: isGrabbed, isEditing, isFocus, thing.color
  isInverted?: boolean;
  isDisabled?: boolean;
  isSelected?: boolean;
  subtype?: string;                // For special cases (e.g., T_Control.details)

  constructor(hit_target: S_Hit_Target | Identifiable | undefined, 
              isDisabled?: boolean, 
              isSelected?: boolean, 
              isInverted?: boolean, 
              subtype?: string)

  // Getters:
  get hover(): boolean             // Checks hit_target.isHovering or compares identifiable.id with hits.w_s_hover
  get grabbed(): boolean           // Returns ancestry?.isGrabbed ?? false
  get editing(): boolean           // Returns ancestry?.isEditing ?? false
  get focus(): boolean             // Returns ancestry?.isFocus ?? false
  get thing_color(): string        // Returns ancestry?.thing?.color ?? k.empty
}

Key characteristics:

  • "State of go" - Represents transient user attention/intention state (hover, grab, edit, focus)
  • Constructor accepts S_Hit_Target or Identifiable - handles both patterns
  • hover getter has special logic: for widgets, checks if title with same ancestry is hovered (fixes widget title hover bug)
  • grabbed, editing, focus, thing_color safely accessed via ancestry getter (casts identifiable to Ancestry)
  • State object is created fresh on each getter call (no caching needed due to Svelte reactivity)
  • All properties are optional except those provided by getters

Standardize state transitions

Document color inheritance rules

Color Sources

Colors are sourced from different places depending on the color type and component:

Color TypeSourceUsed ByNotes
thing_colorancestry.thing?.colorWidget, Drag, RevealInherited from the Thing object associated with the ancestry
element_colorSet via set_forHovering(color, cursor)Drag, Reveal, ButtonSet explicitly per component, typically matches thing.color
background_colorcolors.w_background_color storeAll componentsGlobal background color, shared across entire UI
hoverColorcolors.hover_special_blend(element_color)Drag, Reveal, ButtonComputed from element_color when hover state detected

Inheritance Patterns

Thing Color Inheritance:

  • Widgets, drag dots, and reveal dots inherit their base color from ancestry.thing.color
  • Accessed via S_Snapshot.thing_color getter
  • If no thing exists, defaults to k.empty (empty string)
  • This is the primary color identity for a widget/item

Element Color Pattern:

  • Drag and reveal components call set_forHovering(thing.color, cursor) to set element_color
  • Buttons use their own element_color (may differ from thing color)
  • element_color is the base stroke/outline color when not hovered
  • Set once per component instance (not reactive to thing color changes)

Hover Color Computation:

  • hoverColor is computed from element_color via colors.hover_special_blend()
  • Same computation for all components (drag, reveal, button)
  • Computed on-demand when needed (via S_Hit_Target.hoverColor getter)

Background Color:

  • All components share the same global background from colors.w_background_color store
  • Reactive - changes propagate to all components automatically
  • Used for fill colors and contrast calculations

Inheritance Rules

  1. Thing color flows from Ancestry:

    • ancestry.thing.colorS_Snapshot.thing_colorStyles computation functions
    • Widgets always use thing color for text/base color
    • Dots use thing color for outline color (when not grabbed/editing)
  2. Element color is component-specific:

    • Each component sets its own element_color via set_forHovering()
    • Not inherited from parent/ancestry
    • Typically matches thing.color but can differ (e.g., buttons)
  3. Background color is global:

    • Single source: colors.w_background_color store
    • No inheritance - all components read from same store
    • Changes affect entire UI simultaneously
  4. Hover color is computed, not inherited:

    • Derived from element_color, not inherited
    • Computation is consistent: colors.hover_special_blend(element_color)
    • Same algorithm for all components

No True Inheritance Hierarchy

Important: Unlike DOM CSS inheritance, colors do NOT cascade from parent to child:

  • Child widgets do not inherit colors from parent widgets
  • Each component independently computes its colors based on its own state
  • Shared colors (like background) come from global stores, not parent components

Color Relationships:

ancestry.thing.color  →  thing_color  →  Styles.compute...Colors()

    (set via set_forHovering)

   element_color  →  hoverColor (computed)  →  Styles.compute...Colors()

colors.w_background_color (global store)  →  Styles.compute...Colors()

Current Implementation Status

Implemented correctly:

  • Thing color accessed via S_Snapshot.thing_color getter (safe null handling)
  • Element color set explicitly per component
  • Background color from global store
  • Hover color computed consistently

No changes needed - Current implementation follows clear patterns without problematic inheritance hierarchies.

Current State Flow

Color states transition based on user interactions and system events. Each state is independent but can combine to produce the final color output.

State Sources:

  1. hover - From hits.w_s_hover store, managed by Hits manager

    • Transitions: none → hover (mouse enters), hover → none (mouse leaves)
    • Detected via hit testing on mouse move
    • Persists while mouse over element
  2. grabbed - From ancestry.isGrabbed, managed by x.si_grabs (UX manager)

    • Transitions: none → grabbed (item grabbed), grabbed → none (item released)
    • Set via ancestry.grab() methods
    • Persists until explicitly released
  3. editing - From ancestry.isEditing, managed by s.w_s_title_edit (Stores)

    • Transitions: none → editing (edit starts), editing → none (edit stops)
    • Set when title editing begins
    • Persists until edit completes/cancels
  4. focus - From ancestry.isFocus, managed by s.w_ancestry_focus (Stores)

    • Transitions: none → focus (focus set), focus → none (focus changes)
    • Set via ancestry.becomeFocus()
    • Single focus at a time (moving focus clears previous)
  5. selected - From isSelected property on S_Element

    • Transitions: none → selected (selection set), selected → none (deselected)
    • Used for buttons in selection contexts
    • Component-level state (not ancestry-based)
  6. disabled - From isDisabled property on S_Element

    • Transitions: none → disabled (disabled set), disabled → none (enabled)
    • Component-level state
    • Typically static per component instance

State Transitions:

StateOnOff
hoverMouse enters elementMouse leaves element
grabbedItem grabbed (ancestry.grab())Item released
editingEditing startsEditing stops
focusFocus set (ancestry.becomeFocus())Focus changes to another item
selectedisSelected = trueisSelected = false
disabledisDisabled = trueisDisabled = false

Notes:

  • All states can combine simultaneously (e.g., hover + grabbed + editing)
  • States transition independently - no coordination needed between state managers
  • Color computation in Styles.ts handles all state combinations with proper priority

State Collection Pattern:

States are collected via S_Snapshot getters which read from:

  • hover: S_Hit_Target.isHoveringhits.w_s_hover store
  • grabbed: ancestry.isGrabbedx.si_grabs (UX manager)
  • editing: ancestry.isEditings.w_s_title_edit (Stores)
  • focus: ancestry.isFocuss.w_ancestry_focus (Stores)
  • selected: this.isSelected (component property)
  • disabled: this.isDisabled (component property)

Current Implementation:

✅ States are collected reactively in getters ✅ S_Snapshot provides unified access to all states ✅ Styles computation functions handle all state combinations ✅ No state transition coordination needed - each state is independent

Migration Strategy

Current State:

  • State transitions are handled by respective managers/stores
  • No centralized state transition logic
  • States transition independently
  • Color computation handles all combinations

Potential Improvements:

  1. State Transition Events

    • Consider emitting events when states transition
    • Could enable analytics/logging
    • Could trigger side effects
    • Recommendation: Not needed for color management alone
  2. State Validation

    • Validate impossible combinations (e.g., disabled + editing)
    • Recommendation: Handle in Styles computation if needed (currently not an issue)
  3. State Transition Hooks

    • Allow components to react to state transitions
    • Recommendation: Use existing reactive patterns (Svelte reactivity already handles this)
  4. State History/Debouncing

    • Track state transition history
    • Debounce rapid transitions
    • Recommendation: Not needed - current reactive system handles rapid changes

Migration Steps (if needed):

If we need to standardize state transitions in the future:

  1. Define State Transition Interface

    typescript
    interface StateTransition {
      from: string;
      to: string;
      timestamp: number;
      source: string;  // 'hover', 'grab', 'edit', 'focus', etc.
    }
  2. Add Transition Tracking (Optional)

    • Track transitions in respective managers
    • Log or emit events
    • Use for debugging/analytics
  3. Add Transition Validation (Optional)

    • Validate transitions in Styles computation
    • Handle edge cases (e.g., disabled + editing)
  4. Add Transition Hooks (Optional)

    • Allow components to register transition callbacks
    • Use for complex side effects

Recommendation:

No changes needed - Current implementation is sufficient:

  • States transition independently (correct design)
  • Reactive system handles state changes automatically
  • Color computation already handles all combinations
  • Adding transition tracking would add complexity without clear benefit

Future Considerations:

  • If we add state transition analytics → add tracking to managers
  • If we need transition validation → add to Styles computation
  • If we need transition hooks → use Svelte reactive statements (already supported)

Implementation Notes

  • ✅ Colors respond to: hover, grab, edit, focus, selected, disabled
  • ✅ Inversion logic: color_isInverted = (isInverted ?? false) !== hover (computed in Styles)
  • ✅ Background color from colors.w_background_color
  • ✅ Thing color from ancestry.thing?.color (accessed via S_Snapshot.thing_color getter)
  • ✅ Widget title hover: S_Snapshot.hover checks if widget/title with same ancestry is hovered
  • ✅ Deprecated code removed: colorFor_grabbed_andEditing(), isRadial_focus, unused svg_outline_color fallback logic
  • ✅ Only widgets can have selected, editing or focus state (via ancestry)

Styles manager (Implementation)

Current Pattern: Managers are singleton classes exported from Global_Imports:

  • Colors manager - color scheme management, stores, utility functions
  • Hits manager - hit testing, hover detection, click handling
  • UX manager (exported as x) - grab/focus/expand state management
  • Pattern: export class ManagerName { ... } then export const instance = new ManagerName();

Pros of Manager Pattern:

  1. Consistency with existing codebase

    • Follows established architectural pattern
    • Integrates seamlessly with Global_Imports
    • Matches how other computation/state logic is organized
  2. Single instance, shared access

    • One instance imported globally (e.g., styles.computeWidgetColors())
    • No need to pass instances around
    • Easy to access from any component
  3. Future extensibility

    • Can add state/stores if needed (e.g., caching, computed color themes)
    • Can add lifecycle methods (initialization, cleanup)
    • Room to grow beyond pure computation
  4. Clear ownership

    • Explicitly owns color computation domain
    • Clear separation from Colors manager (scheme/stores) vs Styles (computation logic)

Cons of Manager Pattern:

  1. Overhead for pure functions

    • Styles is primarily pure computation (functions that take state → return colors)
    • Manager pattern adds class instantiation overhead
    • State objects (S_Snapshot) already passed in - no need for manager to hold state
  2. Alternative: Static utility class

    • Could be export class Styles { static computeWidgetColors(...) }
    • No instance needed for pure functions
    • Simpler: Styles.computeWidgetColors() vs styles.computeWidgetColors()
  3. Separation from Colors manager

    • Colors manager handles color schemes, stores, utility functions
    • Styles handles computation logic
    • Might be confusing having two color-related managers
    • Could alternatively live as methods on existing Colors manager
  4. No state management needed

    • Unlike Hits (manages stores, timers) or UX (manages S_Items), Styles has no persistent state
    • Pure computation doesn't benefit from singleton instance
    • All state comes from S_Snapshot parameter

Implementation:

Used static utility class approach:

  • export default class Styles { static computeWidgetColors(...), static computeDotColors(...), static computeButtonColors(...) }
  • Located in src/lib/ts/utilities/Styles.ts
  • Pure computation functions - no instance state needed
  • Easy to test and reason about
  • Clear separation: Styles computes colors, Colors manager handles schemes/stores

File Structure:

  • src/lib/ts/utilities/Styles.ts - Color computation logic
  • src/lib/ts/state/S_Snapshot.ts - State object for passing to Styles
  • src/lib/ts/state/S_Element.ts - Uses Styles for dot/button colors
  • src/lib/ts/state/S_Widget.ts - Uses Styles for widget colors

Design

The t_hover_target property is extracted from the S_Snapshot state object (const t_hover_target = s_go.t_hover_target;) to determine which specific part of a widget is being hovered. This information provides visual treatments that are specific to the hover target type:

TypeTreatment
revealwidget background -> transparent, border -> transparent
titleborder -> thing_color directly
dragborder -> a special blend of thing_color with faint opacity
widgetborder -> default background color computation

This design allows the same widget to display different visual feedback depending on which interactive element within it is being hovered, providing more precise user feedback.

Trade-offs: inject Styles methods into S_Snapshot

Pros of migrating methods into S_Snapshot:

  • Closer to the data: Color computation lives next to the state it depends on, which can make the API feel more "object-oriented" (s_go.widgetColors(...)).
  • Potentially fewer parameters: Some calls could avoid threading S_Snapshot as an argument if methods read internal fields directly.
  • Discoverability from state: Browsing S_Snapshot shows both the state and the operations available on that state.

Cons of migrating methods into S_Snapshot:

  • Mixes concerns: S_Snapshot stops being a simple state snapshot and becomes a behavior-heavy object, blurring the line between “state carrier” and “color engine”.
  • Harder to test in isolation: Pure functions in Styles are easy to unit-test; methods on S_Snapshot risk picking up extra dependencies over time.
  • Tighter coupling: Every change to color logic now touches the state class, increasing churn and making it harder to reuse S_Snapshot for non-color purposes later.
  • Less obvious separation of roles: Right now S_Snapshot = “state of go” and Styles = “color computation”; merging them weakens the mental model and the ability to swap out or extend the style engine independently.

Analyze set_forHovering

Current Usage Patterns

set_forHovering(element_color: string, hoverCursor: string) is called in multiple places:

LocationComponent TypeColor SourceCursorWhen Called
Widget_Drag.svelteDrag dotthing.color'pointer' or 'normal'In update_colors() (reactive)
Widget_Reveal.svelteReveal dotthing.color'pointer'In update_colors() (reactive)
Buttons_Row.svelteButtoncolors.default'pointer'During setup (static)
D_Actions.svelteButtoncolors.default'pointer'During setup (static)
D_Traits.svelteButtoncolors.default'pointer'During setup (static)
D_Selection.svelteButtoncolors.default'pointer'In reactive block (static)
D_Data.svelteButtoncolors.default or 'black''pointer'During setup (static)
Elements.tsControlcolors.default or 'white''pointer'In s_control_forType() (static)
Cluster_Pager.sveltePagercolor (computed)'pointer'During setup (static)

Key Observations

  1. Dots (drag/reveal):

    • Always use thing.color as element_color
    • Called reactively in update_colors() when thing color changes
    • Cursor depends on state ('pointer' if interactive, 'normal' otherwise)
  2. Buttons/Controls:

    • Use colors.default (or occasionally 'white' or 'black')
    • Called once during setup (not reactive)
    • Always use 'pointer' cursor
  3. What set_forHovering does:

    • Sets element_color (base stroke/outline color)
    • Computes hoverColor via colors.hover_special_blend(element_color)
    • Sets hoverCursor (cursor style)

Centralization Opportunities

Option 1: Auto-initialize for dots in S_Element constructor

  • For dots: automatically call set_forHovering(thing_color, 'pointer') in constructor
  • For buttons/controls: require explicit call (current pattern)
  • Pros: Eliminates repetitive calls in Widget_Drag/Widget_Reveal
  • Cons: Cursor logic ('pointer' vs 'normal') is state-dependent, can't be determined in constructor

Option 2: Make element_color a getter for dots

  • For dots: get element_color() returns thing_color (reactive)
  • For buttons/controls: keep explicit element_color property
  • Pros: Fully reactive, no manual updates needed
  • Cons: Requires refactoring S_Hit_Target.element_color from property to getter (breaking change)

Option 3: Centralize in S_Element with setup method

  • Add S_Element.setup_forHovering() that components call
  • For dots: automatically uses thing_color
  • For buttons: accepts explicit color
  • Pros: Single place to call, clear intent
  • Cons: Still requires calls in each component

Option 4: Reactive element_color for dots only

  • Add element_color_override?: string property
  • For dots: get element_color() returns element_color_override ?? thing_color
  • For buttons: set element_color_override explicitly
  • Pros: Backward compatible, reactive for dots
  • Cons: Adds complexity with override pattern

Recommendation

Recommended: Option 2 (Getter for dots) + Helper method

  1. Make element_color a getter in S_Hit_Target:

    typescript
    private _element_color?: string;  // Explicit override
    get element_color(): string {
      // For dots: use thing_color if no override
      if (this.isADot && !this._element_color) {
        return this.ancestry?.thing?.color ?? 'black';
      }
      return this._element_color ?? 'black';
    }
    set element_color(value: string) {
      this._element_color = value;
      this.hoverColor = colors.hover_special_blend(value);
    }
  2. Add set_forHovering() convenience method (keep for backward compatibility):

    • Sets element_color (which now auto-computes hoverColor)
    • Sets hoverCursor
  3. Update components:

    • Dots: Remove set_forHovering() calls from update_colors() - element_color is now reactive
    • Buttons: Keep explicit set_forHovering(colors.default, 'pointer') calls
    • Cursor: For dots, add separate hoverCursor update logic in update_colors() if needed

Benefits:

  • Eliminates ~10+ set_forHovering() calls in drag/reveal components
  • Makes dot colors fully reactive to thing color changes
  • Maintains explicit control for buttons/controls
  • Backward compatible (existing button code unchanged)

Migration Impact:

  • Low risk: Dots become more reactive (improvement)
  • No breaking changes: Buttons continue to work as-is
  • Code reduction: ~20-30 lines eliminated from Widget_Drag/Widget_Reveal

Current Status

Not yet implemented - This is a future optimization opportunity. Current scattered calls work correctly but could be simplified.