從去年起tailwind在台灣熱度越來越高,另外最近也在HISKIO上買了tailwind的課程學習,所以想說也來蹭一下分享一下所學順便兼做筆記方便日後複習
tailwindcss是一個utility-first的css框架,而且由於tailwind將絕大部分的css樣式通通寫成class使用(ex:mt、flex、grid…),所以可以讓工程師在寫html的時候一併處理樣式而幾乎不用寫額外的css,跟bootstrap之間最大的不同是tailwind沒有像是「Accordion」、「Dropdown」之類的組件,全部都是功能性的class,另外由於使用tailwind等同於直接在html上寫style,所以可以避免樣式幾乎相同卻硬是要命名不同的class,大大減少多餘的css
註:想要玩一下tailwind的話可以先到tailwindplay嚐嚐鮮
tailwind官方有提供三種配置方式
<link href="https://unpkg.com/tailwindcss@^2/dist/tailwind.min.css" rel="stylesheet">
不過官方不推薦使用這個方式,主要是因為以下原因
個人認為最要命的是最後一點,雖然如果沒有要客製化的話直接使用也不是不行,但是tailwind沒做purge的話總共有3MB的大小,丟到vscode看將近有20萬行的css原始碼,這麼大包會出事的阿…
使用以下指定建立tailwind.css (-o為output)
npx tailwindcss -o tailwind.css
要讓自己寫的css跟tailwind一起編譯的話要先將tailwind的基礎樣式用@tailwind引入至自定義的css檔案內
/* ./src/index.css */
@tailwind base; /* 基礎樣式 */
@tailwind components; /* 模組樣式 */
@tailwind utilities; /* 共用樣式 */
@layer base { ... }
@layer components { ... }
再使用以下指令匯出css( -i為input、-o為output)
npx tailwindcss -i ./src/index.css -o ./dist/tailwind.css
可透過tailwind.config.js對tailwind本身進行設定,用以下指令建立tailwind.config.js
npx tailwindcss init
tailwind.config.js
module.exports = {
purge: [],
darkMode: false, // 也可以是 'media' 或 'class'
theme: {
extend: {},
},
variants: {},
plugins: [],
}
使用tailwind cli時config檔會自動被讀取,另外如果要以production mode建置的話要在指令前面加上「NODE_ENV=production」這個環境變數
NODE_ENV=production npx tailwindcss -i ./src/tailwind.css -o ./dist/tailwind.css --minify
註:–minify會把css檔案內的間距、空白移除掉,進一步壓縮css檔案的大小
註:windows作業系統並不自帶NODE_ENV,使用windows的話記得要額外下載
透過Postcss來安裝tailwind,這個方式還可以透過webpack等打包工具整合其他第三方插件來協助開發
透過npm載入tailwindcss、postcss、autoprefixer
npm install -D tailwindcss@latest postcss@latest autoprefixer@latest
postcss是一個加強處理css的js plugin,包括依照Can i use添加瀏覽器前綴、使用新的css功能或是做圖片的處理,我們透過postcss整合tailwind並使用autoprefixer去添加瀏覽器前綴
我本身是使用webpack打包,所以為了要讓webpack能夠讀取解析postcss需要載入postcss loader
npm install -D postcss-loader
新增postcss.config.js、tailwind.config.js、webpack.config.js
postcss.config.js
module.exports = {
plugins: [
require('tailwindcss'),
require('autoprefixer')
]
}
tailwind.config.js
module.exports = {
purge: [],
darkMode: false, // 也可以是 'media' 或 'class'
theme: {
extend: {},
},
variants: {},
plugins: [],
}
跟tailwind cli一樣把tailwind的樣式引入到自訂義的index.css內
/* ./src/index.css */
@tailwind base; /* 基礎樣式 */
@tailwind components; /* 模組樣式 */
@tailwind utilities; /* 共用樣式 */
@layer base { ... }
@layer components { ... }
webpack.config.js配置
const path = require('path');
module.exports = {
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'index.js',
},
module: {
rules: [
{
test: /\.css$/,
use: ['style-loader', 'postcss-loader'],
},...
]
}
}
註: 記得把index.css給import進index.js
如果是用vscode開發可以安裝這個插件,在寫class時會出現提示
tailwind會把沒使用到的樣式移除掉來減少產生的css檔案大小,但是tailwind並不知道有哪些檔案內有使用到tailwind的class,所以如果有檔案使用tailwind的話要在purge內加上content的物件,這樣tailwind就會透過content內的路徑去看這些路徑內的檔案並把檔案內有使用到的class保留下來
module.exports = {
purge: {
content: ['./src/**/*.html', './src/**/*.{html,js}']
}
}
有無purge的差別真的滿大的…
雖然tailwind很貼心的把沒有使用到的css移除掉但是這會衍伸一個問題,如果今天html是透過後台編輯器產生的話就有可能使用到被移除掉的class,為了避免這個問題這時候我們可以在config檔內加上safelist,tailwind在產生css時會把safelist內的class保留下來
module.exports = {
purge: {
content: ['./src/**/*.html', './src/**/*.{html,js}'],
safelist: [
'bg-blue-500',
'text-center',
'hover:opacity-100',
}
}
補充一下雖然tailwind第三版還沒有正式釋出不過第三版purge被移除掉了,content跟safelist直接寫就可以了
module.exports = {
content: ['./src/**/*.html', './src/**/*.{html,js}'],
safelist: [
'bg-blue-500',
'text-center',
'hover:opacity-100',
]
}
base裡面主要是寫基礎的樣式,如h1的size以及margin之類的
@layer base {
h1{
font-size: 20px;
margin: 1rem 0;
}
}
可能會有人覺得使用tailwind會讓class變得又臭又長,這時候我們可以透過tailwind的components、@apply將多個class群組起來
@layer components {
.card{
@apply rounded-lg mt-4 px-4 py-3 bg-fb-card;
}
}
建議只將需要共用的class群組起來就好了
module.exports = {
darkMode: 'false', //參數可選填media or class
}
dark mode就是所謂的夜間模式,接受「media」、「class」的字串參數,media會以電腦系統的設定來轉換夜間模式而class則是使用手動的方式轉換夜間模式 ,在tailwindplay試一下會發現tailwind就是在html上添加dark的class
@layer base {
body {
@apply dark:bg-black;
}
}
需要在夜間模式顯示的樣式可以在html標籤內或是在自定義的index.css裡的base裡面寫「dark: + utility class」
手動模式要透過js的click事件在html上添加dark的class
var light_mode_btn = document.getElementById('light')
var dark_mode_btn = document.getElementById('dark')
light_mode_btn.addEventListener('click', function () {
document.documentElement.classList.remove('dark')
})
dark_mode_btn.addEventListener('click', function () {
document.documentElement.classList.add('dark')
})
variant又有人稱為偽類變體,所謂的偽類就是css裡的「:hover」、「:active」、「:focus」等…可以添加互動功能的pseudo-class,在tailwind裡面可以直接在html上寫上variant來增加頁面的互動效果,寫法是「variant: + utility class」
<input class="focus:bg-white hover:bg-black focus:border-white hover:border-black">
在tailwindcss推出JIT之後只要JIT mode有開啟那麼所有class的variant都可以直接使用,但如果使用的是舊版本的tailwind(v1.96以前)又或是新版本的tailwindcss但是沒開啟JIT mode的話就要注意不是所有class的variant預設都是開啟的,可以從Configuring Variants裡面去看tailwind有支援的variant跟預設有開啟的variant
我們要添加自定義的樣式需要在tailwind.config.js裡面預先寫,記得一定要寫在extend裡面不然會把原先tailwind裡相對應的utility class整個洗掉…
module.exports = {
theme: {
extend: {
screens: {},
spacing:{},
colors: {},
},
}
}
自定義的範圍很廣,可以試RWD斷點(screens)、顏色(colors)、間距(spacing)…,相關的填寫方式直接看官網會比較快,不過每次要自定義樣式就要在tailwind.config.js裡面預先寫其實是有點麻煩…,這時候tailwindcss在2.1版後推出了just in time的功能
最後補充一下本身在使用webpack + tailwindcss時除了tailwind.config.js以外安裝的套件以及配置
package.json
{
"name": "tailwindProject",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"dev": "cross-env NODE_ENV=development webpack serve --open",
"build": "cross-env NODE_ENV=production webpack",
},
"author": "",
"license": "ISC",
"devDependencies": {
"@babel/core": "^7.14.6",// 將js分析成ast,方便各個插件分析語法處理
"@babel/preset-env": "^7.14.5", // 將尚未被大部分瀏覽器支援的js語法轉換成能被瀏覽器支援的語法,以及讓較舊的瀏覽器也能支援大部分瀏覽器能支援的語法
"autoprefixer": "^10.2.6", // 給css添加瀏覽器前綴字
"babel-loader": "^8.2.2", // 使webpack可以讀懂babel
"clean-webpack-plugin": "^4.0.0-alpha.0", // 打包前先將output資料夾清空
"compression-webpack-plugin": "^8.0.0", //將檔案進行壓縮產生壓縮檔
"copy-webpack-plugin": "^9.0.0",// 將src內特定資料夾的檔案複製到dist內的特定資料夾(名稱可自定義也可指定多組)
"cross-env": "^7.0.3", // windows系統不自帶cross-env環境變數時安裝
"css-loader": "^5.2.6", // 使webpack可以讀懂css
"cssnano": "^5.0.8", // 移除css內的空白
"html-webpack-plugin": "^5.3.1", // 將html輸出至dist資料夾內並在html內引入js檔案
"mini-css-extract-plugin": "^1.6.0", // 將css抽離成單獨的css檔案並在html內引入css檔案
"postcss": "^8.3.5", // 加強處理css,包括添加前綴、將新語法轉換成通用的舊語法
"postcss-loader": "^6.1.0", // 使webpack可以讀懂postcss
"tailwindcss": "^2.2.4", // tailwindcss
"webpack": "^5.39.1", //webpack打包工具
"webpack-cli": "^4.7.2", // webpack cli工具,提供操作webpack相關的cli指令
"webpack-dev-server": "^3.11.2" // 透過webpack開啟本地server
}
}
module.exports = {
plugins: [
require('tailwindcss'),
require('autoprefixer'),
require('cssnano')({
preset: 'default',
}),
]
}
babel.config.json
{
"presets": ["@babel/preset-env"]
}
明確標示要支援的瀏覽器給如autoprefixer、Babel等等…
last 2 versions
not dead
> 0.2%
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const CopyPlugin = require("copy-webpack-plugin");
const CompressionPlugin = require("compression-webpack-plugin");
const webpack = require('webpack')
const path = require('path');
module.exports = {
target: 'web',
entry: {
index: './src/index.js', //多個entry可以以物件形式輸入
...
},
mode: process.env.NODE_ENV, // 如果是production mode會使用tree shaking,不會打包沒使用到的東西
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].[contenthash].js', //name會去對應到entry裡物件的key值,hash為避免修改網頁時的快取
},
devServer: {
compress: true,
port: 8080
},
// loader
module: {
rules: [
{
test: /\.css$/i,
use: [
MiniCssExtractPlugin.loader,
{
loader: 'css-loader',
options: {
importLoaders: 1, //如果在css檔案內使用@import要將importLoaders設為1,如果是sass則為2
}
},
{
loader: 'postcss-loader'
}
],
},
{
test: /\.(jpe?g|png|gif)$/,
// webpack5透過asset module來使用資源而不需載入loader,
type: 'asset/resource' // 將檔案輸出至output
},
{
test: /\.m?js$/,
exclude: /node_modules/, //babel處理時忽略node_modules
use: {
loader: "babel-loader",
}
}
],
},
// 插件
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html'
}),
new MiniCssExtractPlugin({
filename: '[name].[contenthash].css'
}),
new CleanWebpackPlugin(),
new CopyPlugin({
patterns: [
{ from: "./static", to: "./static" },
],
}),
new webpack.DefinePlugin({ // 在全域環境注入設定的變數,如果開發跟測試使用的config檔不同可以使用
// Definitions...
PRODUCTION: JSON.stringify(false),
VERSION: JSON.stringify('5fa3b9'),
BROWSER_SUPPORTS_HTML5: true
}),
new CompressionPlugin()
],
devtool: 'source-map' // source-map參數會在console端顯示原始的程式碼而非打包過後的程式碼方便debug
}