Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[css-values-4] Add new relative length units that are relative to the size of the system cursor #8315

Open
yjugl opened this issue Jan 16, 2023 · 11 comments
Labels

Comments

@yjugl
Copy link

yjugl commented Jan 16, 2023

[Related bug in Firefox]

There are currently two ways for web developers to get tooltip behavior for a website, as listed in this example StackOverflow thread:

  • use a title attribute, which yields a standardized text tooltip managed by the browser;

  • create a CSS-customizable tooltip, listen to mouse move events with JavaScript to dynamically change the position of the tooltip, reveal it when some other element is hovered.

When computing the new position for a customizable tooltip with JavaScript, web developers will typically add an offset to account for the system cursor. However, because the system cursor size is not exposed, the offset they add is arbitrary and cannot account for varying cursor sizes. This causes problems for accessibility, as visually impaired users may set their cursors to a bigger size than the average and have their cursor cover the contents of the tooltip -- thus part of the information is not shown for them.

This can be seen in the StackOverflow thread listed above, where two different web developers answering the same question answered with a different arbitrary offset:

    // Offset of 20px
    tooltipSpan.style.top = (y + 20) + 'px';
    tooltipSpan.style.left = (x + 20) + 'px';
  // Offset of 10px
  tooltip.style.left =
      (e.pageX + tooltip.clientWidth + 10 < document.body.clientWidth)
          ? (e.pageX + 10 + "px")
          : (document.body.clientWidth + 5 - tooltip.clientWidth + "px");
  tooltip.style.top =
      (e.pageY + tooltip.clientHeight + 10 < document.body.clientHeight)
          ? (e.pageY + 10 + "px")
          : (document.body.clientHeight + 5 - tooltip.clientHeight + "px");

This issue proposes to add two new relative length units.

unit relative to
'scw' width of the system cursor
'sch' height of the system cursor

Browsers may let the option to users to use up-rounded or predefined system cursor sizes to limit fingerprinting.

The first example from above may then be changed to the following:

    tooltipSpan.style.top = 'calc(1sch + ' + y + 'px)';
    tooltipSpan.style.left = 'calc(1scw + ' + x + 'px)';

The second example could be partially adapted ...

  tooltip.style.left =
      (e.pageX + tooltip.clientWidth + 10 /* still arbitrary */ < document.body.clientWidth)
          ? ("calc(1scw + " + e.pageX + "px)")
          : ("calc(0.5scw + " + (document.body.clientWidth - tooltip.clientWidth) + "px)");
  tooltip.style.top =
      (e.pageY + tooltip.clientHeight + 10 /* still arbitrary */ < document.body.clientHeight)
          ? ("calc(1sch + " + e.pageY + "px)")
          : ("calc(0.5sch + " (document.body.clientHeight - tooltip.clientHeight) + "px)");

... but, as you can see, the proposed change would not be enough on its own to completely adapt the second example. This code is trying to assess from JavaScript whether the tooltip would be partially positioned outside the limits of the rendered page and not get rendered completely -- and adjust its position accordingly. For this example to not use any arbitrary offset, I believe we would have to expose the system cursor size directly to JavaScript -- so I'd like to hear your opinions regarding this possibility as well.

Thanks!

@Loirooriol
Copy link
Contributor

Note that the cursor image may have some transparent areas, so visually the cursor can look smaller.
Also, note that the cursor image doesn't have to be anchored at the top left corner, e.g.

cursor: url(http://www.javascriptkit.com/dhtmltutors/cursor-hand.gif) 10 4, auto

So even if the cursor image is 25px wide, it will only extend 15px to the right, so that's the amount you probably want.

@SebastianZ
Copy link
Contributor

Maybe instead of new units this could be solved via CSS Anchor Positioning. A new cursor keyword could be introduced that dynamically resolves to the cursor's width or height depending on the <anchor-side>.

So positioning the tooltip could then look like this:

.tooltip {
  position: absolute;
  left: calc(anchor(cursor right) + 10px);
  top: calc(anchor(cursor bottom) + 10px);
}

Not sure whether the cursor's hotspot really needs to be taken into account here but that could surely be expressed via keywords as well.

The keyword probably would have to compute to itself in order to avoid fingerprinting.

The big benefits of this approach are that it's a pure CSS solution and it's very concise in comparison to the JS solution. Also, Anchor Positioning already provides fallbacks for the case when the element would overflow its containing block.

Sebastian

@kbrilla
Copy link

kbrilla commented Jan 16, 2023

I though about It but the problem is that this is dynamic value and could potentialy be used in few places at once and have diffrent values as You would want It to be stuck at first computed value or something like this as when You open context menu You want this menu to be anchored to this exact point and not move when moving cursor

@Loirooriol
Copy link
Contributor

The keyword probably would have to compute to itself in order to avoid fingerprinting.

But if it affects the position, a script can always use getClientRects() to measure it. Privacy concerns still apply.

@emilio
Copy link
Collaborator

emilio commented Jan 18, 2023

Tangentially related: #4691, which would expose the scrollbar size.

@SebastianZ
Copy link
Contributor

when You open context menu You want this menu to be anchored to this exact point and not move when moving cursor

Though context menus are a totally different use case. And they normally also don't require to take the cursor size into account because they open at the pixel the user clicked.

The keyword probably would have to compute to itself in order to avoid fingerprinting.

But if it affects the position, a script can always use getClientRects() to measure it. Privacy concerns still apply.

Right. It would still be possible to calculate the size of the cursor and with that infer the OS. I guess there's no way around that then unless getClientRects(), getBoundingClientRect() and possibly other functions somehow don't expose the info in those cases.

Sebastian

@yjugl
Copy link
Author

yjugl commented Jan 30, 2023

Thanks for your sharing your ideas everyone!

Note that the cursor image may have some transparent areas, so visually the cursor can look smaller. Also, note that the cursor image doesn't have to be anchored at the top left corner, e.g.

cursor: url(http://www.javascriptkit.com/dhtmltutors/cursor-hand.gif) 10 4, auto

So even if the cursor image is 25px wide, it will only extend 15px to the right, so that's the amount you probably want.

The goal of this proposal is to expose the system cursor size, which web developers cannot know at all for the moment. If the CSS is altering the cursor (which is not recommended for accessibility), then the developer should know the characteristics of the cursor they are adding, which we cannot guess, so I think that is a case where using hardcoded values instead of the extra capabilities added by this proposal would be legitimate? Am I missing something here?

Maybe instead of new units this could be solved via CSS Anchor Positioning. A new cursor keyword could be introduced that dynamically resolves to the cursor's width or height depending on the <anchor-side>.

So positioning the tooltip could then look like this:

.tooltip {
  position: absolute;
  left: calc(anchor(cursor right) + 10px);
  top: calc(anchor(cursor bottom) + 10px);
}

This is an interesting suggestion, in particular I think it reads well. This adds more new behaviors than the new units though: anchor(cursor width), anchor(cursor height) would be the equivalent of 1scw, 1sch, and all other behaviors are an extra, e.g. we could now have things that automatically follow the dynamic position of the cursor in pure CSS.

To me this looks more complex to implement than the first proposal as it implies having a pseudo-anchor element for the cursor that moves with every mouse move -- something that users currently simulate with JavaScript mouse move events. But the implementation complexity may be worth it, if the extra behaviors are desirable?

@Loirooriol
Copy link
Contributor

the developer should know the characteristics of the cursor they are adding

Even if authors know the size of the image they are providing, the final cursor may be scaled:

If an operating system is incapable of rendering a cursor above a given size, cursors larger than that size must be shrunk to within the OS-supported size bounds

Also, different elements can have different cursors, and even if they have the same size, they may have different <x> <y>. So knowing which values to use in the tooltip would require knowing the style of the hovered element.

@yjugl
Copy link
Author

yjugl commented Jan 30, 2023

As a matter of fact, trying to implement this change for Firefox's UI made me realize how important this comment was:

Note that the cursor image may have some transparent areas, so visually the cursor can look smaller. Also, note that the cursor image doesn't have to be anchored at the top left corner, e.g.

cursor: url(http://www.javascriptkit.com/dhtmltutors/cursor-hand.gif) 10 4, auto

So even if the cursor image is 25px wide, it will only extend 15px to the right, so that's the amount you probably want.

This is true even for "official" Windows cursors, e.g. the usual arrow pointer is a 32x32 bitmap with 19 transparent columns on the right side, 13 transparent rows at the bottom. So indeed I now understand why exposing bitmap width and height (e.g. 32x32) is not enough, instead we would ideally need to expose 4 coordinates that define a "non-transparent box" for the cursor within the bitmap. Sebastian's proposal is quite appealing in that regard although I'm still unsure about the dynamic part.

@Crissov
Copy link
Contributor

Crissov commented Jan 30, 2023

What I dislike about this proposal is that it doesn't solve the original use case presented unless Javascript is also involved. To actually solve the use case, you would first need something like position: pointer.

I'd also like to point out that there were proposals for a similar unit, tip, that would give the approximate size of the pointing device regardless whether it was virtual or physical like a finger or stylus.

@SebastianZ
Copy link
Contributor

SebastianZ commented Jan 30, 2023

What I dislike about this proposal is that it doesn't solve the original use case presented unless Javascript is also involved. To actually solve the use case, you would first need something like position: pointer.

The idea was that anchor(cursor <anchor-side>) would dynamically resolve the cursor's position plus offset defined by <anchor-side>. This is also how anchor() works for anchor elements where it needs to account for transitions of the anchor elements.
But you're probably right that we might additionally need a pointer keyword for position to avoid offset issues resulting from any containing block.

Sebastian

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

6 participants