- David Bushell의 사이트가 일부 사용자에게 오래 깨져 보인 원인은 Grammarly 브라우저 확장이 페이지에 몰래 주입한 CSS였음
- Firefox 기준 Grammarly 확장은 로컬 확장 자산의 스타일시트를 삽입하며, 웹페이지의
StyleSheetList로 찾기 어렵고 Content Security Policy도 우회함
- 충돌은 Grammarly가
:root에 전역 정의한 --rem:16과 사이트의 유동 타이포그래피 계산용 --rem이 같은 이름을 쓴 데서 발생함
- 사이트 쪽
--rem은 cascade layer 안에 있었고, 레이어 밖 스타일이 우선하는 CSS 규칙 때문에 Grammarly 값이 계산을 덮어쓸 수 있었음
- 임시로는 mutation observer와
!important로 버텼지만, 최종 대응은 속성명을 --🤡로 바꾸는 것이었고, 확장이 전역 :root에 평범한 이름을 주입하면 웹페이지와 쉽게 충돌할 수 있음
페이지 안으로 들어온 Grammarly CSS
- 몇 달 동안 사이트 레이아웃이 어긋나고 크기가 이상하다는 산발적인 제보가 있었고, 스크린샷도 함께 전달됨
- 기술에 익숙한 독자들이 Grammarly browser extension을 주요 원인으로 지목했고, David Bushell은 Firefox 기반 Mullvad browser에 직접 설치해 확인함
- 확장 설치 시 권한은 다음을 포함함
- 모든 웹사이트 데이터 접근
- 알림 표시
- 브라우저 탭 접근
- Grammarly는 웹페이지에 로컬 확장 자산에서 로드되는 스타일시트를 주입함
- 확장은 사용자가 상호작용하지 않아도 모든 웹사이트의
<html> 문서에 <grammarly-desktop-integration> 커스텀 요소를 추가함
--rem 이름 하나가 레이아웃을 깨뜨린 과정
- Grammarly 스타일시트 끝에는 다음 CSS가 포함됨
:host,
:root {
--rem:16
}
- 같은 스타일시트의 다른 부분에서는
--rem을 사용해 글꼴 크기와 줄 높이를 계산함
.kE2Bj {
font-size:calc(0.86px*(var(--rem) - 2));
line-height:calc(1.2868px*(var(--rem) - 2));
}
- 사이트 역시 자체 유동 타이포그래피 실험을 위해
--rem 커스텀 속성을 쓰고 있었음
@layer base {
:root {
--rem: 0.0625rem;
--fluid: calc((100vi - (400 * var(--rem))) / (1920 - 400));
--font-size-h1: clamp(
calc(31 * var(--rem)),
calc((31 * var(--rem)) + (80 - 31) * var(--fluid)),
calc(80 * var(--rem))
);
}
}
- 사이트의
--rem은 cascade layer 안에 정의돼 있었고, 레이어 밖 스타일은 CSS specificity와 관계없이 레이어 안 스타일보다 우선함
- 소스 순서도 영향을 주므로 Grammarly의
--rem이 이겼을 가능성이 있음
- 그 결과 사이트의 계산식이 깨지고 레이아웃 문제가 발생함
- 초기에는 mutation observer로 추가된 웹 컴포넌트를 감지한 뒤
!important 스타일을 더해 대응함
- 정확한 원인을 파악한 뒤에는 사이트의 커스텀 속성명을
--🤡로 바꿈
- 이 이름은 CSS에서 유효한 커스텀 속성명임
--rem은 Grammarly가 전역으로 쓰기 때문에 충돌 위험이 있는 이름이 됨
- Grammarly는 무작위 클래스명을 만들면서도
--rem이라는 일반적인 커스텀 속성명을 :root에 전역 적용했고, 확장을 실제로 사용하지 않아도 모든 웹페이지에 코드를 주입함
- Grammarly 지원팀에는 연락했지만, 아직 문제를 이해하는 기술 담당자에게 닿지 못한 상태임