1

I have CSS like this

*:focus-visible {
  outline: 2px solid blue; /* This value is dynamically set based on configuration */
}

I now have a button with class toggle that has

.toggle {
  /* ... many other styles */
  outline: none;
}

This outline: none is to prevent the default focus outline from showing when the button is clicked. However, I'd now like the focus-visible style defined above to apply to it, since focus-visible styles are only applied in more specific cases, when the focus really should be visible, not just when the button is clicked.

I half expected something like this to work:

.toggle:focus-visible {
  outline: inherit;
}

But if I understand this correctly, inherit does not mean it's inheriting the value from the parent's focus-visible styles. Is there any way to do this without the .toggle styles having to duplicate the value set earlier? In other words, is there some way to have it "fall back"?

4
  • Another option would be to set outline: none with a selector like .toggle:not(:focus-visible), instead of resetting the value. Commented Aug 15, 2023 at 0:59
  • Ooh, I like that idea. I'll give it a go! Commented Aug 15, 2023 at 1:04
  • Or outline: unset; ? Commented Aug 15, 2023 at 1:28
  • Nope, unset sets it to the browser's default value. @DarrylNoakes's solution worked though. Daryl, if you post it as an answer, I'll accept it. Commented Aug 15, 2023 at 1:43

1 Answer 1

1

There is no way (AFAIK) to reset a value to what was set in another, certain ruleset.

Instead setting it to none by default for .toggle and then "resetting" it to the "active" value on a certain state, you can clear it only when it is not in that state.

This can be done by setting outline: none with a selector like .toggle:not(:focus-visible).

The code will explain better than I can:

/* As before */
*:focus-visible {
  outline: 2px solid blue;
}

/* Do not set it at all here. */
.toggle {
  /* outline: none; */
}

/* Remove the outline only when it is not :focus-visible. */
.toggle:not(:focus-visible) {
  outline: none;
}

This has a distinct benefit over the other approaches: users with browsers that do not support :focus-visible will get their browser's default focus ring, instead of nothing — because the last ruleset is dropped due to an unsupported/unrecognized and therefore "invalid" pseudo-class.


Another option would be to use a CSS custom property (AKA CSS variable) to hold the initial, default value. This would of course require that you can configure the the configuration system to set this custom property instead of outline directly.
Then you could do something like this:

/* Set a CSS var here instead of `outline`. */
*:focus-visible {
  --outline: 2px solid blue;
}

/* Use the outline if specified, else none. */
.toggle {
  outline: var(--outline, none);
}

This defaults to using none, but will use the value of the --outline var if it is defined.

A possible downside to the basic approach is that everything that relied on the outline being set would now have to set it from --outline. Instead, you can set the outline property as well: "naive" elements will just use it, but any that need it can be customized (such as buttons). As follows:

*:focus-visible {
  --outline: 2px solid blue;
  outline: var(--outline); /* This is new */
}

.toggle {
  outline: var(--outline, none);
}

This approach does unfortunately force outline to none unless --outline is set, so it disables default outline behavior, such as the browser's focus indicator.


A last-ditch option: use !important in the *:focus-visible rule, like so:

*:focus-visible {
  outline: 2px solid blue !important;
}

This should be avoided if at all possible, because !important makes things complicated and harder to maintain and modify in the long run. But sometimes you gotta do what you gotta do (although it usually means there are flaws somewhere in the system).

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.