Search code examples
cssbrowserfocus

CSS - focus not working on any browser Why?


Hi there I really never had this problem but today I dont't really understand why the focus is not working - I tried to built a custom switch without using js --- and it looks good and with the mouse it is working. Unfortunately not with the focus state -

but here is the code:

input {
  appearance: none;
  position: relative;
  display: inline-block;
  background: lightgrey;
  height: 1.65rem;
  width: 2.76rem;
  vertical-align: middle;
  border-radius: 2rem;
  box-shadow: 0px 1px 3px #0003 inset;
  transition: 0.25s linear background;
}

input::before {
  content: "";
  display: block;
  width: 1.25rem;
  height: 1.25rem;
  background: #fff;
  border-radius: 1.2rem;
  position: absolute;
  top: .2rem;
  left: .2rem;
  box-shadow: 0px 1px 3px #0003;
  transition: 0.25s linear transform;
  transform: translateX(0rem);
}

:checked {
  background: green;
}

:checked::before {
  transform: translateX(1rem);
}

input:focus-visible {
  outline: 2px solid dodgerblue;
  outline-offset: 2px;
}

input:focus {
  outline: none;
  outline-color: transparent;
}

`
<label for="awesomeFeature">
  <input type="checkbox" name="awesomeFeature" id="awesomeFeature">
  my awesome feature
 </label>

I expect the focus highlightning


Solution

  • This is because the CSS selectors input:focus-visible and input:focus are both matching when the element is focused.

    :focus-visible is not more specific than :focus. In the absence of any other selectors, the browser sees your input:focus-visible and input:focus selectors to be as important as each other. Now, your input:focus-visible rules have a visible outline, but your input:focus rules have an outline of none. The browser can't do both, so it picks the final rule of that specificity to be the one that's applied. The input:focus's rule wins out and the element is given an outline of none.

    To fix this you could re-order your CSS:

    input:focus {
      outline: none;
      outline-color: transparent;
    }
    input:focus-visible {
      outline: 2px solid dodgerblue;
      outline-offset: 2px;
    }
    

    With this the :focus-visible rule is last and thus will be the one applied.