[Tailwind CSS] 把我的樣式還來

2023/12/24閱讀時間約 5 分鐘

情境

前陣子使用 Tailwind CSS 開發的正舒服時,遇到一個常見的需求。

為了營運方便,網頁常常會留一個區域給客戶使用,客戶可以編輯 HTML 來達到想要的效果

這樣的好處是,客戶可以馬上應對業務需要,IT也不用重新佈署程式

壞處是要考量資安,不過都是內部來源,出、入口增加檢核,好好把關就能把風險降到最低

示意圖

示意圖


問題

Tailwind CSS 預設是會把 HTML 元素原生樣式移除,使所有元素都在相同基礎上設計,避免在各瀏覽器有不一樣的顯示。

以上面情境來看,客戶編輯的 HTML 內容就會不如預期,例如: <ul>、<li>、<h1>

如圖

比較

比較


native vs tailwind

Tailwind Preflight

官方稱此為 Preflight ( 很不容易想到的關鍵字… ),而 @tailwind base; 就是使 Preflight 作用的地方

這段Code有使用 Tailwind 應該都不陌生~

@tailwind base; /* Preflight will be injected here */
@tailwind components;
@tailwind utilities;

解決方法

  • 移除 @tailwind base; ⇒ 整個網頁排版會爆炸
  • 透過 Global/Scoped CSS 修改 ⇒ 有太多元素要考慮到,你不知道客戶會用到那些元素
  • Shadow DOM ⇒ 這次的解決方式

Shadow DOM 簡單介紹,可以想像 HTML 是一張紙,而 Shadow DOM 則是在藏於其後的另一張紙,而畫面渲染時會像是兩張紙疊起來顯示

這次則是利用DOM、Shadow DOM之間互不干擾的特性!


Shadow DOM

Shadow DOM


Vue 實作

先看結果

Demo

Demo

透過 vue directive 方便使用

Setup

import { DirectiveBinding, ref } from 'vue'

const htmlStr = ref(`
<h4>Top 3 Frontend Framewors</h6>
<ul>
<li>Vue</li>
<li>React</li>
<li>Angular</li>
</ul>`)

const vShadowHtml = {
mounted: (el: HTMLElement, binding: DirectiveBinding) => {
let shadow: ShadowRoot | null = el.shadowRoot
if (shadow == null) {
shadow = el.attachShadow({ mode: "open" });
}
shadow.innerHTML = binding.value
},
updated: (el: HTMLElement, binding: DirectiveBinding) => {
let shadow: ShadowRoot | null = el.shadowRoot
if (shadow == null) {
shadow = el.attachShadow({ mode: "open" });
}
shadow.innerHTML = binding.value
}
}

Template

<div class="p-5">
<div class=" text-red-300">Without Shadow DOM</div>
<div v-html="htmlStr"></div>
<br />
<div class=" text-red-300">Using Shadow DOM</div>
<div v-shadow-html="htmlStr"></div>
</div>

結論

使用 Tailwind CSS 可以方便管理樣式與提高開發效率,但遇到不同的業務情境時,還是要額外處理。

幾年前研究過 Shadow DOM,沒想倒在此時派上用場,救了我一把,因為這個問題發生是在專案 UAT 階段

另外,對於要不要使用框架,我自己的想法是八二法則~ 若框架能解決8成以上的問題,我就會考慮使用,而剩下的特殊狀況額外處理即可

參考

Tailwind CSS Preflight

Andy Tsou
Andy Tsou
留言0
查看全部
發表第一個留言支持創作者!