Geuni

버튼 포커스 시 Border로 인한 레이아웃 시프트(Layout Shift) 방지하기

버튼 컴포넌트를 focus(이하 포커스)했을 때, border를 통해 포커스 되었음을 사용자가 인지할 수 있게 만들었다. 이때 border의 두께가 2px 이상일 경우, 다음과 같은 Layout Shift가 발생한다.

border 1px
border 2px

어떻게 해결할 수 있을까?


margin

button:focus {
  border-width: 2px;
  border-color: #93c5fd; /* blue-300 */
  margin: -1px; /* prevent layout shift */
}

먼저 focus 되었을 때 border 1px일 경우, shift가 발생하지 않는다.
border가 2px 일 경우, margin를 -1px로 설정하여 레이아웃 시프트를 방지할 수 있다.

즉, border의 두께만큼 주변의 레이아웃 변경을 margin으로 상쇄시키는 것이다.
border가 4px일 경우, margin을 -3px로 설정하여 레이아웃 시프트를 방지할 수 있다.


box-shadow

button:focus {
    outline: none;
    box-shadow: 0 0 0 2px #93c5fd;
}

box-shadow를 통해


outline

button:focus {
      outline: 2px solid #93c5fd;
      outline-offset: 0;
    }

outline을 통해 버튼의 높이를 조정하여 레이아웃 시프트를 방지할 수 있다.



마무리

  • outline, box-shadow는 border width를 사용했을 때보다 얇은 두께가 반영된다.
    • 나의 경우엔 border-width가 피그마에서 계산된 값이 반영되도록 하고 싶었고, shift는 방지하고 싶었기에 margin을 사용했다.
  • tailwind에서 outline과 box-shadow는 각각 outline, ring으로 제공된다.
  • radix 기준으로는 focus-visible 속성을 통해 포커스 되었을 경우, outline을 사용한다.

참고자료

레이아웃 변경 횟수(CLS)
how to prevent shifting when changing border width
What's the difference between outline and ring in tailwind

버튼 포커스 시 Border로 인한 레이아웃 시프트(Layout Shift) 방지하기 | geuni