Chrome 79 - 遲到 6 年的 rem/em 修正

2019/12/15閱讀時間約 14 分鐘
眼看 Chrome 79 大概再兩周就要釋出啦!
對大多數人來說,這個也就是 Chrome 例行更新的一個版本。
而對我,Chrome 79 修正了一個最近一年多在我心裡佔有一席之地的 bug,我曾經努力試著解決它,最後似乎也找到自己應對它的方法。而現在它要被修掉了,多少有點感觸 😥。
本文特別想趕在 Chrome 79 正式釋出之前寫出來,讓在閱讀的你也可以一同經歷這個變化的過程。
想直接看結論也可以往下到 TL;DR 的部分喔!

Chrome 的「最小字體」傳說

我想小有接觸前端的人大概都知道 Chrome 有個 最小字體是 12px 的傳說,然而時過境遷、滄海桑田,2019 的現在還是如此嗎?
實際上現在再去 Google “chrome minimum font size” 得到的結果跟討論大都是 2013 年以前的內容,就算有稍微近期的文章,也大都是參考早期文章寫出來的。
從搜尋結果裡面可以找到這個 issue:
裡面提到幾件重要的事情:
  • Chrome 預設的最小字體是和 UI 使用的語言有關的
  • 大部分的 UI 語言預設最小字體是 1px
  • 複雜 UI 語言(多為亞洲語言)的預設最小字體是 10px/12px
  • 2010年時還沒有辦法從設定該改最小字體的設定
而到了 2019 的現在,最小字體已經可以從設定裡面更改了,位置就在「設定」->「自訂字型」
可能很多人會是在看到這篇文章之後才第一次打開這個設定畫面,比對一下上面列出來的項目可以發現繁中 UI 的 Chrome 的最小字體預設值就是 12px。
我猜如果從安裝時就是英文版 UI,預設值應該會是 0px/6px。
曾經是傳說的最小字體 12px,僅限於某些 UI 語言(基本上就是中文、日文、韓文、泰文)的瀏覽器。
現在其實是使用者把它調低就可以解決的問題。當然,對於開發者來說還是一個無法跨越的障礙。
做一個 fiddle 來測試一下「沒調設定」跟「調過設定」的結果: https://jsfiddle.net/pc035860/jgtfdL98/4/

Twitch 聽說你是 rem 排版大師?

自從近年各瀏覽器對 rem 的支援度有提高之後,坊間出現一些使用 rem 為單位進行網頁排版的文章,並且漸漸也有些網站開始使用這種排版方法(雖然我覺得還是相對蠻少的)。
建議往下看之前要先瞭解一下用 rem 單位進行排版大概是怎麼樣進行,可以參考下面這篇 sitepoint 的文章 https://www.sitepoint.com/understanding-and-using-rem-units-in-css/
普遍常見的起手式,是把 rem base 設為 1px 或 10px,這樣一來直接使用 rem 單位換算的過程會簡單很多。
其中又以在 <html> 上用 font-size: 62.5% 來設定為 10px 最常見:

Twitch 的大改版衝擊

Twitch 在 2018 年 2 月左右做了一次重大改版,把原本用的 Ember.js 換成 React,這一次改版同時也把網站的排版單位改為 rem,而這也是我們繁中 Chrome 使用者的噩夢開端(好像講得有點誇張了)。
改版之後,原本頻道的資訊頁面變得非常奇怪。下面示範截圖是目前最新版,當然也還是用 rem 單位排版。
正常來說應該是這樣:
改版後就直接跑掉:
這還只是第一眼就覺得不對勁的部分。
用 Firefox 打開 Twitch 來比較就可以看出整個網站都有點不一樣,Chrome 整體似乎大了一號,這邊就單取側邊欄來對比:
WTF!? 不是, Twitch 難道沒有測過嗎…

怎麼一回事

來看一下 Twitch 的 <html> 怎麼寫的
也就是他們的 rem base 是設定為 10px,接著看看對應的 side-nav class 寬度是多少呢?
10 * 24 = 240,他們希望的寬度是 240px,那麼實際上在我們繁中 Chrome 顯示會是多寬呢?
288 / 24 = 12,結果不言而喻啦!
Chrome 的最小字體被套用在 <html> 上,導致 rem 的 base 直接用 12px 起跳。
類似問題,其實也曾經在 Apple 的網站上出現過,不過當時是用 em 為單位。
眼尖的你可能會在上面的截圖中發現,字體大小是相同的。那麼我們就接著來看字體大小的部分:
14 / 1.4 = 10 剛好是設定的 rem base 10px,也就是說 font-size 部分並沒有受到 <html> 上 12px 的影響,依然故我的使用了 10px 來計算。接著我順便檢查了 padding, margin 之類的屬性,發現結果跟上面 width 一樣是用 12px 來計算的,看來只有使用在 font-size 時候的計算是分開的。
於是我們稍微修改一下上面論述,得到一個小結論:
Chrome 的最小字體被套用在 <html> 上,導致 rem 單位使用在 font-size 以外的屬性上時,其 base 直接用 12px 來做計算
這邊我做了一個簡單的驗證用 fiddle: https://jsfiddle.net/pc035860/athfj0dc/18/
裡面的 .box ,希望透過 rem 設定出的正確數值如下:
後面也還會用到這些數值來做正確性的驗證,稍微有個印象就好了。

好啦,都是 Chrome 的錯

前面說過現在最小字體可以調整是吧,那我們把最小字體的設定調到 10px 以下不就好了嗎?
那我們就拿上面驗證用的 fiddle 來比較一下,「保持預設 12px」跟「調整到 0px」的結果:
怎…怎麼這樣,竟然沒有變!?

UI 語言的陷阱

冷靜想想,Twitch 團隊在改版過了一年多的現在都沒有修正是什麼原因?
  • 無法 reproduce 問題
  • 只有少部分的人有問題,不用管他們
難道問題出在語言上? 讓我們把 Chrome UI 語言設定成英文試試看。
藍框處顯示目前是中文,點擊紅框處可以切換為英文。(註: Mac 使用者需要從「系統偏好設定」->「地區與語言」裡面改)
接著再用上面的 fiddle 來測一次最小字體 0px:
沒錯,把 UI 切成英文後就一切正常了。
順帶一提,初始安裝英文 UI Chrome 的最小字體應該是 0px/6px 其中一個,可以直接得到上圖的結果。因此如果系統語言不是上述亞洲語系的話,壓根就沒有機會看到這個 bug。
總之雖然原因不明,但看起來在繁中 UI 的 Chrome 下,就算已經調整過最小字體,在 <html> 上用 font-size: 62.5% 並沒有辦法把字體壓到 12px 以下。

繁中 UI 的最後一搏

看到這裡是不是你也覺得繁中 UI 的 Chrome 使用者真是太絕望了…
在我多番嘗試之後發現,剩下最後一手可以解決這個問題:
<html> 上指定 rem base 的方式直接寫成 font-size: 10px
雖然會失去瀏覽器設定「字型大小」的彈性,但不失為一個解決辦法。
用這個更新的 fiddle 來測試: https://jsfiddle.net/pc035860/athfj0dc/20/
其實避免碰到上面這種 rem 單位排版問題的方法就是總是將 base 設為 12px 以上。雖然會造成使用時換算單位的麻煩,但總比碰到這種難以 debug 的問題來的好。
同場加映一個也碰到類似問題的網站 issue: https://github.com/squidfunk/mkdocs-material/issues/911

擴充功能: Twitch REM fix

於是我為了我往後美好的瀏覽 Twitch 體驗,我決定做將這個 workaround 做成 Chrome extension。
起初還有「要判斷當前 UI 語言之後再來套用」之類的天真(?)想法,靜下心來仔細想想,沒有問題的人根本不會來裝這個extension
釋懷後的我,緩緩地開了個新的 CSS 檔案,在裡面輸入:
contentscript.css
大功告成。
截圖跟寫說明花的時間還比打這段 CSS 多…
基本上這個 extension 會在 *.twitch.tv 下面無腦套用 contentscript.css 的內容,有修正版面需求的人可以到下面的 Github 網址逛逛:

所以 Chrome 79 是?

好了,上面說了這麼多,總算可以進入正題啦!
來看這個 2013 年的 Chrome issue
這個 issue 是我在最小字體的相關搜尋中翻到的,主訴是「rem/em 用作單位時,在計算結果前就被套用了最小字體的限制」,怎麼聽起來這麼耳熟?
Use computed font-size for em/rem resolution.
This makes us match how Firefox applies minimum font-size and makes us not break relative length computation when pages use a font-size less than the user’s minimum font-size as a basis for widths, heights, margins, padding, etc.
從中推敲一下,未修正前的版本大概可以這樣表示(純示意,我沒有去翻 source code):
這個 issue 最近終於修正完畢,在 Chrome 79 會 release。
在修正後,resultRelativeLength 的式子大致上變成這樣(純示意,我沒有去翻 source code):
我實際從 beta channel 安裝 Chrome 79 的測試的結果也沒有問題,真的修正啦!
什麼都不用做,只要更新到 Chrome 79 以上、上面的 extension 也不用裝,各種 rem 單位排版造成的問題就迎刃而解。
如果你常在看的網站 (例如: Twitch),突然變了(回原)樣,千萬別太驚訝喔 😉
值得特別提出的是,在 Chrome 79 的繁中 UI 上用 font-size: 62.5% 還是沒有辦法把渲染字體壓到 12px 以下,就算你調了最小字體的設定也一樣。 更多討論請見本文最後的附錄。

TL;DR

🍖 Chrome 的最小字體其實現在可以設定了,並且根據安裝時的 UI 語言,預設值會不同
  • 中文、韓文、日文、泰文等 是 10px/12px
  • 其他通常是 6px 或以下
🍖 Twitch 改版後使用了 rem 單位排版,在繁中 UI 的 Chrome 看起來會跑掉
🍖 在繁中 UI 的 Chrome 底下,如果 font-size 用百分比單位表示,就算調了最小字體設定也沒有辦法被壓到小於 12px
  • 因此造成 Twitch 改版後版面跑掉
  • 把 UI 語言調成英文可以解決這個問題
  • font-size: 10px 取代 font-size: 62.5% 是一種 workaround
  • 我把 workaround 做成 extension 了
🍖 Chrome 79 會修正套用最小字體的方式
  • rem 單位排版計算錯誤的問題會消失
  • 可能有些你看習慣的網站會有變化 (例如: Twitch)
  • 套用在 <html> 上時,繁中 UI 百分比 font-size 的最小值依然是 12px (見附錄)

後記

從去年改版後, Twitch 版面的問題就一直困擾著我,有點像是揮不走的蒼蠅,每次打開網站就看到 😨
直到最近(2019/9)又再一次改版,新的版面搭上崩壞的 rem 單位真的是慘不忍睹,我只好弄了一個 extension 先撐著。
偶然發現下一版 Chrome release (大概兩周後)就可以躺著解決(?),發這篇文正是為了紀念這期間我內心的掙扎,還有製作擴充功能(跟它的說明)的流下的汗水。
我會懷念你的, Chrome 78 (含以前)!
感謝你讀到這邊,下次有有趣的東西要分享的話,我會再回來的。

附錄: 最終負載測試

以下的測試用 Chrome 79 beta 進行,「最小字體」都已經設定為 0 。
這次在旁邊加了使用同樣 rem 做為長寬的 div 來對照: https://jsfiddle.net/pc035860/jgtfdL98/35/
分別對 繁中 / 英文 UI 版本 以及 font-size: 10px / font-size: 62.5% 的組合做交叉測試,得到的結果如下:
🍖 rem 數值用在 width, height 上不受 font-size 指定方式的影響
🍖 在使用者可以設定的「最小字體」之外,還有別的隱藏字體大小門檻
  • 只在 font-size 用百分比設定時套用
  • 根據 UI 語言會有不同的數值
    🍺 繁中 UI 是 12px
    🍺 英文 UI 是 9px
  • 從 rem base 的角度來看,門檻應該是套用在計算完 rem -> px 結果之後,否則理應會影響我們用 rem 表示的寬跟高
比較一下 Firefox Developer Edition 71 的測試結果:
我想 Chrome 應該是出於好意啦… 嗯…
最後把這個隱藏門檻也考慮進去,整理進前面提到的式子:
為什麼會看到廣告
批歪
批歪
你好,我叫批歪,是一個前端工程師。 雖然我自己已經有一個獨立的 blog,但還是希望能夠透過方格子讓更多人能有機會看到我寫的文章,同時也能為方格子的讀者帶來比較不一樣的內容。
留言0
查看全部
發表第一個留言支持創作者!