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 协议》,转载必须注明作者和本文链接
本帖由系统于 2年前 自动加精
《L05 电商实战》
从零开发一个电商项目,功能包括电商后台、商品 & SKU 管理、购物车、订单管理、支付宝支付、微信支付、订单退款流程、优惠券等
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
讨论数量: 11

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

2年前 评论
pigzzz (楼主) 2年前
MArtian 2年前
pigzzz (楼主) 2年前
王小大 (作者) 2年前

这个UI爱了

2年前 评论
pigzzz (楼主) 2年前
thebestxt

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

2年前 评论

你好,请问哪些苹果小图标是如何设计和绘制的呢?望回复。如果能写一篇相关的文章就最好了,感谢

1年前 评论

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