Bulma.field表单容器详解:.control职责分离与 field 布局体系的演进
【免费下载链接】bulmaModern CSS framework based on Flexbox项目地址: https://gitcode.com/GitHub_Trending/bu/bulma
Bulma 在 0.6.0 版本中引入.field元素,将表单控件从"既能做块级容器、又能做行内容器"的.control中解耦出来,从而支持横向 help 文本、带图标的 addons、多控件分组加载等更复杂的表单布局。本文基于仓库中的发布文档 New field element,结合当前源码 sass/form/tools.scss,完整讲解这次 API 重构的背景、前后写法对照,以及.field/.field-label/.field-body/.control的现行实现与可定制变量。
背景:.control的双重身份与它的代价
从 Bulma 早期版本开始,.control一直是表单控件的"万能容器",一个元素同时承担了六类职责:
- 让控件在垂直方向上拉开间距;
- 在 input、button、textarea 旁边附加图标;
- 为控件追加 loading 旋转器;
- 创建横向表单(horizontal form);
- 创建control addons(连体控件,如搜索框 + 按钮);
- 创建control groups(控件分组)。
问题在于它同时扮演了两种容器角色:
| 角色 | 用途 | 典型修饰类 |
|---|---|---|
| 块级(block)容器 | 垂直间距、横向表单中的行容器 | 块级边距行为 |
| 行内(inline)容器 | 图标、loader、addon、grouping | .has-icon、.is-loading |
这种混合导致三个做不到的场景(这也是.field出现的直接动因):
- 无法在横向表单中放 help 文本—— help 文本需要与
.control同级而不是嵌套其中; - 无法在一组控件里同时挂多个图标或 loader—— 图标/loader 的修饰类只能挂在单个
.control上; - 无法给 addons 中的每个控件挂不同图标—— addon 组整体共享一个
.control上下文。
核心变化:.field承接 block 角色,.control回归纯行内
发布文档给出的结论只有一句话:
TL;DR: there's a new
.fieldcontainer, and.controlhas been re-purposed.
具体分工调整为:
.field成为.control的 block 容器,并继承了原先挂在.control上的布局修饰类:.has-addons、.is-grouped、.is-horizontal;.control只允许包含一个.button、.input、.select或.textarea,外加(至多)一个.icon;它不能再包含.help元素,也不能再嵌套其他.control。
类结构前后对照(摘自发布文档):
// Before .control .has-addons .has-icon .is-grouped .is-horizontal .is-loading // After .control .has-icon .is-loading .field .has-addons .is-grouped .is-horizontal .field-label .field-bodyHTML 写法的前后对照(以"用户名可用"提示为例):
<!-- Before --> <label class="label">Username</label> <p class="control has-icon has-icon-right"> <input class="input is-success" type="text" placeholder="Text input" value="bulma"> <span class="icon is-small"> <i class="fas fa-check"></i> </span> <span class="help is-success">This username is available</span> </p> <!-- After --> <div class="field"> <label class="label">Username</label> <p class="control has-icon has-icon-right"> <input class="input is-success" type="text" placeholder="Text input" value="bulma"> <span class="icon is-small"> <i class="fas fa-check"></i> </span> </p> <p class="help is-success">This username is available</p> </div>结构上的关键差别:.label与.help从.control内部提升为.field的直接子元素,与.control平级。这正是"help 可以横向排布"得以成立的原因——它们与控件同处一个 flex/block 容器层级。
注意:当前仓库源码中图标修饰类已改名为
has-icons-left/has-icons-right(见 sass/form/tools.scss 中.control.has-icons-left, &.has-icons-right的选择器),发布文档里的has-icon/has-icon-right是 0.6.0 时期的历史写法,在后续版本中被弃用。
.field的现行实现:三个布局修饰类
发布文档中.field只列出了"继承了哪些修饰类",而当前源码 sass/form/tools.scss 则展示了这些修饰类的完整实现。
.field基础样式:继承 block 行为
.field { @include cv.register-vars( ( "block-spacing": #{cv.getVar("field-block-spacing")}, ) ); @extend %block; ... }从源码结构看,.field通过@extend %block复用全局 block 的垂直间距行为(%block定义在 sass/utilities/extends.scss),并把自己专属的块间距变量--bulma-block-spacing指向 CSS 变量field-block-spacing。这个变量由 SASS 变量$field-block-spacing注册,默认值0.75rem,同样定义在 sass/form/tools.scss:
$field-block-spacing: 0.75rem !default;.field之间的垂直节奏由它统一控制,这正是"垂直拉开控件间距"这一原.control职责的落点。
.has-addons:连体控件
&.has-addons { display: flex; justify-content: flex-start; .control { &:not(:last-child) { margin-inline-end: -1px; // 边框重叠,视觉上连成一体 } &:not(:first-child):not(:last-child) { .button, .input, .select select { border-radius: 0; // 中间段无圆角 } } &:first-child:not(:only-child) { /* 只保留左侧圆角 */ } &:last-child:not(:only-child) { /* 只保留右侧圆角 */ } &.is-expanded { flex-grow: 1; flex-shrink: 1; } } &.has-addons-centered { justify-content: center; } &.has-addons-right { justify-content: flex-end; } &.has-addons-fullwidth { .control { flex-grow: 1; flex-shrink: 0; } } }要点:addons 通过负外边距让相邻控件边框重叠、对非首尾控件清零圆角来实现"连体"外观;每个子控件用.control.is-expanded声明弹性扩展;交互状态(hover/focus/active)通过z-index: 2/3/4分层,保证聚焦控件的 focus 光圈不被相邻控件压住。
.is-grouped:带间距的控件组
&.is-grouped { display: flex; gap: 0.75rem; justify-content: flex-start; & > .control { flex-shrink: 0; &.is-expanded { flex-grow: 1; flex-shrink: 1; } } &.is-grouped-centered { justify-content: center; } &.is-grouped-right { justify-content: flex-end; } &.is-grouped-multiline { flex-wrap: wrap; } }与 addons 的区别在于用gap: 0.75rem保留控件间距(不连体),并提供居中/靠右/多行三个变体。每个.control默认flex-shrink: 0不被压缩,仅.is-expanded的控件参与伸缩。
.is-horizontal与.field-label/.field-body
&.is-horizontal { @include mx.tablet { display: flex; // 仅 tablet 及以上生效 } }.is-horizontal在移动端保持块级堆叠、在 tablet 断点以上才切换为 flex 横排,是响应式表单的基础。与之配套的两个子容器:
.field-label:tablet 以上flex-grow: 1、text-align: right、右侧1.5rem间距,并支持is-small / is-normal / is-medium / is-large四档字号(每档带padding-top: 0.375em与输入框顶部对齐);移动端仅保留0.5rem的下边距。.field-body:tablet 以上flex-grow: 5(label:body 形成 1:5 的列比例),内部多个.field之间margin-inline-end: 0.75rem,非.is-narrow的子 field 自动flex-grow: 1平分剩余宽度。
这一 label:body = 1:5 的弹性比例,是从源码中可以直接验证的布局细节。
.control的现行实现:纯行内职责
重构后的.control在 sass/form/tools.scss 中只剩与"单个控件内部装饰"相关的逻辑:
.control { box-sizing: border-box; clear: both; font-size: cv.getVar("size-normal"); position: relative; // 为图标/loader 的绝对定位提供参照 text-align: inherit; }- 图标定位:
.has-icons-left/.has-icons-right下,.icon以绝对定位覆盖在输入框上(height/width均取input-height,z-index: 4),输入框则按input-height预留左右 padding;input 的 hover/focus 通过兄弟选择器& ~ .icon联动图标颜色。 - loading 旋转器:
.is-loading用::after伪元素@extend %loader渲染,定位于控件右上角内(inset-inline-end: 0.75em; top: 0.75em),并随is-small / is-medium / is-large调整字号——因为.control现在只包一个控件,spinner 不再与其他控件互相干扰,这正是发布文档"解决方案"一节中"允许多个控件各自挂 loader"的落地形态。 - 控件度量基线:
.control本身的高度、行高、内边距来自 sass/utilities/controls.scss 的$control-height: 2.5em、$control-line-height: 1.5、$control-padding-vertical / -horizontal等变量,统一注册为--bulma-control-*CSS 变量,供 input、select、button、file 等所有控件形态共用。
%controlplaceholder 在 sass/utilities/extends.scss 中定义,被各输入形态@extend,保证同一控件在不同上下文中度量一致。
文档中的两个典型示例
发布文档给出了两个最能体现新结构的场景。
示例一:addons 组内每个控件各自带图标/状态
<div class="field is-grouped"> <p class="control is-expanded has-icon"> <input class="input is-success" type="text" placeholder="Username" value="alexsmith"> <span class="icon is-small"> <i class="fas fa-check"></i> </span> </p> <p class="control is-expanded has-icon"> <input class="input is-warning" type="email" placeholder="Email" value="alex@smith.com"> <span class="icon is-small"> <i class="fas fa-exclamation-triangle"></i> </span> </p> <p class="control is-expanded is-loading"> <input class="input" type="email" placeholder="Name"> </p> </div>三个控件分处三个独立.control,各自携带不同的图标或 loading 状态——这在旧结构(一个.control包整组)中是做不到的。
示例二:横向表单 + help 文本
<div class="field is-horizontal"> <div class="field-label"> <label class="label">From</label> </div> <div class="field-body"> <div class="field is-grouped"> <p class="control is-expanded"> <input class="input" type="text" placeholder="Name"> </p> </div> <div class="field"> <p class="control is-expanded has-icon has-icon-right"> <input class="input is-success" type="email" placeholder="Email" value="alex@smith.com"> <span class="icon is-small"> <i class="fas fa-check"></i> </span> </p> <p class="help is-success">This email is correct</p> </div> </div> </div>嵌套关系值得注意:外层.field.is-horizontal用.field-label+.field-body分成左右两栏,.field-body内部再嵌套两个.field(一个is-grouped的 Name 组、一个 Email 字段),help 文本位于内层.field中、与.control平级——这是.help能正常换行排布的前提。该示例与现行文档 Input 表单页面 中.input.is-static横向表单的示例结构完全一致,说明这一写法在后续版本中持续沿用。
.label与.help:field 的配套元素
.field重构的完整体验还包括两个文字元素,定义同见 sass/form/tools.scss:
.label:display: block,颜色取text-strong($label-color),字重$label-weight默认weight-semibold,非末位时margin-bottom为$label-spacing(默认0.5em),支持is-small / is-medium / is-large字号变体。.help:display: block,字号$help-size默认size-small,margin-top: 0.25rem;通过@each遍历$label-colors(即完整调色板)自动生成is-{color}变体,与.input.is-success、.input.is-warning等状态色一一对应。
这些变量($label-color、$label-spacing、$label-weight、$help-size、$field-block-spacing)都带!default,可以在自定义 SASS 中覆盖;$field-block-spacing对应的 CSS 变量为--bulma-field-block-spacing(见 CHANGELOG.md 中 v1 的变量迁移记录),即项目已同时支持 SASS 变量与 CSS 变量两条定制路径。
版本演进脉络
结合 CHANGELOG.md 可以还原这条 API 线的完整演进:
| 版本 | 变化 |
|---|---|
| 0.5.x | .control.is-grouped使用.control元素作为直接子元素(BREAKING,见 0.6.0 记录) |
| 0.6.0(2017-03) | 新增.field元素;.control重新定义职责—— 即本文发布文档对应版本 |
| 0.7.2 | .control.has-icon弃用,改名为.control.has-icons |
| 1.x | 表单样式全面接入 CSS 变量($field-block-spacing→--bulma-field-block-spacing等),.field继续@extend %block |
从 CHANGELOG 到当前sass/form/tools.scss的选择器可以验证:0.6.0 确立的".field管 block 布局、.control管单控件装饰"这一分层在 1.x 中完全没有被推翻,只经历了命名(has-icon→has-icons-left/right)与变量机制(SASS → CSS 变量)的渐进式迁移。
小结
.field的引入是 Bulma 表单系统一次典型的"单一职责"重构:
.field:block 容器,负责垂直间距与三大布局修饰类(.has-addons/.is-grouped/.is-horizontal),并新增.field-label、.field-body支撑横向表单的响应式 1:5 布局;.control:行内容器,只包裹一个.button/.input/.select/.textarea与可选.icon,负责图标定位(has-icons-left/right)与 loading 旋转器(is-loading);.label/.help:作为.field的直接子元素与.control平级,获得横向排布能力,并与全调色板状态色联动。
对使用方的实际意义:从旧版迁移时,把原先"一个.control包一切"的结构拆开——label、control、help提升为.field的三个兄弟节点;图标与 loading 修饰类下沉到具体控件的.control上;布局修饰类上提到.field上,即可获得发布文档所述"更复杂的表单设计"能力,且与现行 1.x 源码结构完全兼容。
【免费下载链接】bulmaModern CSS framework based on Flexbox项目地址: https://gitcode.com/GitHub_Trending/bu/bulma
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考