kolibri-design-system icon indicating copy to clipboard operation
kolibri-design-system copied to clipboard

[PoC DO NOT MERGE] Floating

Open MisRob opened this issue 3 months ago • 7 comments

Description

Proposes solution for the next version of KTooltip and also has broader exploration of how we could approach floating elements in the future.

Not for merging. After this proof-of-concept stage, selected parts will be finalized through new PRs.

Reviewer guidance

  • (1) See the preview for context and demos.
  • (2) See the implementation notes below.
  • (3) Code review
    • High-level feedback for changes planned in shorter term: useKFloatingInteraction, useKFloatingPosition, KTooltip/next, KIconButton, and v-focusable (majority is in this commit)
    • The rest is long-term or experimental - just use to assess whether the approach suggested here will scale well as future solution for all floating elements

As for a11y, @radinamatic would you please provide feedback whether some techniques I'm playing with in the live examples on this page could be an interesting direction for us and perhaps one step towards better experience? The APG tooltip pattern is so far just very basic and work in progress.

References

Related https://github.com/learningequality/kolibri-design-system/issues/745

Library choice and browser support

Oct 20 update: Note that based on the limitations mentioned below we decided to improve our browserlist and ability to test against it easily - which will change the way we think about the final choice. Feel free to skip this section.

Given our needs for customization, accessibility, performance, and maintainability, I think it'd be suitable to use a third-party library only for low-level position calculations. Candidates include Floating UI or its predecessor Popper v2 (or perhaps even v1).

Floating UI is a modern, actively maintained, and widely used library. However, its browser support is far away from our browserlist.

I previewed one instance of the next KTooltip using useKFloatingPosition built with both Floating UI and Popper 2 on Browserstack and didn't notice any differences between them. In the browsers below, they worked as expected, except one hiccup:

Platform / Browser Floating UI (blue tooltip) Popper 2 (gray tooltip)
Win 7 Chrome 49 tooltip text cut off * tooltip text cut off *
Win 7 Edge 80 ok ok
Win 7 Firefox 52 ok ok
Win 7 Opera 67 ok ok
Mac High Sierra Safari 11.1 tooltip text cut off * tooltip text cut off *
Samsung Galaxy S9 Samsung 25 ok ok
Motorola Moto G7 Play UC Browser 12 ok ok

*

However:

  • I couldn't fully test our browserlist on BrowserStack; some older devices and browser versions aren't available.
  • A simple tooltip test only covers a small subset of library features. This doesn't guarantee everything will work in the final implementation of the many floating features in our apps, not only informative ones, but also essential interactive components, such as dropdowns.
  • Developer experience is similar for both; each approach has its pros and cons (see Floating version in this commit).
  • We'd need to pin the Floating UI version or thoroughly test each upgrade against our browserlist.
  • The authors recommended me Popper
  • It's unclear if we've ever tested Popper 1 against our browserlist.

Based on this, one approach could be:

  • Start with Popper 2.
  • If strict browserlist support is needed, ask QA team for support with full coverage of final implementations in Kolibri.
  • Depending on results, stay with Popper 2, downgrade to its earlier version, or switch to Popper 1 (which is currently used in via tippy.js in some of our implementations)

Once our browserlist allows, upgrading to Floating UI should be straightforward. In the meantime, we can prepare ground by removing other third-party dependencies in favor of useKFloating....

Performance

I looked into performance metrics such as repaints/reflows, JS heap size, number of DOM nodes, number of event listeners, and scripting time.

Number of DOM nodes

In the past, enabling lazy on VTooltips in Studio has reduced performance issues from excessive DOM nodes. Therefore, the lazy option is supported in the next KTooltip.

Number of event listeners

For floating elements that can appear in large numbers, it's important to consider where event listeners controlling their visibility are attached. A common approach is to add listeners to each trigger element. For example, every icon showing a tooltip listening for mouseover and possible other events. On pages with many floating elements, this can lead to hundreds or thousands of listeners. Event delegation is a technique to reduce the number by attaching a single listener to a container or the window/document, handling events for its children. However, this means the handler runs for non-trigger elements too, which can increase scripting time, especially for frequent events like mouseenter.

Even though mouseenter is optimized and the handler is low-cost (exits unless the event target has the data-floating-id attribute), on pages with only a few trigger elements (= smileys), event delegation still feels like unnecessary overhead:

On the other hand, on pages with many trigger elements, event delegation often pays off, reducing the number of listeners by hundreds or even thousands:

For this reason, useKFloatingInteraction and the next KTooltip (or any other floating components) don't enforce a specific approach, but only provide an optional delegateTo parameter. KDS documentation will offer guidance on how to choose the best approach.

The general rule of thumb is to avoid delegation for a small number of tooltips on a page. For larger numbers of tooltips, it's best to profile performance with and without delegation to choose the optimal approach.

Measurements

In addition, useKFloatingInteraction optimizes other areas, such as using an elements cache and attaching leave event listeners only when needed. Here’s a test of the next KTooltip on a page with 1000 tooltips:

VTooltip, lazy next KTooltip, lazy, events delegated to document/window
Page reload 01 02
Hover 50 tooltips 03 04

These results were consistent across repeated measurements.

I tried testing on the aforementioned African Storybook page too, but due to many factors and thousands of listeners, it's nearly impossible to get clear numbers—e.g. repeated measurements of the current version show fluctuations in listener counts in the thousands for reasons unrelated to tooltips. One consistent finding was that, despite event delegation, scripting time was even better than before, suggesting that using mouseover on the whole window should be just fine on this page.

Finally, after examining large channel pages, I believe that applying similar strategies to any elements appearing in large numbers, not just floating ones, will lead to many more improvements.

MisRob avatar Sep 03 '25 15:09 MisRob

delegateTo parameter

I love the deliberateness and thought of this approach - allowing us to tune the performance in the edge cases of large numbers of elements feels so helpful.

rtibbles avatar Sep 03 '25 17:09 rtibbles

@radinamatic

For the first few examples with icons or icon + text. General recommendation is not to make non-interactive elements focusable (even though activating a tooltip may perhaps be considered as interaction?) Also Heydon says that, with recommendation not to use tooltips at all. And in his guide for tooltips, he really doesn't work with non-focusable elements.

On the other hand, APG pattern clearly requests that

Focus stays on the triggering element while the tooltip is displayed.

and it's the only way to make them accessible for keyboard users.

Also, there are some resources that actually suggest making trigger elements focusable, even though they are not interactive in a typical sense

  • https://theadminbar.com/accessibility-weekly/accessible-tooltips/
  • https://www.accede-web.com/en/guidelines/rich-interface-components/customized-tooltips/

I don't know how reputable those are. But it suggest people use it in some ways.

It seems that the main Heydon's issue with it is

Even if you consider the showing of the tooltip an interactive 'action', it happening on focus makes little sense, especially to unsighted screen reader users who won't know anything has happened.

which I've tried to address by labeling via describedby or labelledby, depending on context. That seems what is suggested in the APG pattern as well.

This article https://www.a11y-collective.com/blog/tooltips-in-web-accessibility/ is quite interesting as it tries to balance many perspectives.

With our current patterns, we will inevitably have to break one of the rules. So take it as some ideas that may perhaps help or not - it's probably best to just pragmatically try out and then evaluate whether it can add some value to the current experience, or not.

MisRob avatar Sep 03 '25 19:09 MisRob

Thank you @rtibbles. That's result of my despair after looking into our many use-cases :grin:

MisRob avatar Sep 03 '25 19:09 MisRob

Hey @MisRob!

The amount of thought, information and effort that went into investigating possible implementation for this this new approach was simply astounding! 😍

Can't say I was surprised, being familiar with your thoroughness, but I was still 🙇🏽‍♀️

I may have some further thoughts on the sources you mentioned above, but given that we are navigating an overcharted territory where much more experienced a11y colleagues at APG have not managed to arrive to consensus after 9 years, and even without understanding all that happens under the hood, I very much appreciate the approach you've taken to accommodate various needs.

Let's start with the test results of the NVDA output in latest Chrome and Firefox on Windows 10.

KTooltip

? icon

Upon focusing, NVDA announces the Tooltip text correctly in Chrome, but in Firefox it only reads blank

Captions and subtitles

On both Chrome and Firefox NVDA announces: Captions and subtitles heading Supported formats: '.vtt' level 4

For the best (expected) experience it should be Captions and subtitles Supported formats: '.vtt' heading level 4

Lazy KTooltip

Same as for KTooltip on both Chrome and Firefox.

KTooltip with delegated events

No live examples.

KLabeledIcon with KTooltip

On both Chrome and Firefox NVDA only announces 9, which seems to indicate that the content of the tooltip is not read at all.

KIconButton with KTooltip

On both Chrome and Firefox NVDA correctly announces Add bookmark/Remove bookmark button 🎉

KTextbox with KTooltip

On both Chrome and Firefox NVDA announces: Aggregator edit Website or org hosting the content collection but not necessarily the creator or copyright holder. blank

I believe a better experience would be: Aggregator Website or org hosting the content collection but not necessarily the creator or copyright holder. edit blank or Aggregator edit blank Website or org hosting the content collection but not necessarily the creator or copyright holder.

Note the different tooltip position between Chrome and Firefox (better imho)

Chrome Firefox
chrome firefox

Context menu

Clickable area is not focusable by keyboard, so not sure what is envisioned here to be the equivalent interaction workflow for the keyboard user... 🤔 Even with the mouse, left click does nothing.

radinamatic avatar Oct 13 '25 07:10 radinamatic

Thanks a lot for your time on this @radinamatic.

High-level, that's good news - sounds we may benefit from some of those explorations. One of the first next steps will be a new version of KTooltip, and as I will be preparing documentation, I will include a11y guidance based on what you saw here for relevant parts then. And then, as we're migrating tooltips, we can follow it, which should resolve some a11y issues with the current version. We can consider it as first steps, and if you get any new ideas in the future, we can improve further.

I also appreciate detailed testing with screenreaders that are definitely more common than my Orca. As part of the new tooltip work, I will return to this part and see what we can do.

Apologies for the context menu - that wasn't meant for a11y testing, but rather for preview for developers on how code would look like. I need to remember to be more explicit for you.

MisRob avatar Oct 15 '25 12:10 MisRob

Thanks both for your time and feedback @LianaHarris360 @rtibbles.

maybe we could test adding one other floating component (like a dropdown) using them just to be sure the APIs won’t need to change much down the road?

Yes, I can add a quick experimental draft :)

MisRob avatar Oct 17 '25 16:10 MisRob

Thanks for taking time to preview it @nucleogenesis - it's good to hear that on some basic level it will be hopefully straightforward to use. Yes, I think after some time, we may come up with some tweaks ;)

MisRob avatar Oct 27 '25 05:10 MisRob