
本文从一个小小的需求扩展到更灵活可读性更好的解决方案。写法总数从 <math xmlns="http://www.w3.org/1998/Math/MathML"> O ( 2 N ) → O ( 1 ) O(2N) \rightarrow O(1) </math>O(2N)→O(1) 🎉。
🤕 问题
tailwind css 推崇移动端优先原则:
移动端优先
Tailwind 采用移动优先的断点体系,与 Bootstrap 等框架类似。
即:无前缀的类(如
uppercase
)在所有屏幕尺寸生效;带前缀的类(如md:uppercase
)仅在对应断点及以上尺寸生效。
也就是说默认情况我们的样式是针对移动端的,如果需要适配宽屏需要增加前缀 md:
或 lg:
等,看起来挺好的,但是在隐藏元素方面存在一些不方便。
假如元素想在移动端隐藏,但是其他屏幕展示,按照此原则我们应该怎么写呢?
- block 元素如
<div>, <p>, <h1>-<h6>, <ul>, <li>
:hidden md:block
- inline 元素如
<span>, <a>, <strong>, <em>, <img>
:hidden md:inline
- inline-block 元素如
<button>, <input>, <select>, <textarea>
:hidden md:inline-block
- ...
如果应用了 flex 或 grid
- flex:
hidden md:flex
、hidden md:inline-flex
- grid:
hidden md:grid
、hidden md:inline-grid
- ...
上面只是列举了一部分,出现了两个问题,DRY 以及 display 的值难以简单决定。
问题一:DRY - 插件解决
重复的模式代码 hidden md:block
hidden md:inline
hidden md:inline-block
......。
要是能直接写一个 class 就好了,比如 hidden-mobile-block
hidden-mobile-inline
hidden-mobile-inline-block
......(如果类名长你可以缩写,但是为了可读性不建议)。
可以通过自定义 tailwind 插件解决 ,思路就是通过插件生成一系列固定前缀的 class hidden-mobile-xxx
,优点可以智能提示以及支持任意值(hidden-mobile-[table]
),具体可看我的这篇文章:写一个根据屏幕尺寸动态隐藏元素的插件 🧩 - tailwindcss 系列
问题二:display 变化多端
第二个问题 display
属性实在太多,其次我们不仅要记住 div
是 block
元素,span
是 inline
,button
是 inline-block
,而且如果 display 被修改成其他布局比如 flex 那就得看应用后的最终样式(你可以在 Chrome 的 devtool 的 style computed 模块看到),即我们需要知道当前元素的 display 值才能使用,要是能自动识别就好。
问题二解法:智能识别?
根据元素最终布局自动精准生成 display
?
即我们连 hidden-mobile-block
hidden-mobile-inline
hidden-mobile-inline-block
都无需写,只需统一写 hidden-mobile
。
比如 span
上应用 class hidden-mobile
自动生成 hidden-mobile-inline
,div 则 block
......。但 tailwind 插件还没有如此强大并不能"反射"拿到被其附着的元素,而且有时候拿到元素不一定就能判断其 display
,因为父元素或者自己本身的 className
也会影响自己,这就需要运行时动态计算了,并非插件能力范围(假设可以拿到附着的元素,可以将其在 Node.js 无头浏览器渲染或者在实际 DOM 渲染完毕后动态添加样式,但是性能会有很大影响,前者影响编译时后者影响运行时)。
小结:无法智能识别
🌟 更好的解法:通过 tailwind 的 max-md:hidden
逆向思维 >=
到 <
。通过 max-md:hidden
来达到我们目的,< 768px
则 hidden,编译后:
css
@media not all and (min-width: 768px) {
.max-md:hidden {
display: none;
}
}
代码解释:min-width: 768px
即 >= 768
取反 not all
就变成 < 768px
。
为什么 tailwind 要这么编译?不就等价于
max-width: 767px
吗?注意是 768-1 否则就不等价了。
- 不能使用已有断点,需要做减法。
not
语法意图更明确,即取反>=
直接变成<
。
写法对比:
tsx
# 原始写法 O(2N)
<div className="hidden md:block">只适合宽屏显示的内容<div/>
# 自定义插件写法 O(N)
<div className="mobile-hidden-block">只适合宽屏显示的内容<div/>
# 范围写法 O(1)
<div className="max-md:hidden">只适合宽屏显示的内容<div/>
写法总数从 <math xmlns="http://www.w3.org/1998/Math/MathML"> O ( 2 N ) → O ( 1 ) O(2N) \rightarrow O(1) </math>O(2N)→O(1) 🎉:

看看前后的对比,你就能感受到它的简洁,只需一个类名无需写 display
,没有性能影响。
还有问题吗?
如果你"吹毛求疵" max-md:hidden
仍然还存在两个问题,可读性不好和临界值问题。你第一眼能看出下面的代码是在移动端显示还是隐藏吗?
html
<div className="max-md:hidden">...<div/>
我们还是可以通过插件自定义可读性更好的类名:
js
// @ts-check
// tailwind-plugins\hidden-on-mobile.js
const plugin = require('tailwindcss/plugin')
module.exports = plugin(function ({ addUtilities }) {
const lessThanMdHidden = {
// < 768 隐藏
'@apply max-md:hidden': {},
}
addUtilities({
// 768 认为是宽屏,非移动端
'.show-on-mobile': {
// >= 768 隐藏
'@apply md:hidden': {},
},
'.hide-on-mobile': lessThanMdHidden,
'.show-on-tablet-and-up': lessThanMdHidden,
'.show-on-not-mobile': lessThanMdHidden,
})
})
现在好多了,我们做了两个重命名,以及一些 alias,现在可读性是不是变好了?
html
<div className="show-on-mobile">只适合移动端显示的内容<div/>
<div className="show-on-tablet-and-up">只适合宽屏显示的内容<div/>
<div className="show-on-not-mobile">只适合宽屏显示的内容<div/>
<div className="hide-on-mobile">只适合宽屏显示的内容<div/>
第二,临界值问题是指,假设我们认为 768px 需要和移动端保持一致风格 ,那么 md:hidden
和 max-md:hidden
结合使用时在临界值 768px 是会出现问题。
html
<div className="md:hidden">只适合移动端显示的内容<div/>
<div className="max-md:hidden">只适合宽屏显示的内容<div/>
如果尺寸刚好 等于 md 768px
页面会如何展示?将展示只适合宽屏显示的内容
,即并没有按照我们的预期。 这里我们需要重写我们的插件,而且只能使用 media query:
js
// tailwind-plugins\hidden-on-mobile.js
// @ts-check
const plugin = require('tailwindcss/plugin')
module.exports = plugin(function ({ theme, addUtilities }) {
/** @type {`${number}px`} */
const mdBreakpoint = theme('screens.md')
// console.log('mdBreakpoint:', mdBreakpoint) // 768px
const breakpoint769 = `${parseInt(mdBreakpoint) + 1}px` // 769px
addUtilities({
// 768 认为是移动端
// >= 769 隐藏
'.show-on-mobile': {
[`@media (min-width: ${breakpoint769})`]: {
display: 'none',
},
},
// <= 768 隐藏
'.hide-on-mobile': {
[`@media (max-width: ${mdBreakpoint})`]: {
display: 'none',
},
},
})
})
🎯 总结
移动端隐藏其他屏幕展现若使用 hidden md:block
这个基于 tailwind 移动端优先的设计理念的写法,我们首先通过插件解决了要写多个类名问题(写法从 <math xmlns="http://www.w3.org/1998/Math/MathML"> O ( 2 N ) → O ( N ) O(2N) \rightarrow O(N) </math>O(2N)→O(N)),但我们仍然需要基于当前元素在展示状态下的 display 值来写 md:xxx
的后半部分,易出错,我们逆向思维通过 max-md:hidden
进一步统一写法,写法从 <math xmlns="http://www.w3.org/1998/Math/MathML"> O ( N ) → O ( 1 ) O(N) \rightarrow O(1) </math>O(N)→O(1),最后我们继续优化通过插件重命名解决了可读性问题!
html
# 原始写法 O(2N) ↓
<div className="hidden md:block">只适合宽屏显示的内容<div/>
# 自定义插件写法 O(N) ↓
<div className="mobile-hidden-block">只适合宽屏显示的内容<div/>
# 范围写法 O(1) ↓
<div className="max-md:hidden">只适合宽屏显示的内容<div/>
# 可读性更好的写法 O(1) ↓
<div className="show-on-tablet-and-up">只适合宽屏显示的内容<div/>