2351 字
12 分鐘

CSS 文字外框怎麼做?用多層 text-shadow 畫出圓潤中文描邊

最近開發畫面時剛好需要一圈醒目的文字外框。太久沒寫這類 CSS,第一時間只記得 text-shadow-webkit-text-stroke,實際套到粗體中文字後卻不太滿意:筆畫轉角容易顯得尖,描邊也會往字形內部延伸,讓原本的字面變窄。

最後我採用多層 text-shadow:把沒有模糊的文字副本往四周平移,密集疊成一圈。因為外部陰影會畫在文字下方,原本的文字填色會蓋回中央,看起來就像一圈只長在外側、轉角較圓的外框。

先看最短版本:8 個方向疊一圈#

如果外框只有 1px 左右,先從八方向版本開始就夠直覺:

<h1 class="text-outline">文字外框</h1>
.text-outline {
color: #ffffff;
text-shadow:
1px 0 0 #166534,
0.71px 0.71px 0 #166534,
0 1px 0 #166534,
-0.71px 0.71px 0 #166534,
-1px 0 0 #166534,
-0.71px -0.71px 0 #166534,
0 -1px 0 #166534,
0.71px -0.71px 0 #166534;
}

8 方向 text-shadow 文字外框的實際渲染效果

每一組值的格式都是:

text-shadow: offset-x offset-y blur-radius color;

1px 0 0 #166534 為例,瀏覽器會把同一段文字往右移 1px,不加模糊,再用綠色畫一次。把副本放到上、下、左、右與四個斜角,文字外圍就會被同色副本包住。

這裡的 0.71px 約等於 1 × cos(45°)。若斜角也寫成 1px 1px,它與中心的直線距離會變成約 1.41px,外框就不再是同一個半徑。

為什麼多層 text-shadow 不會把原字蓋住?#

CSS Text Decoration 規格把外部 text-shadow 的繪製層級放在元素背景與文字之間。簡化後可以理解為:

文字填色
────────
多層 text-shadow
────────
元素背景

所以這個做法不需要再複製一份 HTML,也不用靠偽元素把原字疊回來。只要文字的 color 是不透明色,位於中央的陰影自然會被文字本身蓋住,畫面上主要看到的就是外圍部分。

text-shadow 也不參與排版計算。外框變粗不會增加元素寬高,因此要自己檢查標題是否貼到容器邊緣、相鄰行是否太近,必要時增加 paddingline-height

外框加粗後,為什麼八方向開始出現缺口?#

把八個位移全部從 1px 改成 4px,方向之間的距離也會跟著拉開。中文字的轉角與細小筆畫可能露出缺口,看起來像鋸齒或八角形。

比較穩定的做法是同時增加兩件事:

  1. 增加圓周上的方向數量,讓相鄰副本更靠近。
  2. 增加內圈,填補中心到最外圈之間的空白。

這個幾何概念接近把字形向外做形態膨脹(morphological dilation):以不同方向的位移副本掃過字形,外緣便會接近圓形結構元素擴張後的輪廓。方向越密,圓周近似越平滑;但它仍是離散取樣,不是真正連續的向量描邊。

用 JavaScript 產生 24 層圓形外框#

手動維護 24 組座標很容易改錯,我把它整理成一個沒有框架依賴的產生器:

function createTextOutline({
color,
radius = 3,
rings = 2,
steps = 16,
}) {
const shadows = [];
for (let ring = 1; ring <= rings; ring += 1) {
const currentRadius = (radius * ring) / rings;
const currentSteps = Math.max(8, Math.round((steps * ring) / rings));
for (let index = 0; index < currentSteps; index += 1) {
const angle = (Math.PI * 2 * index) / currentSteps;
const x = Number((Math.cos(angle) * currentRadius).toFixed(2));
const y = Number((Math.sin(angle) * currentRadius).toFixed(2));
shadows.push(`${x}px ${y}px 0 ${color}`);
}
}
return shadows.join(", ");
}
const title = document.querySelector(".text-outline");
title.style.textShadow = createTextOutline({
color: "#166534",
radius: 3,
rings: 2,
steps: 16,
});

預設會產生:

  • 內圈:半徑 1.5px,8 個方向。
  • 外圈:半徑 3px,16 個方向。
  • 合計:24 層沒有模糊的 text-shadow

雙圈 24 層 text-shadow 文字外框的實際渲染效果

x = r × cos(θ)y = r × sin(θ) 會把每個位移點放在同一個圓周上。用 CSS 變數可以把填色、外框色集中管理:

.text-outline {
--text-fill: #ffffff;
--text-outline: #166534;
color: var(--text-fill);
}

接著把 getComputedStyle(title).getPropertyValue("--text-outline") 傳給產生器即可。如果樣式在建置時就能確定,也可以先執行一次產生器,把結果貼回 CSS,避免每次載入都用 JavaScript 設定。

text-shadow 與 -webkit-text-stroke 怎麼選?#

-webkit-text-stroke 雖然保留了 -webkit- 前綴,但 MDN 已標示為 Baseline Widely available;現在選擇它或 text-shadow,重點不是「能不能用」,而是想要哪一種外觀與維護方式。

粗體中文字使用 -webkit-text-stroke 的實際渲染效果

比較項目-webkit-text-stroke多層 text-shadow
寫法一行即可完成需要多組陰影或產生器
描邊位置直接沿字形輪廓描繪,粗描邊會占用字面空間陰影在文字下層,中央由原字填色蓋回
轉角觀感依字型、字重與瀏覽器渲染而定,粗體中文字可能出現尖角以圓周位移取樣,可用方向密度調整圓潤度
效能成本宣告簡單,適合大量或動態文字陰影層數乘上元素數量,重繪成本較高
適合情境一般標題描邊、空心字、需要簡單維護少量大標、活動字卡、需要柔和外框的中文字

無外框、text-stroke、8 方向與 24 層 text-shadow 的四種效果比較

如果 -webkit-text-stroke 的結果在實際字型上已經好看,我會直接用它。只有在「描邊壓縮字面」或「轉角太尖」真的看得出來時,才換成多層陰影。

注意:外框效果高度依賴字型輪廓、字重、裝置像素密度與瀏覽器的反鋸齒結果。不要只在一組英文字型上調完就結束,至少要用實際會出現的繁體中文字、數字與標點一起測試。

三種常用的 CSS 文字特效#

同一個 text-shadow 還能做出其他效果,差別主要在位移、模糊半徑與疊加順序。

發光文字:同位置疊加不同模糊半徑#

.text-glow {
color: #d9f99d;
text-shadow:
0 0 4px #84cc16,
0 0 14px #65a30d,
0 0 34px #3f6212;
}

所有陰影都留在原點,再從小到大疊三層模糊,就能形成由內而外的光暈。這種效果適合短標題,不適合整段內文。

多層模糊 text-shadow 形成的發光文字效果

浮雕文字:一亮一暗模擬光源#

.text-emboss {
color: #cbd5e1;
text-shadow:
-1px -1px 0 #ffffff,
2px 2px 0 #475569;
}

左上方放亮色、右下方放暗色,會產生凸起的錯覺;把亮暗方向互換,就會更像壓進背景的凹版文字。背景與文字顏色要接近,效果才不會變成單純的雙色重影。

一亮一暗 text-shadow 形成的浮雕文字效果

漸層文字:把背景裁進字形#

.text-gradient {
color: #38bdf8;
background: linear-gradient(90deg, #38bdf8, #a3e635);
background-clip: text;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}

這個做法不是陰影,而是用文字當成背景的裁切範圍。保留 color 當作降級顏色,避免不支援裁切或高對比模式下完全看不到文字。

background-clip text 形成的漸層文字效果

如果你也在整理 UI 細節,可以接著參考 Skeleton 與 Shimmer 載入效果CSS 圖片比例控制:aspect-ratio vs Padding Hack純 CSS 實作內凹圓角頁籤

實作時要記得的限制#

  • 只套在少量大字:24 層陰影乘上大量節點,會增加繪製成本;不要替整篇內文描邊。
  • 不要套在包住內文的父層text-shadow 會繼承。若把效果設在整張卡片,子元素文字也可能一起吃到陰影。
  • 粗細增加時同步加密方向:只放大半徑、不增加取樣點,外框會露出缺口。
  • 預留外框空間:陰影不影響盒模型,也不會替你撐開容器。
  • 檢查可讀性:裝飾不能取代文字與背景的基本對比。WCAG 對一般文字要求至少 4.5:1,大型文字至少 3:1;背景複雜時,外框可當作 halo 協助分隔,但仍要用實際相鄰顏色檢查。

這次最後留下的判斷很簡單:細描邊、數量多,先用 -webkit-text-stroke;少量粗體中文字遇到尖角或字面被吃掉,再用多圈、多方向的 text-shadow。未來要調粗細時,不只改半徑,也一起調整圓周取樣數。

常見問題#

Q: CSS 文字外框可以只往外長、不壓到文字嗎?#

A: 可以用多層、零模糊的 text-shadow,把文字副本往四周位移。外部文字陰影位於文字填色下方,中央區域會被原字蓋住,因此視覺上主要留下字形外側的部分。外框越粗,越需要增加方向數與內圈,避免出現缺口。

Q: 為什麼中文套 text-shadow 外框會有刺或鋸齒?#

A: 常見原因是只用了四向或八向陰影,卻把位移半徑拉得太大。圓周上的取樣點距離變遠,中文字複雜轉角間就會露出空隙。可以增加圓周方向、補內圈,並用實際字型在不同裝置像素密度下檢查。

Q: text-shadow 外框會影響元素尺寸嗎?#

A: 不會。text-shadow 屬於繪製效果,不會參與版面配置,也不會替外框增加元素的寬高。若外框靠近容器邊界或相鄰文字,需要自行用 paddingmarginline-height 預留空間。

參考資料:

W3C CSS Text Decoration Module Level 4:text-shadow

MDN:text-shadow

MDN:-webkit-text-stroke

WebKit:Introducing Text-Stroke

W3C WAI:Understanding Success Criterion 1.4.3 Contrast

CSS 文字外框怎麼做?用多層 text-shadow 畫出圓潤中文描邊
https://laplusda.com/posts/css-rounded-text-outline/
作者
Zero
發佈於
2026-07-23
許可協議
CC BY-NC-SA 4.0
這篇文章有幫助嗎?

回報錯字、失效連結,或告訴我你想看的延伸主題。