Parity: complete Bootstrap behavior for tooltip, popover, and scrollspy #1

Closed
opened 2026-06-23 14:53:30 +00:00 by mik-tf · 8 comments
Owner

The dbcss design rule is:

If Bootstrap does it, this crate should expose a typed Dioxus way to express it. If Bootstrap does not do it, this crate should not invent it.

This issue tracks the current parity gaps where dbcss has a typed component but does not yet expose/implement enough of the Bootstrap behavior. These should not stay as permanent caveats.

References:

Tooltip parity

Current state:

  • Tooltip supports basic text, placement, and hover rendering.
  • Positioning is static CSS relative to the trigger wrapper.
  • There is no viewport collision detection, fallback placement, boundary handling, container/body placement, offset, trigger mode selection, delay, focus handling, or manual control API.

Bootstrap behavior to cover with typed Dioxus props and dbcss-owned implementation:

  • placement: top, bottom, start/end, and auto/fallback behavior equivalent to Bootstrap's Popper-backed behavior.
  • fallback placements and boundary handling so tooltips stay visible near viewport and scroll-container edges.
  • offset support.
  • container/body placement support to avoid clipping and complex layout issues.
  • trigger modes: hover, focus, click, manual, and combined hover+focus default behavior where appropriate.
  • delay show/hide support.
  • custom class support without losing Bootstrap classes.
  • disabled-trigger wrapper guidance or component support.
  • accessible behavior: focus-triggered visibility and predictable role/aria output.

Popover parity

Current state:

  • Popover supports title, body, placement, click toggle, and outside-click close.
  • Positioning is static CSS relative to the trigger wrapper.
  • It does not implement Bootstrap/Popper-style fallback placement, boundary/container handling, offset, trigger modes, delay, or manual control.

Bootstrap behavior to cover with typed Dioxus props and dbcss-owned implementation:

  • same positioning engine as tooltip: viewport-aware placement, fallback placements, boundary, offset, and optional container/body placement.
  • trigger modes: click, focus, hover, manual, and dismiss-on-next-click behavior.
  • delay show/hide support.
  • custom class support.
  • support title/body absence rules consistently with Bootstrap behavior.
  • disabled-trigger wrapper guidance or component support.
  • accessible role/aria behavior and keyboard/focus behavior.

Shared overlay positioning work

Implement a small dbcss positioning layer instead of relying on Bootstrap JS or Popper at runtime:

  • measure trigger and overlay rects when shown.
  • choose the best placement from requested placement plus fallback placements.
  • position overlays with viewport-aware coordinates.
  • update on scroll, resize, and relevant layout changes while open.
  • keep Bootstrap class names and arrow orientation aligned with the effective placement.
  • clean up event listeners when overlay closes or component unmounts.

No bootstrap.bundle.js dependency should be introduced.

Scrollspy parity

Current state:

  • Scrollspy exposes target, active, and deprecated-style offset.
  • Implementation uses document::eval, document-wide [id] scanning, a global window.__dioxus_scrollspy_active, and scroll listeners.
  • The implementation comment mentions IntersectionObserver, but the code is not using IntersectionObserver.
  • Cleanup/scoping is weak for multiple instances.

Bootstrap behavior to cover with typed Dioxus props and dbcss-owned implementation:

  • target scoping to nav/list-group/simple anchors and their referenced sections.
  • support body and custom scroll containers.
  • use IntersectionObserver-style behavior with typed root_margin and threshold props, matching Bootstrap 5.3 direction.
  • keep backward compatibility for offset if practical, mapping it to root margin semantics.
  • ignore non-visible target elements.
  • deterministic active-section selection when multiple sections are visible.
  • refresh behavior for dynamically added/removed sections.
  • cleanup for observers/listeners on unmount.
  • no shared global state between multiple Scrollspy instances.
  • optional smooth-scroll behavior if exposed as a typed prop.
  • expose active-section updates through Dioxus state and document the mapping to Bootstrap's .active nav/list-group behavior.

Tests / definition of done

  • Unit-test the positioning decision logic where possible.
  • E2E: tooltip appears on hover/focus and stays inside viewport near each edge.
  • E2E: popover opens/closes, outside-click dismissal works, and overlay stays inside viewport near each edge.
  • E2E: overlay effective placement class/arrow tracks fallback placement.
  • E2E: scrollspy updates active section when scrolling body and a custom scroll container.
  • E2E: hidden/non-visible sections are ignored.
  • E2E: multiple scrollspy instances do not interfere with each other.
  • Docs update: remove caveat wording from docs/DESIGN.md once parity work is complete, and document typed props in component docs.
  • Migration tooling update if needed: map raw Bootstrap tooltip/popover/scrollspy attributes to typed dbcss props where static and safe; flag dynamic/ambiguous cases.
  • Existing checks stay green: cargo fmt, clippy, check, tests, migration fixtures, raw-Bootstrap gate, and Playwright e2e.

Signed-by: mik-tf mik-tf@noreply.invalid

The dbcss design rule is: > If Bootstrap does it, this crate should expose a typed Dioxus way to express it. If Bootstrap does not do it, this crate should not invent it. This issue tracks the current parity gaps where dbcss has a typed component but does not yet expose/implement enough of the Bootstrap behavior. These should not stay as permanent caveats. References: - Bootstrap tooltip docs: https://getbootstrap.com/docs/5.3/components/tooltips/ - Bootstrap popover docs: https://getbootstrap.com/docs/5.3/components/popovers/ - Bootstrap scrollspy docs: https://getbootstrap.com/docs/5.3/components/scrollspy/ - Migrated from GitHub mirror issue: https://github.com/mik-tf/dioxus-bootstrap-css/issues/46 ## Tooltip parity Current state: - `Tooltip` supports basic text, placement, and hover rendering. - Positioning is static CSS relative to the trigger wrapper. - There is no viewport collision detection, fallback placement, boundary handling, container/body placement, offset, trigger mode selection, delay, focus handling, or manual control API. Bootstrap behavior to cover with typed Dioxus props and dbcss-owned implementation: - placement: top, bottom, start/end, and auto/fallback behavior equivalent to Bootstrap's Popper-backed behavior. - fallback placements and boundary handling so tooltips stay visible near viewport and scroll-container edges. - offset support. - container/body placement support to avoid clipping and complex layout issues. - trigger modes: hover, focus, click, manual, and combined hover+focus default behavior where appropriate. - delay show/hide support. - custom class support without losing Bootstrap classes. - disabled-trigger wrapper guidance or component support. - accessible behavior: focus-triggered visibility and predictable role/aria output. ## Popover parity Current state: - `Popover` supports title, body, placement, click toggle, and outside-click close. - Positioning is static CSS relative to the trigger wrapper. - It does not implement Bootstrap/Popper-style fallback placement, boundary/container handling, offset, trigger modes, delay, or manual control. Bootstrap behavior to cover with typed Dioxus props and dbcss-owned implementation: - same positioning engine as tooltip: viewport-aware placement, fallback placements, boundary, offset, and optional container/body placement. - trigger modes: click, focus, hover, manual, and dismiss-on-next-click behavior. - delay show/hide support. - custom class support. - support title/body absence rules consistently with Bootstrap behavior. - disabled-trigger wrapper guidance or component support. - accessible role/aria behavior and keyboard/focus behavior. ## Shared overlay positioning work Implement a small dbcss positioning layer instead of relying on Bootstrap JS or Popper at runtime: - measure trigger and overlay rects when shown. - choose the best placement from requested placement plus fallback placements. - position overlays with viewport-aware coordinates. - update on scroll, resize, and relevant layout changes while open. - keep Bootstrap class names and arrow orientation aligned with the effective placement. - clean up event listeners when overlay closes or component unmounts. No `bootstrap.bundle.js` dependency should be introduced. ## Scrollspy parity Current state: - `Scrollspy` exposes `target`, `active`, and deprecated-style `offset`. - Implementation uses `document::eval`, document-wide `[id]` scanning, a global `window.__dioxus_scrollspy_active`, and scroll listeners. - The implementation comment mentions IntersectionObserver, but the code is not using IntersectionObserver. - Cleanup/scoping is weak for multiple instances. Bootstrap behavior to cover with typed Dioxus props and dbcss-owned implementation: - target scoping to nav/list-group/simple anchors and their referenced sections. - support body and custom scroll containers. - use IntersectionObserver-style behavior with typed `root_margin` and `threshold` props, matching Bootstrap 5.3 direction. - keep backward compatibility for `offset` if practical, mapping it to root margin semantics. - ignore non-visible target elements. - deterministic active-section selection when multiple sections are visible. - refresh behavior for dynamically added/removed sections. - cleanup for observers/listeners on unmount. - no shared global state between multiple `Scrollspy` instances. - optional smooth-scroll behavior if exposed as a typed prop. - expose active-section updates through Dioxus state and document the mapping to Bootstrap's `.active` nav/list-group behavior. ## Tests / definition of done - Unit-test the positioning decision logic where possible. - E2E: tooltip appears on hover/focus and stays inside viewport near each edge. - E2E: popover opens/closes, outside-click dismissal works, and overlay stays inside viewport near each edge. - E2E: overlay effective placement class/arrow tracks fallback placement. - E2E: scrollspy updates active section when scrolling body and a custom scroll container. - E2E: hidden/non-visible sections are ignored. - E2E: multiple scrollspy instances do not interfere with each other. - Docs update: remove caveat wording from `docs/DESIGN.md` once parity work is complete, and document typed props in component docs. - Migration tooling update if needed: map raw Bootstrap tooltip/popover/scrollspy attributes to typed dbcss props where static and safe; flag dynamic/ambiguous cases. - Existing checks stay green: cargo fmt, clippy, check, tests, migration fixtures, raw-Bootstrap gate, and Playwright e2e. Signed-by: mik-tf <mik-tf@noreply.invalid>
Author
Owner

Implementation split for #1:

  • #7 Parity spec: document Bootstrap tooltip, popover, scrollspy matrix: #7
  • #8 Overlay: add shared viewport-aware positioning core: #8
  • #9 Tooltip: implement Bootstrap trigger and positioning parity: #9
  • #10 Popover: implement Bootstrap trigger, dismiss, and positioning parity: #10
  • #11 Scrollspy: replace global eval with scoped observer parity: #11
  • #12 Migration/docs: finish overlay and scrollspy parity cleanup: #12

Order of work:

  1. Parity spec/matrix.
  2. Shared overlay positioning core.
  3. Tooltip parity on top of the shared core.
  4. Popover parity on top of the shared core.
  5. Scrollspy scoped observer parity.
  6. Migration/docs cleanup, then close this tracker.

Signed-by: mik-tf mik-tf@noreply.invalid

Implementation split for #1: - [ ] #7 Parity spec: document Bootstrap tooltip, popover, scrollspy matrix: https://forge.ourworld.tf/lhumina_code/dioxus-bootstrap-css/issues/7 - [ ] #8 Overlay: add shared viewport-aware positioning core: https://forge.ourworld.tf/lhumina_code/dioxus-bootstrap-css/issues/8 - [ ] #9 Tooltip: implement Bootstrap trigger and positioning parity: https://forge.ourworld.tf/lhumina_code/dioxus-bootstrap-css/issues/9 - [ ] #10 Popover: implement Bootstrap trigger, dismiss, and positioning parity: https://forge.ourworld.tf/lhumina_code/dioxus-bootstrap-css/issues/10 - [ ] #11 Scrollspy: replace global eval with scoped observer parity: https://forge.ourworld.tf/lhumina_code/dioxus-bootstrap-css/issues/11 - [ ] #12 Migration/docs: finish overlay and scrollspy parity cleanup: https://forge.ourworld.tf/lhumina_code/dioxus-bootstrap-css/issues/12 Order of work: 1. Parity spec/matrix. 2. Shared overlay positioning core. 3. Tooltip parity on top of the shared core. 4. Popover parity on top of the shared core. 5. Scrollspy scoped observer parity. 6. Migration/docs cleanup, then close this tracker. Signed-by: mik-tf <mik-tf@noreply.invalid>
Author
Owner

Tracker update:

Completed:

  • #7 parity matrix/spec documentation
  • #8 shared overlay positioning core

Next logical implementation step:

  • #9 Tooltip trigger and positioning parity, built on the shared overlay core

Remaining after #9:

  • #10 Popover parity
  • #11 Scrollspy scoped observer parity
  • #12 migration/docs cleanup

Signed-by: mik-tf mik-tf@noreply.invalid

Tracker update: Completed: - #7 parity matrix/spec documentation - #8 shared overlay positioning core Next logical implementation step: - #9 Tooltip trigger and positioning parity, built on the shared overlay core Remaining after #9: - #10 Popover parity - #11 Scrollspy scoped observer parity - #12 migration/docs cleanup Signed-by: mik-tf <mik-tf@noreply.invalid>
Author
Owner

Tracker update for Tooltip parity (#9): implementation is pushed in b05c34577f.

Done:

  • Typed Tooltip triggers, delay, controlled open state, Auto/fallback placement, offset, boundary padding, and disabled-trigger wrapper.
  • Viewport-aware positioning through the shared overlay core.
  • Showcase and E2E coverage for hover, focus, click, role/aria output, and viewport-edge fallback.

Status:

  • Local verification is green, including the full E2E suite.
  • Forge CI MSRV is green.
  • Forge CI Check, lint, format is still pending/running, so #9 remains open until that resolves.

Next logical issue after #9 closes: #10 Popover parity.

Signed-by: mik-tf mik-tf@noreply.invalid

Tracker update for Tooltip parity (#9): implementation is pushed in b05c34577f2dfc01160bc91d755b9d80a424a0fa. Done: - Typed Tooltip triggers, delay, controlled open state, Auto/fallback placement, offset, boundary padding, and disabled-trigger wrapper. - Viewport-aware positioning through the shared overlay core. - Showcase and E2E coverage for hover, focus, click, role/aria output, and viewport-edge fallback. Status: - Local verification is green, including the full E2E suite. - Forge CI MSRV is green. - Forge CI Check, lint, format is still pending/running, so #9 remains open until that resolves. Next logical issue after #9 closes: #10 Popover parity. Signed-by: mik-tf <mik-tf@noreply.invalid>
Author
Owner

Tracker update for Popover parity (#10): implementation is pushed in 95373b6dea.

Done:

  • Typed Popover placement, triggers, delay, controlled open, fallback placements, offset, boundary padding, outside dismiss, and disabled-trigger wrapper.
  • Viewport-aware positioning through the shared overlay core.
  • Empty title/body suppression, Bootstrap role/classes, stable aria-describedby, showcase coverage, and E2E coverage for click, focus dismiss, outside dismiss, viewport fallback, and empty content.

Status:

  • Local verification is green, including the full E2E suite.
  • Forge CI MSRV is green.
  • Forge CI Check, lint, format is still pending/running, so #10 remains open until that resolves.

Next logical issue after #10 closes: #11 Scrollspy parity.

Signed-by: mik-tf mik-tf@noreply.invalid

Tracker update for Popover parity (#10): implementation is pushed in 95373b6deadf6fee31d675c882fcab8c83f4b735. Done: - Typed Popover placement, triggers, delay, controlled open, fallback placements, offset, boundary padding, outside dismiss, and disabled-trigger wrapper. - Viewport-aware positioning through the shared overlay core. - Empty title/body suppression, Bootstrap role/classes, stable aria-describedby, showcase coverage, and E2E coverage for click, focus dismiss, outside dismiss, viewport fallback, and empty content. Status: - Local verification is green, including the full E2E suite. - Forge CI MSRV is green. - Forge CI Check, lint, format is still pending/running, so #10 remains open until that resolves. Next logical issue after #10 closes: #11 Scrollspy parity. Signed-by: mik-tf <mik-tf@noreply.invalid>
Author
Owner

Popover parity (#10) is complete and Forge CI is green for 95373b6dea.

Closed scope:

  • Typed Popover triggers, delay, controlled open, Auto/fallback placement, offset, boundary padding, outside dismiss, and disabled-trigger wrapper.
  • Viewport-aware positioning through the shared overlay core.
  • E2E coverage for click, focus dismiss, outside dismiss, viewport fallback, aria/role output, and empty content.

Next logical issue: #11 Scrollspy parity.

Signed-by: mik-tf mik-tf@noreply.invalid

Popover parity (#10) is complete and Forge CI is green for 95373b6deadf6fee31d675c882fcab8c83f4b735. Closed scope: - Typed Popover triggers, delay, controlled open, Auto/fallback placement, offset, boundary padding, outside dismiss, and disabled-trigger wrapper. - Viewport-aware positioning through the shared overlay core. - E2E coverage for click, focus dismiss, outside dismiss, viewport fallback, aria/role output, and empty content. Next logical issue: #11 Scrollspy parity. Signed-by: mik-tf <mik-tf@noreply.invalid>
Author
Owner

Scrollspy parity (#11) is complete and Forge CI is green for 451361ec45.

Done:

  • Scoped per-instance Scrollspy observer/listener state; no shared active global.
  • Bootstrap target semantics with root, root_margin, threshold, refresh_key, smooth_scroll, and offset compatibility.
  • Body scroll, custom scroll-root, multi-instance isolation, active link updates, and dynamic refresh coverage.

Next logical issue: #12 Migration/docs cleanup.

Signed-by: mik-tf mik-tf@noreply.invalid

Scrollspy parity (#11) is complete and Forge CI is green for 451361ec455c9e22cec27216066eba2c6c464e60. Done: - Scoped per-instance Scrollspy observer/listener state; no shared active global. - Bootstrap target semantics with root, root_margin, threshold, refresh_key, smooth_scroll, and offset compatibility. - Body scroll, custom scroll-root, multi-instance isolation, active link updates, and dynamic refresh coverage. Next logical issue: #12 Migration/docs cleanup. Signed-by: mik-tf <mik-tf@noreply.invalid>
Author
Owner

Migration/docs cleanup (#12) is complete and Forge CI is green for 77e629efdf.

Done:

  • Current parity docs now describe implemented Tooltip, Popover, and Scrollspy behavior instead of pre-implementation targets.
  • Migration docs and tools docs now state the converter rule, safe static overlay mappings, and Scrollspy manual-review boundary.
  • Converter fixtures cover static Tooltip/Popover conversion and Scrollspy manual review.
  • Raw-Bootstrap gate now covers Tooltip/Popover raw overlay classes and links to docs/MIGRATION.md.

Remaining tracker check before #1 closure: #9 is still open and should be audited/closed only if Tooltip acceptance is fully satisfied.

Signed-by: mik-tf mik-tf@noreply.invalid

Migration/docs cleanup (#12) is complete and Forge CI is green for 77e629efdf9dfec60a986171e32a3ccb3ad80b91. Done: - Current parity docs now describe implemented Tooltip, Popover, and Scrollspy behavior instead of pre-implementation targets. - Migration docs and tools docs now state the converter rule, safe static overlay mappings, and Scrollspy manual-review boundary. - Converter fixtures cover static Tooltip/Popover conversion and Scrollspy manual review. - Raw-Bootstrap gate now covers Tooltip/Popover raw overlay classes and links to docs/MIGRATION.md. Remaining tracker check before #1 closure: #9 is still open and should be audited/closed only if Tooltip acceptance is fully satisfied. Signed-by: mik-tf <mik-tf@noreply.invalid>
Author
Owner

Parity tracker complete.

Closed issues:

  • #8 Overlay shared viewport-aware positioning core
  • #9 Tooltip trigger and positioning parity
  • #10 Popover trigger, dismiss, and positioning parity
  • #11 Scrollspy scoped observer parity
  • #12 Migration/docs cleanup

Final verification state:

  • Tooltip, Popover, and Scrollspy behavior is documented in docs/PARITY.md and docs/MIGRATION.md.
  • Converter fixtures cover static Tooltip/Popover mappings and Scrollspy manual review.
  • Raw-Bootstrap gate is aligned with the current typed surface.
  • Local checks passed on the final cleanup commit, including cargo fmt/check/clippy/test, migration tests, raw-Bootstrap gate, and targeted tooltip/popover/scrollspy E2E.
  • Forge CI is green for final head 77e629efdf.

Any new parity gap found after this should be opened as a focused follow-up issue rather than keeping this tracker open.

Signed-by: mik-tf mik-tf@noreply.invalid

Parity tracker complete. Closed issues: - #8 Overlay shared viewport-aware positioning core - #9 Tooltip trigger and positioning parity - #10 Popover trigger, dismiss, and positioning parity - #11 Scrollspy scoped observer parity - #12 Migration/docs cleanup Final verification state: - Tooltip, Popover, and Scrollspy behavior is documented in docs/PARITY.md and docs/MIGRATION.md. - Converter fixtures cover static Tooltip/Popover mappings and Scrollspy manual review. - Raw-Bootstrap gate is aligned with the current typed surface. - Local checks passed on the final cleanup commit, including cargo fmt/check/clippy/test, migration tests, raw-Bootstrap gate, and targeted tooltip/popover/scrollspy E2E. - Forge CI is green for final head 77e629efdf9dfec60a986171e32a3ccb3ad80b91. Any new parity gap found after this should be opened as a focused follow-up issue rather than keeping this tracker open. Signed-by: mik-tf <mik-tf@noreply.invalid>
mik-tf referenced this issue from a commit 2026-06-23 23:01:16 +00:00
Sign in to join this conversation.
No labels
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/dioxus-bootstrap-css#1
No description provided.