點燈坊

失くすものさえない今が強くなるチャンスよ

使用 @apply 抽出共用 Style

Sam Xiao's Avatar 2020-12-13

很多人對於 Tailwind CSS 誤解在於 Utility 都寫在 HTML,很難重複使用 Style,Tailwind CSS 另外提供了 @apply directive 專門處理此問題。

Version

Tailwind CSS 2.1.1

Button

<template>
  <button>
    Submit
  </button>
</template>

在沒有使用 Tailwind CSS 下,在 HTML 僅使用簡單 <button> 就可建立 button。

button000

因為 browser 已經為 <button> 提供很多預設 property。

Tailwind Button

button001

在 Tailwind 下若使用簡單 <button>,會發現 browser 下已經不像 button,因為 Tailwind 內建 CSS Reset 了,<button> 已經不具備預設 property。

<template>
  <button class="bg-gray-200 hover:bg-gray-300 border-2 rounded border-solid border-gray-600 px-3 py-1 m-2">
    Submit
  </button>
</template>

若要做出與 browser 預設 button 相同外觀,可自行加入適當 utility:

  • bg-gray-200: 設定 button 背景顏色
  • hover: bg-gray-300:當 hover 時的 button 背景顏色
  • border-2: 設定 border width
  • rounded:設定 border 具有圓角
  • border-solid:設定 border 有實體線
  • border-gray-600:設定 border 顏色
  • px-3 py-1:設定 padding
  • m-2:設定 margin

button002

Real World Button

button003

實務上幾乎不會使用 browser 的預設 button,而會自行採用 CSS 加以 style。

<template>
  <button class="bg-blue-500 hover:bg-blue-700 text-white rounded px-3 py-1 m-2">
    Submit
  </button>
</template>
  • bg-blue-500:設定背景顏色
  • hover: bg-blue-700:當 hover 時的背景顏色
  • text-white:設定文字顏色
  • rounded:設定 border 具有圓角
  • px-3 py-1:設定 padding
  • m-2:設定 margin

Local Component

<template>
  <button class="btn">
    Submit
  </button>
</template>

<style scoped>
.btn {
  @apply bg-blue-500 hover:bg-blue-700 text-white rounded px-3 py-1 m-2
}
</style>

若 style 在 component 內多處使用,也可抽成 local component。

第 8 行

.btn {
  @apply bg-blue-500 hover:bg-blue-700 text-white rounded px-3 py-1 m-2
}

使用 @apply directive 將原本在 HTML 的 class 全部搬到 btn class。

第 2 行

<button class="btn">
  Submit
</button>

如此 <button> 只要套用 btn class 即可。

Global Component

assets/css/tailwind.css

@tailwind base;
@tailwind components;
@tailwind utilities;

@layer components {
  .btn {
    @apply bg-blue-500 hover:bg-blue-700 text-white rounded px-3 py-1 m-2
  }
}

若 style 在多處都使用,也可抽成 global component。

  • tailwind.css 建立 btn global component,如此所有 HTML 都可直接使用。

  • 由於 btn 屬於 component,所以寫在 @layer components 下。

App.vue

<template>
  <button class="btn">
    Submit
  </button>
</template>

如此 HTML 可直接使用 btn class。

Component Plugin

tailwind.config.js

let plugin = require('tailwindcss/plugin')

let buttonPlugin = ({ addComponents }) => {
  let style = {
    '.btn': {
      borderRadius: '0.25rem',
      paddingTop: '0.5rem',
      paddingBottom: '0.5rem',
      paddingLeft: '1rem',
      paddingRight: '1rem',
      backgroundColor: '#4299c1',
      color: '#fff',
      '&:hover': {
        backgroundColor: '#2b6cb0',
      }
    },
  }

  addComponents(style)
}

module.exports = {
  purge: [
    './src/**/*.html',
    './src/**/*.vue',
  ],
  theme: {
    extend: {},
  },
  plugins: [
    plugin(buttonPlugin)
  ],
}

若 style 在多 project 都使用,也可抽成 component plugin。

第 1 行

let plugin = require('tailwindcss/plugin')

引用 Tailwind 所提供的 plugin()

第 3 行

let buttonPlugin = ({ addComponents }) => {

建立 buttonPlugin(),從 argument destructure 出 addComponents(),將用此 function 建立 component。

第 4 行

let style = {
  '.btn': {
    borderRadius: '0.25rem',
    paddingTop: '0.5rem',
    paddingBottom: '0.5rem',
    paddingLeft: '1rem',
    paddingRight: '1rem',
    backgroundColor: '#4299c1',
    color: '#fff',
    '&:hover': {
      backgroundColor: '#2b6cb0',
    }
  },
}

btn utility 以 CSS-in-JS 形式寫在 style object 內。

目前 plugin 不能使用 @apply,因此只能寫 pure CSS

19 行

addComponents(style)

使用 addComponents() 寫入新 style。

30 行

plugins: [
  plugin(buttonPlugin)
],

plugins 內使用 plugin() 掛入 buttonPlugin()

Conclusion

  • 使用 Tailwind 時甚少在 <style> 定義 class,除非在 component 內共用 style,此時會使用 @apply 將共用 utility 抽出 local component

  • 若要在各 component 共用 style,可在 tailwind.css 使用 @apply 抽成 global component

  • 若要在各 project 共用 style,可抽成 component plugin,但目前在 plugin 內只能搭配 pure CSS,而不能使用 @apply,最後以 NPM package 形式共用

  • 在抽成 component 時,也必須遵守 Functional CSS 精神,將 CSS 拆成最小單位,以利 Function Composition

Reference

Tailwind CSS, Extracting Components