自定义tailwindcss@3.x的theme

382 阅读5分钟

自定义配置tailwindcsstheme

前言

css原子化应该已经出来很久了,代表的有tailwindcss 还有Unocss,今天我们主要介绍一下tailwindcss的配置。

tailwindcss官网

在开始之前,需要安装一下插件Tailwind CSS IntelliSense,用于代码提示

image.png

安装

# postcss 和 autoprefixer 可选
npm install -D tailwindcss@latest postcss@latest autoprefixer@latest

初始化配置文件

npx tailwindcss init -p

配置文件

// tailwind.config.js
module.exports = {
  content: [
    "./src/**/*.{js,jsx,ts,tsx}",
  ],
  theme: {
    extend: {},
  },
  plugins: [],
}

导入全局样式

// global.css

// 这里导入的是tailwindcss的默认样式和一些预设的样式,可以按照需求导入
@tailwind base;
@tailwind components;
@tailwind utilities;


配置文件详解

主要配置就这几个,当然还有其他配置项,可以阅读官网配置的文档进行一一配置

属性类型描述
contentArray指定哪些文件需要被tailwindcss处理,默认是当前目录下的所有文件,可以指定具体的文件路径,也可以使用glob模式。
themeObject配置tailwindcss的主题,可以扩展默认的主题,也可以覆盖默认的主题。
pluginsArray配置tailwindcss的插件,可以扩展默认的插件,也可以覆盖默认的插件。
prefixString配置tailwindcss的类名前缀,默认是"tw-"
importantBoolean配置tailwindcss的样式是否需要加important,默认是false。

创建 theme.extend 配置项目

其中要自定义配置theme,可以根据官方的extend 配置项去配置自定义的样式,这里可以配置所有的样式,


/** @type {import('tailwindcss').Config} */
module.exports = {
  theme: {
    extend: {
      screens: {
        '3xl': '1600px', // Adds a new `3xl:` screen variant
      },
      fontFamily: {
        display: 'Oswald, ui-serif', // Adds a new `font-display` class
      },
      opacity: {
        '0': '0',
        '20': '0.2',
        '40': '0.4',
        '60': '0.6',
        '80': '0.8',
        '100': '1',
      }
    }
  }
}

但是如果要一个一个去配置,有点麻烦,举个例子吧,在项目中,我们的宽度都是自定义的,1 - 2000 不等,但是官方的配置里面,无法通过 w-2000 这样的方式去赋值,所以我这里采用extend去加载所有需要的样式,这样在书写过程中都可以使用任何1 - 2000内的宽度,其他属性亦是如此。

生成过程分两步

1, 确定属性

确定需要生成的属性,因为不是所有的属性都需要,我们要定义哪些属性是需要去生成的,例如:width,height,margin,padding等,这里给出我的属性

  • key: 属性名
  • max: 属性的最大值
  • min: 属性的最小值
// tailwind.config.json
{
  "tailWindAttrs": [
    {
      "key": "width",
      "max": 2000,
      "min": 2
    },
    {
      "key": "maxWidth",
      "max": 2000,
      "min": 2
    },
    {
      "key": "height",
      "max": 2000,
      "min": 2
    },
    {
      "key": "maxHeight",
      "max": 2000,
      "min": 2
    },
    {
      "key": "borderWidth",
      "max": 100,
      "min": 2
    },
    {
      "key": "padding",
      "max": 1000,
      "min": 0
    },
    {
      "key": "margin",
      "max": 1000,
      "min": 0
    },
    {
      "key": "borderRadius",
      "max": 200,
      "min": 0
    },
    {
      "key": "lineHeight",
      "max": 100,
      "min": 0
    },
    {
      "key": "zIndex",
      "max": 1000,
      "min": 0
    },
    {
      "key": "gap",
      "max": 500,
      "min": 0
    }
  ]
}

2, 创建属性

根据属性,要生成对应的属性值,这里可以写一个脚本函数去生成就好了,无非就是循环生成所有的key-value,然后组装成一个对应的object,下面是我的脚本函数

/**
 * target {number} 要循环多少次,比如 1000,这里要根据 tailwind.config.json 中的 max 值来决定最终的循环次数,在一个循环里面生成所有的 key-value,避免多次循环
 * tailWindAttrs {Record<string, Record<string, string>>} tailwind.config.json 中的tailWindAttrs属性
 */
const getAttrubutes = (target: number) => {
  const obj: Record<string, Record<string, string>> = {}

  // 最外层循环
  for (let i = 0; i < target; i++) {
    // 内层循环,根据tailWindAttrs里面的属性个数,循环生成对应的key-value
    for (let index = 0; index < tailWindAttrs.length; index++) {
      const cur = tailWindAttrs[index]
      if (cur.max > i) {
        if (obj[cur.key]) {
          obj[cur.key] = {
            ...obj[cur.key],
            [i]: i + 'px',
          }
        } else {
          obj[cur.key] = {
            [i]: i + 'px',
          }
        }
      }
      continue
    }
  }
  return obj
}

3,使用



import Colors from 'tailwindcss/colors'
import type { Config } from 'tailwindcss'
const attributes = getAttrubutes(2000)

export default {
  content: [
    './pages/**/*.{js,ts,jsx,tsx,mdx}',
    './components/**/*.{js,ts,jsx,tsx,mdx}',
    './app/**/*.{js,ts,jsx,tsx,mdx}',
  ],
  theme: {
    extend: {
      ...attributes,
      colors: {
        ...Colors,
        ...themeColor,
      },
    },
  },
  plugins: [],
} satisfies Config


到这里就完成了,如果需要更多的属性,只需要在tailWindAttrs数组里面添加对应的属性即可。

4,优化

上面的代码虽然实现了功能,但是每次都需要重新生成所有的属性,每次都需要遍历,这样会导致每次启动项目都会重新生成,这样显然是不合理的,所以我们需要优化一下。

我们可以将生成的属性保存到本地,然后在项目启动的时候读取本地文件,如果本地文件不存在,则重新生成并保存到本地。

创建一个脚本去生成属性,并保存到本地

// tailwindcss.config.ts

import chalk from 'chalk'
import fs from 'node:fs'
import path from 'node:path'
// 导入之前配置的属性
import { tailWindAttrs } from '../theme/tailwind.config.json'
// 要缓存的目标地址:把生成的属性保存到cache下面的tailwindcss目录下面
const CACHE_PATH = '../.cache/tailwindcss'
// 缓存文件名
const FILE_NAME = 'tailwind.tmp.json'
// 缓存的完整路径 ../.cache/tailwindcss/tailwind.tmp.json
const FULL_PATH = path.join(CACHE_PATH, FILE_NAME)

/**
 * 判断缓存是否存在
 */
const isCache = () => {
  const tmp = path.resolve(__dirname, CACHE_PATH, FILE_NAME)
  if (fs.existsSync(tmp)) {
    return true
  }
  return false
}

/**
 * 获取缓存
 */
const getCache = () => {
  return JSON.parse(fs.readFileSync(path.resolve(__dirname, FULL_PATH), 'utf-8'))
}

/**
 * 生成属性
 */
const getAttrubutes = (target: number) => {
  const obj: Record<string, Record<string, string>> = {}
  for (let i = 0; i < target; i++) {
    for (let index = 0; index < tailWindAttrs.length; index++) {
      const cur = tailWindAttrs[index]
      if (cur.max > i) {
        if (obj[cur.key]) {
          obj[cur.key] = {
            ...obj[cur.key],
            [i]: i + 'px',
          }
        } else {
          obj[cur.key] = {
            [i]: i + 'px',
          }
        }
      }
      continue
    }
  }
  return obj
}

/**
 * 设置缓存,生成属性之后,将属性写入缓存文件
 */
const setCache = () => {
  const now = new Date().getTime()
  console.log(chalk.yellow('[ tailwindcss ] no cached, now is generate attrubutes, please wait ...\n'))
  const result = getAttrubutes(2000)
  const cur = new Date().getTime()
  console.log(chalk.cyan(`[ tailwindcss ] attrubutes is created, Total time spent ${(cur - now) / 1000}s \n`))
  // 将result写入到cache文件目录下
  fs.mkdirSync(path.resolve(__dirname, CACHE_PATH), { recursive: true })
  fs.writeFileSync(path.resolve(__dirname, FULL_PATH), JSON.stringify(result), {
    encoding: 'utf-8',
  })
  const ctm = new Date().getTime()
  console.log(
    chalk.green(`[ tailwindcss ] is write ${FILE_NAME} in ${FULL_PATH}, Total time spent ${(ctm - now) / 1000}s\n`)
  )
  return result
}

/**
 * 入口函数,有缓存直接读取缓存,没有缓存则生成属性
 */
export const init = () => {
  if (isCache()) {
    console.log(chalk.green('[ tailwindcss ] is cached 🎉, now is using cache file. \n'))
    return getCache()
  }
  return setCache()
}

export const themeExtend = init()


到这里,基本就全部完成了,这里可以根据需要去决定是否要这样做,如果觉得麻烦,也可以直接使用 tailwindcss 的默认配置,但是这样生成的属性就会比较少。大家可自己权衡决断