CSS大混亂
CSS → Sass → CSS Module → JSS。寫於2020/11/12。之後的事之後再說。
三位一體:html、css、JavaScript
於現在網頁的開發於,html、css、JavaScript 已是三位一體。各項可分離但又密不可分。在技術發展上各自獨立;在應用上缺一不可。其中 CSS 是最混亂的剛好有機會順便整理。
關鍵名稱清單
CSS
CSS
Cascading Style Sheets。參考:階層式樣式表(CSS)。 為全靜態的組態於 Modern Web 是完全不敷使用的。
SCSS(Sass)
/Less
SCSS(Sass)
/Less
Sass: Syntactically Awesome Style Sheets less css: Leaner Style Sheets
皆為CSS預處理器。提供變數與一些彈性,但關鍵的 CSS conflict/collision selectors
問題仍存在。
Sass 與 SCSS 現已互為別名,泛指相同的標的於版本發展中命名不同。
SCSS, LESS 是不同的技術但解決同樣的問題,也用近乎相似的方法(因為互相學習)所以指令也大同小異。
CSS Modules
參考:CSS Modules 用法教程 (此篇文章技術內容已過時,但說明仍有參考性)
終於解決了 CSS conflict/collision selectors
問題,方法就是為 css class name 取一個獨一無二的ID名稱。如:foo-clsname
→(pre-compile)→ foo-clsname-xzqse98j
,邏輯名稱 + 亂數字串。
CSS-in-JS
把 CSS 寫在 JavaScript 中。由於 modern web 中的css套用太複雜,所以有了各自負責並互不干擾的需求。 也就是說,在某個組件內所撰寫的樣式,即使有相同 class 的命名,但在最後編譯後這些樣式都只會作用在該組件內,不會干擾到外層或其他組件的樣式。參考: 把 CSS 寫在 JavaScript 中。
不同的技術會有不同的指令實作,但指令形式一樣。 styled-component 是一個 CSS-In-JS 的函式庫,使你可以在 JSX 中撰寫 CSS code,更方便的是他可以接到 component 的 props 值來動態改變 css 樣式。參考:styled-component 是什麼
JSS
JSS 是一個 CSS in JS 的函式庫。不同的前端技術會有各自的JSS方案。比如:React-JSS、Material-UI.v4。大概都會有幾個共同的指令,如:、makeStyles
, ThemeProvider
, withStyles
等等的。
TSS (tss-react)
簡單的說就是 TypeScript 版本的 JSS,之中也有提昇/調整部份規格並與 Material UI.v5 有緊密整合。
'tss-react' is intended to advantageously replace the now deprecated @material-ui v4 makeStyles and react-jss by providing much better TypeScript support.
A tool for transforming CSS with JavaScript
一套 CSS 後處理工具模組,有各種 CSS 後處理的插件。
為 CSS 後處理器(post processor),處理位置上與 SCSS/Less 相反。它可以做的事還滿多的最有名的產品之一就是 Tailwind CSSostCSS 最基本的能力就是把『標準的CSS』再 postfix 時期再加入各家瀏覽器的前綴詞,如:
// 轉換前...
.example {
display: grid;
transition: all .5s;
}
// 用Autoprefixer轉換後...
.example {
display: -ms-grid;
display: grid;
-webkit-transition: all .5s;
-o-transition: all .5s;
transition: all .5s;
}
優點:就是“跨瀏覽器” 還可以除錯等等。上述案例是安裝了 Autoprefixer 插件。
缺點:PostCSS 有各種的插件不是統一的標準。PostCSS 的能力來自安裝了多少插件。PostCSS 基本功能不包含CSS分隔,這項能力要另外安裝搭配的相關插件。
補充 on 2022/6/16
一套適用於過場動畫的模組。有獨特的設計理念如同其命名 spring (彈簧)一樣的自然而然。
範例一:將會定時翻動文字
import type { CSSProperties, FC } from 'react'
import { useState } from 'react'
import { useSpring, animated, config } from 'react-spring'
// 範例一:將會定時翻動文字
const SpringBasic2_FlipText: FC = (props) => {
const [flip, setFlip] = useState(false)
// 設定得會動的 spring style 物件
const aniStyle = useSpring({
opacity: 0,
from: { opacity: 1 },
reset: true,
reverse: flip,
delay: 500,
config: config.molasses,
onRest: () => setFlip(f => !f),
})
return (
<div>
{/* 將會動的 anyStyle 物件帶入 animated 元素。 */}
<animated.div style={aniStyle}>
{props.children}
</animated.div>
</div>
)
}
範例二:依訊息翻動文字(使用 api
)
import type { CSSProperties, FC } from 'react'
import { useState } from 'react'
import { useSpring, animated, config } from 'react-spring'
const SpringBasic4_SpringApi: FC = (props) => {
const [aniStyle, api] = useSpring(() => ({ opacity: 1 }))
//設定 CSS 初始狀態:顯現。並拿到 spring api。
return (
<div>
{/* 依訊息觸發 api.start() 函式指定目標CSS並開始變化。 */}
<AButton mutant='primary' label="隱藏 "
onClick={() => api.start({ opacity: 0 })} // CSS 變化目標:隱藏
/>
<AButton mutant='primary' label="顯現"
onClick={() => api.start({ opacity: 1 })} // CSS 變化目標:顯現
/>
{/* 將會動的 anyStyle 物件帶入 animated 元素。 */}
<animated.div style={aniStyle}>
{props.children}
</animated.div>
</div>
)
}
如何應用
較新的 CSS 解法方案太概都會疊加之前的技術,不過不能保證會完全疊加,這也是CSS混亂的原因之一。總之我個人把 Sass + CSS Module + JSS 疊加混用才解決css套用的綜合問題。
Sass 讓CSS有彈性。 CSS Module 解決 conflict/collision 問題。 JSS 提高 React 前端元件 closure 屬性。 styled-components 單純的元件就用這招。
整合 Sass、CSSModule 與 JSS
在scss 定義全域基本共通參數與通用的 layout 與元件呈現設計,比如:色系等。
用CSS Module 之 :export
指令匯出給 React/JavaScript 使用。
最後用 JSS 指令 makeStyles
等等套用 css 組態到元件。
範例 --- 依然很混亂啊
/* variables.scss */
$white-color: #fcf5ed;
$dark-color: #402f2b;
$light-color: #e6d5c3;
$medium-color: #977978;
$alert-color: #cb492a;
$light-black-color: #706e72;
$black-color: #414042;
/*** 將 SCSS 變數匯出成JsvaScript物件 ***/
:export {
colors: {
white: $white-color;
dark: $dark-color;
light: $light-color;
medium: $medium-color;
alert: $alert-color;
lightblack: $light-black-color;
black: $black-color;
}
}
/*** SCSS class 將會自動匯出成JsvaScript物件之 className ***/
.container /* 轉譯成 .container */ {
background: red;
color: white;
.hello /* 轉譯成 .container.hello */ {
padding-left: 50px;
}
}
import React from 'react'
import { CssBaseline } from '@material-ui/core'
import { ThemeProvider, createMuiTheme } from '@material-ui/core/styles'
import './App.scss' // 匯入一般的 Sass/SCSS
import globalStyles from 'global.module.scss'; // 匯入 CSS Module
const theme = createMuiTheme({
/* 可在此引入 globalStyles 客製化 Theme */
})
export default function App() {
return (
<ThemeProvider theme={theme}>
<CssBaseline />
<div style={{
color: globalStyles["colors-light"],
backgroundColor: globalStyles["colors-dark"]
}}>
By globalStyle color
</div>
<div className={globalStyles.container}>
<div className={globalStyles.hello} >
Hello SCSS container.hello
</div>
</div>
</ThemeProvider>
)
}
{
"compilerOptions": {
"plugins": [ { "name": "typescript-plugin-css-modules" } ],
"target": "es5",
"lib": [
"dom",
"dom.iterable",
"esnext"
],
"allowJs": true,
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"noFallthroughCasesInSwitch": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react",
"baseUrl": "src"
},
"include": [
"src"
]
}
Last updated