Tailwind CSS 在小程序中的应用

初次接触 Tailwind CSS 还是在 Laravel的一些扩展包中,一上手就觉得是前端的一大利器。特别是现在出了jit功能后,基本所有的样式都可以直接在模板中编写

背景介绍

最近使用 Taro 开发了一个小程序,心血来潮,想在小程序中使用 Tailwind CSS,但 wxss 比较奇葩,不同于 css 的类名规范,这就意味 Tailwind 不太好在小程序中生成样式。

开始填坑

安装

第一步,当然是安装 Tailwind CSS

// 安装 tailwindcss
npm install -D tailwindcss@npm:@tailwindcss/postcss7-compat postcss@^7 autoprefixer@^9
// 生成 tailwindcss 配置文件
npx tailwindcss init

参考 Tailwind CSS 在 PostCSS 7中安装
app.css 中,添加:

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

config/index.js 中启用 Tailwind CSS

...
mini: {
    postcss: {
      tailwindcss: {
        enable: true,
        config: {}
      },
      ...
    }
}

万里长征第一步,到这里,已经完成 Tailwind CSSTaro 中的引入了。

配置

如果你以为安装完成就可以正常使用,那就有点想当然了,你现在编译 npm run dev:weapp,一定是一片红,因为 wxss 不支持类名包含:/* 等符号,而在 Tailwind CSS 框架生成的样式中,大量包含了上述这些符号。所以编译失败。
我们开始修改 Tailwind 默认配置,

  • 禁用部分核心插件(用不上的或会引起报错的):
corePlugins: {
    preflight: false, // 默认样式
    divideColor: false, // 分割边框颜色
    divideOpacity: false, // 分割边框透明度
    divideStyle: false, // 分割边框类型
    divideWidth: false, // 分割边框长度
    space: false, // 间距
    placeholderColor: false, // 占位符颜色
    placeholderOpacity: false, // 占位符透明度
    ringWidth: false, // 阴影相关
    boxShadow: false, // 阴影
    container: false, // 容器布局
    borderColor: false, // 边框颜色(在高版本中,生成了一个带 * 的样式,所以需要禁用)
},
  • separator 配置默认为:, 修改为 _
  • theme 配置中,spacing 会生成百分比的样式,包含了/符号样式,所以需要修改:
const colors = require('tailwindcss/colors')
const plugin = require('tailwindcss/plugin')
...
theme: {
    spacing: {
      px: '1px',
      0: '0',
      '0_5': '0.125rem',
      1: '0.25rem',
      '1_5': '0.375rem',
      2: '0.5rem',
      '2_5': '0.625rem',
      3: '0.75rem',
      '3_5': '0.875rem',
      4: '1rem',
      '4_5': '1.125rem',
      5: '1.25rem',
      6: '1.5rem',
      7: '1.75rem',
      8: '2rem',
      9: '2.25rem',
      10: '2.5rem',
      11: '2.75rem',
      12: '3rem',
      14: '3.5rem',
      16: '4rem',
      18: '4.5rem',
      20: '5rem',
      24: '6rem',
      28: '7rem',
      32: '8rem',
      36: '9rem',
      40: '10rem',
      44: '11rem',
      48: '12rem',
      52: '13rem',
      56: '14rem',
      60: '15rem',
      64: '16rem',
      72: '18rem',
      80: '20rem',
      96: '24rem',
      full: '100%',
    },
    height: (theme) => ({
      auto: 'auto',
      ...theme('spacing'),
      '1_2': '50%',
      '1_3': '33.333333%',
      '2_3': '66.666667%',
      '1_4': '25%',
      '2_4': '50%',
      '3_4': '75%',
      '1__5': '20%',
      '2__5': '40%',
      '3__5': '60%',
      '4__5': '80%',
      '1_6': '16.666667%',
      '2_6': '33.333333%',
      '3_6': '50%',
      '4_6': '66.666667%',
      '5_6': '83.333333%',
      screen: '100vh',
    }),
    inset: (theme, { negative }) => ({
      auto: 'auto',
      ...theme('spacing'),
      ...negative(theme('spacing')),
      '1_2': '50%',
      '1_3': '33.333333%',
      '2_3': '66.666667%',
      '1_4': '25%',
      '2_4': '50%',
      '3_4': '75%',
      '-1_2': '-50%',
      '-1_3': '-33.333333%',
      '-2_3': '-66.666667%',
      '-1_4': '-25%',
      '-2_4': '-50%',
      '-3_4': '-75%',
      '-full': '-100%',
    }),
    translate: (theme, { negative }) => ({
      ...theme('spacing'),
      ...negative(theme('spacing')),
      '1_2': '50%',
      '1_3': '33.333333%',
      '2_3': '66.666667%',
      '1_4': '25%',
      '2_4': '50%',
      '3_4': '75%',
      '-1_2': '-50%',
      '-1_3': '-33.333333%',
      '-2_3': '-66.666667%',
      '-1_4': '-25%',
      '-2_4': '-50%',
      '-3_4': '-75%',
      '-full': '-100%',
    }),
    width: (theme) => ({
      ...theme('height'),
      '1_12': '8.333333%',
      '2_12': '16.666667%',
      '3_12': '25%',
      '4_12': '33.333333%',
      '5_12': '41.666667%',
      '6_12': '50%',
      '7_12': '58.333333%',
      '8_12': '66.666667%',
      '9_12': '75%',
      '10_12': '83.333333%',
      '11_12': '91.666667%',
    }),
    screens: {},
    extend: {}
  },
  • 扫尾工作,在第一步的禁用核心插件中,把边框颜色及阴影禁用掉了,所以只能以插件形式生成样式:
const _ = require('lodash')
...
plugins: [
    plugin(function({ addUtilities, theme }) {
      const flattenColorPalette = (colors) =>
        Object.assign(
          {},
          ...Object.entries(colors).flatMap(([color, values]) =>
            typeof values == 'object'
              ? Object.entries(flattenColorPalette(values)).map(([number, hex]) => ({
                  [color + (number === 'DEFAULT' ? '' : `-${number}`)]: hex,
                }))
              : [{ [`${color}`]: values }]
          )
        )
      const colors = flattenColorPalette(theme('borderColor'))
      const utilities = _.map(colors, (value, key) => {
        return {
          [`.border-${key}`]: {
            borderColor: `${value}`
          }
        }
      })
      addUtilities(utilities)
    }),
    plugin(function({ addUtilities, theme }) {
      const utilities = _.map(theme('boxShadow'), (value, key) => {
        return {
          [key === 'DEFAULT' ? `.shadow` : `.shadow-${key}`]: {
            boxShadow: `${value}`
          }
        }
      })
      addUtilities(utilities)
    }),
]

至此,Tailwind CSS 可完美引入。完整的配置如下:

const _ = require('lodash')
const colors = require('tailwindcss/colors')
const plugin = require('tailwindcss/plugin')

module.exports = {

  darkMode: false,
  corePlugins: {
    preflight: false,
    divideColor: false,
    divideOpacity: false,
    divideStyle: false,
    divideWidth: false,
    space: false,
    placeholderColor: false,
    placeholderOpacity: false,
    ringWidth: false,
    boxShadow: false,
    container: false,
    borderColor: false,
  },
  separator: '_',
  theme: {
    spacing: {
      px: '1px',
      0: '0',
      '0_5': '0.125rem',
      1: '0.25rem',
      '1_5': '0.375rem',
      2: '0.5rem',
      '2_5': '0.625rem',
      3: '0.75rem',
      '3_5': '0.875rem',
      4: '1rem',
      '4_5': '1.125rem',
      5: '1.25rem',
      6: '1.5rem',
      7: '1.75rem',
      8: '2rem',
      9: '2.25rem',
      10: '2.5rem',
      11: '2.75rem',
      12: '3rem',
      14: '3.5rem',
      16: '4rem',
      18: '4.5rem',
      20: '5rem',
      24: '6rem',
      28: '7rem',
      32: '8rem',
      36: '9rem',
      40: '10rem',
      44: '11rem',
      48: '12rem',
      52: '13rem',
      56: '14rem',
      60: '15rem',
      64: '16rem',
      72: '18rem',
      80: '20rem',
      96: '24rem',
      full: '100%',
    },
    height: (theme) => ({
      auto: 'auto',
      ...theme('spacing'),
      '1_2': '50%',
      '1_3': '33.333333%',
      '2_3': '66.666667%',
      '1_4': '25%',
      '2_4': '50%',
      '3_4': '75%',
      '1__5': '20%',
      '2__5': '40%',
      '3__5': '60%',
      '4__5': '80%',
      '1_6': '16.666667%',
      '2_6': '33.333333%',
      '3_6': '50%',
      '4_6': '66.666667%',
      '5_6': '83.333333%',
      screen: '100vh',
    }),
    inset: (theme, { negative }) => ({
      auto: 'auto',
      ...theme('spacing'),
      ...negative(theme('spacing')),
      '1_2': '50%',
      '1_3': '33.333333%',
      '2_3': '66.666667%',
      '1_4': '25%',
      '2_4': '50%',
      '3_4': '75%',
      '-1_2': '-50%',
      '-1_3': '-33.333333%',
      '-2_3': '-66.666667%',
      '-1_4': '-25%',
      '-2_4': '-50%',
      '-3_4': '-75%',
      '-full': '-100%',
    }),
    translate: (theme, { negative }) => ({
      ...theme('spacing'),
      ...negative(theme('spacing')),
      '1_2': '50%',
      '1_3': '33.333333%',
      '2_3': '66.666667%',
      '1_4': '25%',
      '2_4': '50%',
      '3_4': '75%',
      '-1_2': '-50%',
      '-1_3': '-33.333333%',
      '-2_3': '-66.666667%',
      '-1_4': '-25%',
      '-2_4': '-50%',
      '-3_4': '-75%',
      '-full': '-100%',
    }),
    width: (theme) => ({
      ...theme('height'),
      '1_12': '8.333333%',
      '2_12': '16.666667%',
      '3_12': '25%',
      '4_12': '33.333333%',
      '5_12': '41.666667%',
      '6_12': '50%',
      '7_12': '58.333333%',
      '8_12': '66.666667%',
      '9_12': '75%',
      '10_12': '83.333333%',
      '11_12': '91.666667%',
    }),
    screens: {},
    extend: {}
  },
  variants: {},
  plugins: [
    plugin(function({ addUtilities, theme }) {
      const flattenColorPalette = (colors) =>
        Object.assign(
          {},
          ...Object.entries(colors).flatMap(([color, values]) =>
            typeof values == 'object'
              ? Object.entries(flattenColorPalette(values)).map(([number, hex]) => ({
                  [color + (number === 'DEFAULT' ? '' : `-${number}`)]: hex,
                }))
              : [{ [`${color}`]: values }]
          )
        )
      const colors = flattenColorPalette(theme('borderColor'))
      const utilities = _.map(colors, (value, key) => {
        return {
          [`.border-${key}`]: {
            borderColor: `${value}`
          }
        }
      })
      addUtilities(utilities)
    }),
    plugin(function({ addUtilities, theme }) {
      const utilities = _.map(theme('boxShadow'), (value, key) => {
        return {
          [key === 'DEFAULT' ? `.shadow` : `.shadow-${key}`]: {
            boxShadow: `${value}`
          }
        }
      })
      addUtilities(utilities)
    })
  ]
};

最后

我用 Tailwind CSS开发的小程序截图:
截图

本作品采用《CC 协议》,转载必须注明作者和本文链接
本帖由系统于 3个月前 自动加精
《L04 微信小程序从零到发布》
从小程序个人账户申请开始,带你一步步进行开发一个微信小程序,直到提交微信控制台上线发布。
《G01 Go 实战入门》
从零开始带你一步步开发一个 Go 博客项目,让你在最短的时间内学会使用 Go 进行编码。项目结构很大程度上参考了 Laravel。
讨论数量: 10

画图了吗?还是直接开搞 :joy:

3个月前 评论
pigzzz (楼主) 3个月前
MArtian 3个月前
pigzzz (楼主) 3个月前
王小大 (作者) 3个月前

全栈 约等于 全干

3个月前 评论

这个UI爱了

3个月前 评论
pigzzz (楼主) 3个月前

很不错

2个月前 评论
thebestxt

这个ui真的爱了,比我这种直接画卡片的不知高到哪里去了

1周前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!