textarea 样式化 · 一份配方的七条声明
大多数元素在没有作者样式时是「透明」的:一个 <div> 的尺寸完全由内容与容器决定。<textarea> 不是。它自带 cols / rows 两个尺寸属性、一套 13.33px 的 monospace 字体、inline-block 的外部显示类型,以及一个可由用户拖动的 resize 把手。任何一条作者声明落上去,都是在与这些既有取值博弈。
本系列拆解的是一份在社群里流传的 textarea 配方 —— 七条带编号注释的声明,覆盖盒模型还原、容器内铺满、行数尺寸、内容驱动高度与移动端字号下限。每条声明单看都平平无奇,合起来却有严格的先后依赖:box-sizing 的取值决定了 inline-size 该写什么值,inline-size 的缺失会让 field-sizing: content 把控件宽度压塌,而 @supports 的分档决定了旧浏览器里用户还能不能手动拉高输入框。
每页都把当条声明设到真实控件上,用 getBoundingClientRect 与 getComputedStyle 读回浏览器的实际布局结果,正文里出现的每个像素值都出自 Chrome 152 的实测。配方原注释里有两处与实测不符,分别在 行数尺寸 与 @supports 分档 两页写明。
控件的既有形态
作者样式落地之前控件长什么样:cols / rows 给出的初始尺寸、UA stylesheet 给出的字体与盒子类型,以及 display 双值语法在这里的位置。
UA 默认样式与 rows 属性
未写任何 CSS 时,<textarea> 已有 183×36px 的盒子与 13.33px 的字体:尺寸来自 cols / rows 属性,其余来自 UA stylesheet,两层都会被框架 reset 改写。
display 双值语法与基线间隙
配方的第一行可见声明 display: block flow 只做成了一半的事:把外部显示类型从 inline 改成 block,消掉控件底部 6px 的基线间隙。内部显示类型在 <textarea> 上观察不到效果。
盒模型与尺寸
三条彼此牵制的尺寸声明:box-sizing 还原成 content-box、inline-size 用 stretch 铺满容器、min-block-size 用 1lh 把行数写成长度。顺序不能换。
box-sizing 与 reset 的冲突
配方第一条声明把 box-sizing 还原成 content-box。它撤销的不是 UA stylesheet,而是项目自己的 * reset;理由在后一条声明上:只有 content-box 下 5lh 才正好是五行。
inline-size 与百分比宽度
content-box 下 width: 100% 会让控件溢出容器,溢出量恒等于 padding 与 border 之和。stretch 撑满的是 margin box,与盒模型无关;连写前缀别名则是为了 Firefox。
行数尺寸与 lh 单位
1lh 等于元素自身的 used line-height,calc(5 * 1lh) 于是精确地等于五行。配方原注释称此后 rows 属性失效,实测不成立:两者取较大者,让 rows 真正失效的是另一条声明。
内容驱动与移动端
field-sizing: content 让高度跟随内容,连带把 max-block-size 与 resize 拖进同一个 @supports 块;移动端另有一条与缩放行为绑定的字号下限。
@supports 里的三条声明
配方把 max-block-size、resize: unset、field-sizing: content 三条一并关进 @supports。三条本可各自独立生效,一起关进去是因为前两条在没有第三条时要么无意义、要么有害。
16px 字号下限与 iOS 缩放
iOS Safari 聚焦字号低于 16px 的表单控件时会自动放大整页,而 <textarea> 的默认字号恰好是 13.33px。max(16px, 1rem) 取下限而非定值,既避开缩放,也不剥夺用户调大字号的权利。
配方成型
七条声明合并回一份可复制的样式表,并把它开放成一组带 fallback 的自定义属性接口 —— 调用方只改变量,不覆盖声明。
相关链接
规范
-
CSS Form Control Styling · field-sizing
CSSWG
对应「@supports 里的三条声明」页:
fixed与content两个取值的定义,以及它对控件 intrinsic size 的改写。 - CSS Box Sizing · stretch 关键字 CSSWG 对应「inline-size 与百分比宽度」页:stretch 把 margin box 撑满 containing block,与百分比作用于 content box 的区别。
-
CSS Values · lh 与 rlh 单位
CSSWG
对应「行数尺寸」页:
1lh等于元素自身的 used line-height,1rlh取根元素的。 - CSS Display · 双值语法 CSSWG 对应「display 双值语法」页:outer / inner display type 的分离,以及单值写法到双值的等价映射表。
-
HTML Rendering · textarea 的默认渲染
WHATWG
规范建议的 UA stylesheet:
cols/rows如何参与尺寸计算,以及 white-space 与 overflow 的默认取值。
MDN
- field-sizing MDN 取值说明与浏览器支持矩阵,本系列的兼容性断言以其 BCD 为准。
- box-sizing MDN content-box 与 border-box 的度量差异,以及 initial value 是 content-box 这一条。
-
resize
MDN
取值与初始值
none;textarea 的both来自 UA stylesheet 而非属性初始值。 -
<textarea>
MDN
cols/rows/wrap等属性,以及它们与 CSS 尺寸的优先关系。
同站
-
field-sizing content:textarea 一行 CSS 自动长高
同站
HTML 表单系列里
field-sizing的独立页:三种控件上的效果对比与上下界设定,本系列不再重复。 -
尺寸:inline-size 与 block-size
同站
配方通篇用逻辑尺寸而非
width/height,轴向模型见该页。 -
var() 的解析、陷阱与妙用
同站
配方的变量接口建立在
var(--api, fallback)的两段式取值上,fallback 的解析时机见该页。 -
CSS-wide 关键字
同站
unset/initial/revert的分工:配方里的两处unset都取 initial 值,而非回到 UA 样式。