w3c / csswg-drafts

CSS Working Group Editor Drafts
https://drafts.csswg.org/
Other
4.47k stars 658 forks source link

Do we need :focus-visible-within ? #3080

Open dbaron opened 6 years ago

dbaron commented 6 years ago

While looking to close out w3ctag/design-reviews#233, one other thought occurred to me:

Is there a need for :focus-visible-within to complement :focus, :focus-within, and :focus-visible?

In particular, because of limitations on CSS selectors (that is, the difficulty of selecting an element that is an ancestor of an element with a particular characteristic), we've added :focus-within to have a version of focus that behaves like the hierarchical :hover and :active. I think this was desirable because it's not unusual for a visible container to be styled differently when something inside it has focus. Consider examples ranging from an operating system window to a <fieldset> with a bunch of form controls in it. (I suspect there may be better documentation if you trace from where we agreed to add :focus-within.)

It's not clear to me whether the use cases for :focus-within and the use cases for :focus-visible combine. Do we also need :focus-visible-within, or not?

/cc @alice @robdodson @bkardell @AmeliaBR @frivoal

AmeliaBR commented 6 years ago

I personally don't see a use case.

:focus-within is for cases where you really want to enhance focus styling (highlight/expand an entire group of interactive elements), :focus-visible is for cases when you want the styling to be more subtle, only-when-necessary.

But regardless, there's nothing stopping it from being added later if there is a strong demand from authors, once those authors have a chance to really work with the two new selectors.

Malvoz commented 6 years ago

This was brought up in https://github.com/WICG/focus-visible/issues/151

tabatkins commented 6 years ago

I don't think there's any significant use-case difference between "want to see if a descendant has :focus" and "want to see if a descendant has :focus-visible". If you want one, there's a good chance you might want the other. (Or to put it another way, :focus is often not what you actually want in the first place; you usually want :focus-visible, and thus you'll probably want a -within variant if you're currently suboptimally using :focus-within.)

However, if we do decide to add such a thing, I'm against just spamming more keywords into the name; that's unwieldy and you have to remember the (arbitrary) order of the modifier keywords. Instead, I'd say we should expand :focus into a :focus() functional pseudo, like :drop(), taking a list of keywords in any order. So you'd write :focus(visible within) or whatever.

Justineo commented 5 years ago

IMO it's better to have :has(:focus)/:has(:focus-visible) instead of :focus-within/:focus-visible-within. Not sure if this is discussed earlier.

Justineo commented 3 years ago

Is there any chance that we support the :has(...) syntax but temporarily restrict the usage within :focus and :focus-visible? That would introduce little performance overhead and provide some degree of extensibility.

An example is when :not() is first introduced, only simple selectors are allowed as its argument and in Selectors Level 4 we extended that to some non-simple selectors.

craigkovatch commented 2 years ago

Just want to pile on here to say: there are definitely use cases for this, and I also like Tab's idea of :focus(within whatever), assuming that ship hasn't sailed too far :D https://github.com/WICG/focus-visible/issues/151#issuecomment-1075797676

bkardell commented 2 years ago

Since I see some comment here, it's worth noting that :has is in development :has() in 2 engines (chrome 101+/supporting things based on that chromium build under experimental web platform features and webkit with TP versions like Safari in the most recent). In both cases, at least currently

.thing:has(:focus-visible) { .... }

works just fine, so if that ultimately lands it will address this issue (as well as several others, we should probably review)

craigkovatch commented 2 years ago

@bkardell I've no objection to that, although :focus(within whatever) wins on cuteness ;)

valtlai commented 2 years ago

:has() is enabled by default in stable Safari 15.4. 🎊

jpzwarte commented 2 years ago

A checkbox web component:

<span class="box">
  <slot name="input"></slot>
  <svg>...</svg>
</span>
:host(:focus-visible-within) svg {
  box-shadow: ...;
}

Atm, the only solution I see is to use JS.

i-am-the-slime commented 1 year ago

@jpzwarte Exactly how I ended up here, too.

jpzwarte commented 1 year ago

@i-am-the-slime I believe this is now possible using :host(:has(:focus-visible)) svg { ... }. Haven't tried it yet though.

WodarekLy commented 1 year ago

@i-am-the-slime I believe this is now possible using :host(:has(:focus-visible)) svg { ... }. Haven't tried it yet though.

Sadly, :has() does not work in Firefox by default, so this isn't a feasible solution for me. 😭

I think having a :focus-visible-within selector would be tremendously helpful!!!

bkardell commented 1 year ago

@i-am-the-slime I believe this is now possible using :host(:has(:focus-visible)) svg { ... }. Haven't tried it yet though.

Sadly, :has() does not work in Firefox by default, so this isn't a feasible solution for me. 😭

I think having a :focus-visible-within selector would be tremendously helpful!!!

But :has() is shipping by default in 2 of 3 engines, and is part of Interop 2023 - it is going to be universal comparatively soon. At that point, is there a remaining gap in what you can express (if it feels still slightly a little clunkier to express)? Or is it just sugar on that that you're asking for?

craigkovatch commented 1 year ago

Well, it's not just sugar, right? Because the clunkier version has a selector specificity of 3, whereas the others are 1. That will make for some awkward rule-writing at some point.

:focus :focus-visible :focus-within :host(:has(:focus-visible))

One of these is not like the others :) I think the dev- and CSS rulewriting-ergonomics here are nontrivial.

waterplea commented 1 year ago

I doubt you would ever want you :focus-visible styles to have low specificity and be overridden by anything. I came here with basically the same issue of checkbox component-like situation, but it looks like :has indeed has us covered and I believe this ticket can be closed.

craigkovatch commented 1 year ago

I’m not saying I want them to be low, but it would be nice for them to be consistent and predictable. As the author of a 70+ component UI library for about 6 years now, the predictability benefit extends to people who use and potentially want to override the library, too. It would be nice for them all to be the same.

waterplea commented 1 year ago

As the author of a 70+ component UI library for about 6 years now.

I can relate 😃 the thing to keep in mind, if that's the way to handle this, it would also be the same selector with same specificity when users want to override it, so I believe has would be a working solution to the issue once Firefox catches up.

ole-thoeb commented 12 months ago

From my understanding and testing :has inside :host() does not work. So :host(:has(:focus-visible)) is not an option or am I misunderstanding something?

woody-li commented 11 months ago

I agree that :has(:focus-visible) is a good solution.

But how to deal with shadow dom? It's just as ineffective as :has(:focus) for shadow elements (not the slot assigned elements).

bogger33 commented 8 months ago

just chiming in for another use case:

<label><input type="file"></label>

with the actual input hidden, and the label styled as a button that doesn't display the file name (e.g. for easy centering)

bkardell commented 8 months ago

I'm adding the agenda+ label here because I think we should resolve to close this issue and perhaps open another about shadow dom if relevant

steffchep commented 3 months ago

I have tried :has(:focus-visible) on the host of a web-component, but it does not seem to work - it gets ignored

here is my attempt:

    :host(:not([disabled]):has(:focus-visible)) {
        outline: 2px solid var(--vs-color-interactive-outline, initial);
        outline-offset: 2px;
        box-shadow: 0px 0px 0px 2px var(--vs-color-main-background);
    }

.some-class:has(:focus-visible) works just fine, but it seems :has does not like :host

So, I would really love to have a :focus-visible-within. Or :has to work with :host :)

Edit: just seen a similar comment further up, apologies!

css-meeting-bot commented 2 months ago

The CSS Working Group just discussed Do we need :focus-visible-within ?.

The full IRC log of that discussion <emilio> q+
<TabAtkins> astearns: Brian added a comment just asking to close the issue (and open a different one about shadow dom)
<TabAtkins> dbaron: I think we should punt, people missing probably have strong opinions
<astearns> ack emilio
<TabAtkins> astearns: would be great if those strong opinions were written in to the issue
<TabAtkins> emilio: I know focus does propagate out from shadow trees
<TabAtkins> emilio: :has() mostly covers this, just not when shadow dom is involved
<TabAtkins> emilio: And for that, :focus works, but :focus-visible... probably not?
<TabAtkins> emilio: I think moving it out of the shadow tree would cause undesired outlines
<TabAtkins> emilio: So main question is if people are having to work around this with JS we probably want something like this, but...
<TabAtkins> emilio: I don't see a wya of making it work with shadow dom
<TabAtkins> emilio: Slgihtly against closing because there's a use-case that seems valid, and don't see how it can be addressed without this
<TabAtkins> astearns: So punt on this today. keep the agenda tag on it? or solicit opinions first?
<TabAtkins> astearns: Slgihtly inclined to take the tag off
<TabAtkins> dbaron: I think taking it off is fine, tho I think i'm in the opposite camp of Brian (i think we should add it)
rgpublic commented 2 months ago

+1 for :focus-visible-within. It's just intuitive and easier to write than :has(...). We already have :focus-within (because you don't always want to style the focus outline on the element itself) and we have :focus-visible (because most of the time you want the focus outline only to appear for keyboard users). These two things are totally separate requirements that have nothing to do with each other. So it should be possible to combine them. I was actually quite surprised to find that :focus-visible-within doesn't exist. If we add :not(...) to the mix we could avoid another nesting-level and keep things simple. Just my 0,02€.

josepharhar commented 1 month ago

Here is a use case for :focus-visible-within for customizable select, based on this demo: https://codepen.io/una/pen/dyBWYxR

Without focus-visible-within, we have to use this selector:

select:has(option:focus-visible)::picker(select)

With focus-visible-within, we could do this instead:

::picker(select):focus-visible-within