写一个根据屏幕尺寸动态隐藏元素的插件 🧩 - tailwindcss 系列

本篇是结合 tailwindcss 的断点在 CSS 层面隐藏元素。如果需要 JS 层隐藏(即完成移除)见我的另一篇文章:写一个 hook 将自适应断点判断逻辑引入 JS - tailwindcss 系列

问题和背景

在响应式设计中,我们经常会遇到一个元素在移动端隐藏在 PC 端展现,或反之。

我们可以通过 hidden md:xx 来实现,比如将一个 div 元素隐藏可以使用 hidden md:block

tsx 复制代码
<div className="hidden md:block">

如果要将一个 span 隐藏呢?hidden md:inline,以此类推我们将会出现类似的重复结构,出现重复就意味着"坏味道",可能违反了 DRY 原则。

解决 DRY 问题

我们可以通过 tailwind css 的 plugin 帮我们解决。

方式一 addUtilities

addUtilities 适合生成静态固定可枚举的 className。由浅入深我们先添加一个 class:

js 复制代码
const plugin = require('tailwindcss/plugin')

module.exports = plugin(function ({ addUtilities, matchUtilities, theme }) {
  addUtilities({
    '.hidden-mobilex': {
      '@apply hidden': {},
      '@apply md:block': {},
    },
  })
})

以上实现了一个 hidden md:block 的替代品 hidden-mobilex(命名随便取的演示用)。

应用后的效果:

批量添加

改造上述代码一次性添加 4 个:'inline', 'inline-block', 'block', 'flex'

js 复制代码
const plugin = require('tailwindcss/plugin')

/** @typedef {import('react').CSSProperties['display']} IDisplay */

/** @type {IDisplay[]} */
const values = ['inline', 'inline-block', 'block', 'flex']

module.exports = plugin(function ({ addUtilities, matchUtilities, theme }) {
  const batch = values.reduce((acc, display) => {
    return {
      ...acc,
      [`.hidden-mobile-${display}`]: {
        '@apply hidden': {},
        [`@apply md:${display}`]: {},
      },
    }
  }, {})
  // console.log('batch:', batch)
  addUtilities(batch)
})

batch 长这样:

js 复制代码
{
  ".hidden-mobile-inline": {
    "@apply hidden": {},
    "@apply md:inline": {}
  },
  ".hidden-mobile-inline-block": {
    "@apply hidden": {},
    "@apply md:inline-block": {}
  },
  ".hidden-mobile-block": {
    "@apply hidden": {},
    "@apply md:block": {}
  },
  ".hidden-mobile-flex": {
    "@apply hidden": {},
    "@apply md:flex": {}
  }
}

我们加了一个"奇怪"的类型 IDisplay,作用仅为了智能提示,以此也可以管中窥豹看到实际我们的 display 远不止这四个,所以接下来引入我们本文的重点支持任意 display 值。

方式二 matchUtilities

我们能否像 tailwind 的 padding 既支持 px-1/2/3/4 又支持 px-[14px] 这种动态值,这也是 tailwind 的精髓之一。

matchUtilities:适合动态值,其实"能动能静"。

我们要实现的效果是:

  • hidden-mobile-flex 最后生成 hidden md:flex
  • hidden-mobile-[inline-flex] 最后生成 hidden md:inline-flex
js 复制代码
const plugin = require('tailwindcss/plugin')

/** @typedef {import('react').CSSProperties['display']} IDisplay */

/** @type {IDisplay[]} */
const values = ['inline', 'inline-block', 'block', 'flex']

module.exports = plugin(function ({ matchUtilities }) {
  matchUtilities(
    {
      'hidden-mobile': (value) => {
        // console.log('value:', value)

        return {
          '@apply hidden': {},
          [`@apply md:${value}`]: {},
        }
      },
    },
    {
      values,
    },
  )
})

通过 values 在 VSCode 安装 tailwind 插件后,能智能提示 values 中列举的值,同时我们输入的动态值,也能应用(当然此处没有禁止非法值,也无需因为应用后 CSS 不生效)。

应用后的效果:

👆 `hidden-mobile-flex` 最后生成 `hidden md:flex`

👆 `hidden-mobile-inline-flex` 最后生成 `hidden md:inline-flex`

你还可以这么写,即手写 md:xx 部分,实际不推荐,但是这种写法可以帮助我们了解 tailwind 的生成机制。

diff 复制代码
const plugin = require('tailwindcss/plugin')

/** @typedef {import('react').CSSProperties['display']} IDisplay */

/** @type {IDisplay[]} */
const values = ['inline', 'inline-block', 'block', 'flex']

module.exports = plugin(function ({ matchUtilities, theme }) {
  matchUtilities(
    {
      'hidden-mobile': (value) => {
        console.log('value:', value)

        return {
          '@apply hidden': {},
+          [`@media (min-width: ${theme('screens.md')})`]: {
+            display: value,
+          },
        }
      },
    },
    {
      values,
    },
  )
})

这里很巧妙的用到了 ${theme('screens.md') 而非写死 768px,值得大家体会。

改进

暂无

相关推荐
喵个咪19 小时前
Headless 后端实践:基于Go的企业级多栈管理系统脚手架
前端·vue.js·react.js
代码N年归来仍是新手村成员1 天前
【AWS】Lambda 初识与服务部署
javascript·react.js·ai·node.js·云计算·ai编程·aws
大雷神1 天前
HarmonyOS APP<玩转React>开源教程三十一:示例项目下载功能
react.js·开源·harmonyos
大鱼前端1 天前
Veaury:让Vue和React组件在同一应用中共存的神器
前端·vue.js·react.js
五月君_1 天前
继 React、Vue 之后,Three.js 也有 Skills 了!AI 写 3D 终于不“晕”了
javascript·vue.js·人工智能·react.js·3d
小崽崽11 天前
如何实现React 19+Vite+TypeScript技术栈告别高薪主播!从零打造 24 小时“AI 销冠”:星云数字人直播间全链路实战
人工智能·react.js·typescript
光影少年1 天前
Redux Toolkit 用法、解决原生Redux 冗余问题
开发语言·前端·javascript·react.js·中间件·前端框架·ecmascript
whuhewei1 天前
一道React缓存的题目
javascript·react.js
GISer_Jing2 天前
前端沙箱开源项目推荐(React/Next/Vue优先)
前端·react.js·开源
暗不需求2 天前
React 性能优化秘籍:深入理解 `useMemo` 与 `useCallback`
前端·react.js·面试