버튼 포커스 시 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