[whatwg] Outline style to use for drawSystemFocusRing

Ian Hickson ian at hixie.ch
Tue Jan 7 13:10:28 PST 2014

On Wed, 16 Oct 2013, Dominic Mazzoni wrote:
> On Wed, Oct 16, 2013 at 12:46 PM, Ian Hickson <ian at hixie.ch> wrote:
> > WCAG 2.0 claims that "many platforms allow the user to customize the 
> > rendering of this focus indicator", though I admit that I don't see 
> > any references for this claim:
> >
> >    http://www.w3.org/TR/WCAG-TECHS/G165.html
> >
> > IBM similarly claims "users may customize the default indicator in 
> > Windows to a brighter color":
> >
> >   http://www-03.ibm.com/able/guidelines/web/webfocusvisible_aa.html#tech3
> >
> > I haven't been able to support those claims. However, Win32 in 
> > particular has some APIs for changing focus rings (see below for 
> > references).
> If the user changes the focus ring's color and/or width, then 
> drawSystemFocusRing should draw using that style.


As far as I can tell, it's not generally the precise colour that a user 
picks. It's the dimension, and the rendering mode (high contrast vs normal).

> The issue is when the application wants to draw its own focus ring - 
> should the system sometimes override that and draw its own focus ring 
> instead? That's the argument I don't buy.

If the user needs a big ring, it seems bad for us not to render one. 
Especially since we can know this.

> Respecting the system focus ring color but ignoring the rest of the 
> system palette makes no sense.

A single solid-colour focus ring does have that problem, yes. This is the 
case even without <canvas>; if you change your page colour scheme and 
don't override the focus outline colours, and the operating system's 
default focus rings are solid colour, then you can have this problem. The 
solution is for the focus rings not to be a solid colour. Mac OS, for 
example, makes them a gradient. Some vesions of Windows draw the focus 
ring using "xor" (I haven't checked modern Windows behaviour). Mouse 
cursors are commonly drawn using two levels of outlines to avoid the 
equivalent problem with mouse cursors.

A high contrast focus ring should be visible regardless of the background.

> I am totally in favor of trying to provide a better experience for users 
> who want a high-contrast theme and custom focus rings - I just don't 
> think this API is the way to achieve that goal, and I think it would 
> actually make things worse if user agents implemented it as specified.

I don't understand why it would make things worse, but I'm certainly open 
to better suggestions. What's a better way to get authors who don't care 
about accessibility to honour high-contrast focus ring preferences?

> Perhaps this shouldn't even be solved as part of canvas. Maybe we should 
> add web apis to indicate that the user prefers a custom color scheme 
> that could be used for rendering the whole page, not just canvas.

Most authors will just ignore this. That's why we have user style sheets 
that can override author sheets, for example.

> > The name isn't ideal, it's true. I don't know what a better name 
> > should be, though. It's really "let me know if I should draw a focus 
> > ring, and if I should, then take the opportunity to also notify the 
> > accessibility tools", which doesn't make for a very pithy method name.
> What would you call it if it never draws, but just notifies the UI?

At that point, it would just be the addHitRegion() API, with a check for 
whether the control is focused. Not much point having that as a single 
rolled-up API, IMHO.

> > It's canvas-specific because in the non-canvas state, the browser 
> > already does this, in theory. The focus in the non-canvas case is 
> > drawn by the browser using the :focus rules, which, in principle, are 
> > set to the user's preferred state.
> In practice this doesn't necessarily work for the same reason I gave 
> above: the system focus ring might look terrible in particular UIs.

The point isn't for it to be aesthetically pleasing, the point is for it 
to be more obviously visible to the user.

> > > If we added a canvas fallback element as a parameter to 
> > > scrollPathIntoView, I don't think we'd need drawCustomFocusRing.
> >
> > I don't understand how this would work.
> >
> > Suppose you have a control that is floating around the screen. You 
> > need the keyboard-focus-driven magnification to follow this control 
> > while it's focused. You don't want to scroll to that control every 
> > time it moves, you only want to scroll to it when it's focused.
> >
> > So what you do is when it's focused, you scrollPathIntoView(), and 
> > then every 16 milliseconds you move the control and redraw its focus 
> > ring, by calling drawCustomFocusRing() (or drawSystemFocusRing() if 
> > you don't care exactly what it looks like), and that updates the AT.
> Wait, so what's wrong with calling scrollPathIntoView on it every 16 
> milliseconds, only when it's focused?

It would prevent the user from scrolling the page, which seems like a 
pretty serious problem.

> > drawCustomFocusRing() and drawSystemFocusRing() shouldn't cause 
> > anything to scroll. That would be very confusing, IMHO. (When would 
> > you scroll? Consider the case of the control originally being 
> > off-screen on purpose, and animating into position. You want the focus 
> > ring drawn the whole time, and the zoom to follow it maybe, but you 
> > only want to scroll once, at the start, to the location that it will 
> > have at the end.)
> OK, that seems like a reasonable argument. Let's keep scrolling 
> separate.


> > It would appear that on Win32, the SystemParametersInfo function has 
> > SPI_GETHIGHCONTRAST options that may be relevant here.
> Agreed that those should change the appearance of the focus ring drawn 
> by drawSystemFocusRing.
> I don't think it should override an app that wants to draw its own focus 
> ring for the reasons given above - if the canvas author doesn't have any 
> way to query the system foreground and background color, or if the user 
> has explicitly chosen a color palette specific to this web app, then 
> using the system focus ring could look much worse.

What matters is whether it's more visible, not whether it happens to match 
the aesthetic sense of the page's author. I don't see how honouring the 
settings above while making sure to have a carefully styled focus ring 
(e.g. a dotted outline consisting of concentric black and white circles) 
would lead to a less visible focus ring.

> > > Windows has a system setting for high-contrast mode. When you turn 
> > > on high contrast mode, it changes the default color palette. There's 
> > > no other effect on the focus ring that I know of.
> >
> > This may be the high contrast theme, which is distinct from high 
> > contrast mode, according to the Remarks section here:
> They are separate settings, but what I've seen most apps do is use 
> system colors when high contrast mode is on, and use their own colors 
> when it's off. That sounds fine - but only picking the focus ring color 
> by itself doesn't make sense.

I'm all for us supporting the other parts of this as well, but that 
doesn't seem to preclude doing it for focus rings.

I don't see how we can get the other parts done automatically, the way we 
can for focus rings.

> > > High contrast mode may affect the system focus ring color, it's true 
> > > - but there's no reason to believe that this system focus ring would 
> > > look better on a canvas when high contrast mode is on, and in fact 
> > > it might look much worse.
> >
> > But presumably if you're in high-contrast mode, you might want to 
> > render a more contrasty focus ring, even if it's not the system one.
> ...

Not sure if you're agreeing or not here.

> > Cursors solve this problem by having a white border around a black 
> > border around a white arrow (or vice versa). I could see a 
> > high-contrast focus ring being done in a similar fashion.
> Yeah, but this is nonstandard, no other apps do this. What other apps 
> do, if they respect high contrast at all, is to use all of the system 
> colors, or none of them.

How can we make canvas apps do this? I'd love to be able to do it. I just 
don't know how. I can see how to do it for focus rings.

> > > If the author wants to draw their own focus ring, it's probably for 
> > > a good reason. We should let them.
> >
> > Well we're never stopping them, are we? I mean, they can always do 
> > what they want...
> Yes, we're preventing them from drawing their own focus ring and 
> notifying assistive technology.

The number of authors who know they want to draw their own focus ring, and 
know that the user can have focus rings preferences, and know they want to 
ignore the user's focus rings preferences, and yet know that the user 
needs their AT to be notified of where the focus ring is, seems 
vanishingly small.

But that group of authors is still handled, actually. They just need to 
use addHitRegion(). That way the AT knows exactly where the control is at 
all times, and can magnify on it whenever the control is focused, without 
needing to be told anything about focus rings.

> drawCustomFocusRing, as specified now, tries to make judgement calls and 
> doesn't give the web developer any insight or flexibility.

It's "opinionated". :-)

> Plus it's confusingly named, so the odds that the typical developer will 
> use it correctly are not good.

As I said before, I agree that the name isn't ideal, but I don't know what 
a better name would be. "Let me know if I should draw a focus ring, and if 
I should, then take the opportunity to also notify the accessibility 
tools" doesn't make for a very pithy method name. I'm open to better 

> What we really want is for the web developer to know if high-contrast 
> mode is on, and a way to notify the browser of the focus ring location. 
> That sounds like two APIs to me, and the first one isn't 
> canvas-specific.

I agree that those are good APIs to have. The second one is 
addHitRegion(). The first seems like something for CSSOM.

The API here is something different, though. It's intended to allow 
authors to draw their own focus outlines, except when the user has 
explicitly requested bigger special outlines, in which case the author 
would like those rendered instead.

On Wed, 16 Oct 2013, Rik Cabanier wrote:
> > On Wed, 2 Oct 2013, Rik Cabanier wrote:
> > >
> > > I don't understand. If the path that is active during 
> > > drawCustom/SystemFocusRing is off screen and the element is focused, 
> > > the browser will scroll to that area.
> >
> > drawCustomFocusRing() and drawSystemFocusRing() shouldn't cause 
> > anything to scroll. That would be very confusing, IMHO. (When would 
> > you scroll?
> You would scroll when the hidden element gets focus.

That would lead to very poor UI, e.g. in the case where the custom canvas 
control animates its position when focused. You'd end up scrolling to the 
part of the canvas that no longer has the control.

> > Consider the case of the control originally being off-screen on 
> > purpose, and animating into position. You want the focus ring drawn 
> > the whole time, and the zoom to follow it maybe, but you only want to 
> > scroll once, at the start, to the location that it will have at the 
> > end.)
> Yes, there should only be 1 scroll. Not to the location of the hidden 
> element (which only exists in the dom) but to the location of the focus 
> ring.

The focus ring, in the example described above, moves, and starts in the 
"wrong" position. Scrolling there would be bad for the user.

On Thu, 17 Oct 2013, Ryosuke Niwa wrote:
> > 
> > What would you call it if it never draws, but just notifies the UI? I 
> > think we could call it notifyFocusRingPath or something like that. Or 
> > we go with the scrollPathIntoView idea.
> or something like defineFocusableRegion/defineFocusableArea.
> I'd much refer names that signify the fact these functions define 
> focusable area/region than the fact it may draw the focus ring if the 
> element is already focused since authors need to call this function on 
> all focusable elements that are currently visible on the canvas.

You only actually need to call the draw*FocusRing() functions when you 
want to draw the focus ring (that it does nothing for unfocused controls 
is just a convenience so that you don't have to check if it is focused). 
If you want to give the AT position information when there's no visible 
focus ring, that's what addHitRegion() is for.

On Mon, 6 Jan 2014, Dominic Mazzoni wrote:
> On Mon, Jan 6, 2014 at 10:54 PM, Ian Hickson <ian at hixie.ch> wrote:
> > On Mon, 6 Jan 2014, Dominic Mazzoni wrote:
> > >
> > > I understand that the addHitRegion APIs are the intended way to 
> > > specify the permanent location of a fallback content element. But 
> > > practically speaking, it makes far more sense to "cache" the 
> > > bounding box of each element when you call draw*FocusRing, otherwise 
> > > you end up with a timing problem, because as soon as you focus the 
> > > fallback element, the screen magnifier immediately wants to know its 
> > > bounding box - but drawSystemFocusRing may not get called until 
> > > sometime later, when the app decides to repaint - and by then it's 
> > > already drawn the bounding box in the wrong location. Some 
> > > magnifiers respond to a "location change" event, but others don't, 
> > > requiring an ugly hack like sending a second focus event, or 
> > > delaying the first one.
> >
> > The paint is going to come within 16ms (unless the app has high 
> > latency, in which case it'll be unusable anyway). That's when the 
> > magnifier should be updated, not when the focus moves in the HTML 
> > document.
> We don't currently delay accessibility events until paint. It'd actually 
> be a lot of extra work to do this and I can't see any benefit outside of 
> this one corner case.

It's not a corner case, it's the whole design of the feature. :-)

You don't know where the control is if the author hasn't called 
addHitRegion(). The draw*FocusRing() methods aren't stateful. They're 
imperative. If they were to be stateful they'd need all kinds of 
additional stuff, just like addHitRegion() does, to handle marking 
previous information as out of date, etc.

To put it another way: it's not a matter of delaying the AT event. There's 
no AT event to fire, until the draw*FocusRing() method has been invoked, 
at which point there is a notification to send.

> > > The simplest implementation of the current spec to cache the 
> > > bounding box when you call drawSystemFocusRing and return whenever 
> > > the assistive technology requests the bounding box of that fallback 
> > > element, whether it's focused or not. I know it isn't exactly what 
> > > was intended, and addHitRegion will be a more powerful solution, but 
> > > I don't see the harm in implementing it this way.
> >
> > The harm is what I described in the earlier e-mails. You are likely to 
> > end up zooming on the wrong thing. For example, if the app only 
> > renders the focused element, and puts it in a random place when it's 
> > focused, you'll draw the bounding box wherever the control was last 
> > time it was rendered, which may have nothing to do with where it'll be 
> > this time. Similarly, if the control is moving, but you only update 
> > the bounding box when the control is first focused, then you'll leave 
> > the magnifier behind.
> I think that's a good argument that we need to handle the case where the 
> app updates the focus ring after it's been focused, which I agree with. 
> There's no reason to assume the app would update the location before the 
> first paint - it could just as easily begin a transition on the element 
> once it's focused, so it doesn't reach its 'final' location until 1 
> second later. We should handle this case as well as possible.

I'm not sure I follow. Transitions don't apply to canvas.

> I don't see that as an argument against caching the last known location 
> of an object too.

If you want to store state, that's what addHitRegion() is for. It's the 
retained mode API for canvas. The draw*FocusRing() methods are direct-mode 
APIs. There's no expiry logic, there's no API contract that implies that 
the calls will be made, or made correctly, if the element isn't focused.

As a meta-point: when you, as an implementor, disagree with the spec, the 
right way to approach this is to report the problem, describe the use 
cases, and so forth. If you want to implement something different, the way 
to do that is to design a coherent API, and make sure this new API doesn't 
conflict with the specced API, and then implement that new API. 
Implementing something that is "inspired by", and conflicts with, what the 
spec proposal suggests, is not going to lead to a coherent platform, 
because it is is essentially just mixing in multiple people's designs and 
goals without coming up with a coherent single vision.

To clarify, I'm not saying "implement what the spec says". I'm just 
saying, if you don't want to implement what the spec says, please do 
actually design a coherent complete solution. Otherwise we'll end up with 
things like a direct-mode API that acts like a retained-mode API but lacks 
core retained-mode features like expunging expired data.

Ian Hickson               U+1047E                )\._.,--....,'``.    fL
http://ln.hixie.ch/       U+263A                /,   _.. \   _\  ;`._ ,.
Things that are impossible just take longer.   `._.-(,_..'--(,_..'`-.;.'

More information about the whatwg mailing list