ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

深入解析 @carbon/grid 的 CSS Grid 架构:非对称 Gutter、Subgrid 与列控制类的设计与测试

深入解析 @carbon/grid 的 CSS Grid 架构:非对称 Gutter、Subgrid 与列控制类的设计与测试 深入解析 carbon/grid 的 CSS Grid 架构非对称 Gutter、Subgrid 与列控制类的设计与测试【免费下载链接】carbonA design system built by IBM项目地址: https://gitcode.com/GitHub_Trending/carbo/carbon本文基于 packages/grid/ARCHITECTURE.md 展开结合carbon/gridCarbon Design System 的网格包IBM 设计语言 IDL Grid 的实现的 Sass 源码、配置与测试系统讲解其 CSS Grid 实现为何舍弃grid-gap、如何以 CSS 自定义属性支撑 wide / narrow / condensed 三种非对称 gutter 模式、如何提供跨断点的列 span / offset / hang / subgrid 工具类以及这套实现对应的测试策略。读完你可以直接在自己的项目中复刻这套网格架构并理解其每个类名背后的生成逻辑。一、设计动机为什么不用grid-gap做 guttercarbon/grid的 CSS Grid 实现使用display: grid但刻意不使用grid-gap、column-gap等属性来产生列间距。原因在 ARCHITECTURE.md 中写得很明确需要实现多种不同的 gutter 模式而每种模式的前导 / 尾随边距是不对称的。三种模式的定义如下wide宽每个列的两侧各保留 16px 边距narrow窄完全去掉前导的 16px 边距只保留尾随边距condensed紧凑列两侧只保留 0.5px 边距。由于这种不对称性实现必须能够逐格per-cell控制前导与尾随 gutter并且两侧都要支持——因为 right-to-leftRTL布局下前导与尾随会互换。同时还需要提供将内容挂hang在网格上 / 挂在某一列上的工具类使得即使某个单元格缺失 gutter文本等元素依然能与网格对齐。二、包结构与入口carbon/grid是一个独立的 Sass 包版本 11.x见 packages/grid/package.json其构建产物与源码结构如下packages/grid/ ├── index.scss # 包的 Sass 入口转发全部模块 ├── scss/ │ ├── _config.scss # 前缀、gutter、断点等全局配置 │ ├── _breakpoint.scss # 断点工具函数与媒体查询 mixin │ ├── _css-grid.scss # CSS Grid 实现核心 │ ├── _flex-grid.scss # 基于 Flexbox 的网格实现 │ └── _mixins.scss ├── docs/sass.md # Sass API 文档 ├── examples/ # css-grid 与 preview 示例 └── __tests__/scss-test.js入口文件 packages/grid/index.scss 只做转发forward scss/config; forward scss/breakpoint; forward scss/css-grid; forward scss/flex-grid;在项目中引入并输出 CSS Grid 样式见 packages/grid/docs/sass.mduse carbon/grid; // 输出 CSS Grid 的全部样式 include grid.css-grid();如果使用的是传统 Flexbox 网格README 中的默认用法packages/grid/README.mduse carbon/grid; // 输出 flex-grid 的样式 include grid.flex-grid();包依赖carbon/layout提供断点基础与单位换算断点默认直接继承自carbon/layout。三、网格配置$grid-breakpoints与 gutter 变量所有关键配置集中在 packages/grid/scss/_config.scss均可通过!default覆盖变量默认值说明$prefixcds所有选择器的命名空间前缀$flex-grid-columns16Flex Grid 的总列数若设为12lg/xlg/max断点会合并覆盖为 12 列$grid-gutter32px (2rem)常规 gutterwide 模式下每侧各 16px$grid-gutter-condensed1pxcondensed 模式下每侧各 0.5px$grid-breakpoints见下表五个断点的列数、边距与最大宽度$grid-breakpoints的默认映射IDL 规范断点列数margin最大宽度sm40320pxmd816px672pxlg1616px1056pxxlg1616px1312pxmax1624px1584px从源码可以看到一个值得注意的细节_config.scss里对$flex-grid-columns 12的情况做了专门处理——通过map.merge把lg/xlg/max的columns覆盖为 12实现从 16 列网格到 12 列网格的平滑切换Bootstrap 风格。这印证了 ARCHITECTURE.md 中网格定义必须匹配 IDL 规范的测试要求断点列数是整个网格系统的契约。四、CSS Grid 核心实现剖析_css-grid.scsspackages/grid/scss/_css-grid.scss 是架构文档的主角。它的设计原则是尽可能少地输出 CSS只要某个断点下的值与上一断点相同就不再重复输出。整体分为几个阶段先输出最小断点sm的无断点样式作为基础其余每个断点包裹在对应的媒体查询中仅在命中时生效在合适的地方输出无条件unconditional选择器例如始终横跨 4 列。4.1:root上的 CSS 自定义属性css-gridmixin 首先在:root上设置三个顶层 CSS 自定义属性--cds-grid-gutter整体 gutter默认 2rem--cds-grid-columns当前断点列数从最小断点开始在:root输出之后仅在变化时按断点覆盖--cds-grid-margin当前断点页边距同样仅在变化时覆盖:root { --cds-grid-gutter: #{$grid-gutter}; each $key, $value in $breakpoints { if is-smallest-breakpoint($key, $breakpoints) { --cds-grid-columns: #{get-column-count($breakpoints, $key)}; --cds-grid-margin: #{get-margin($breakpoints, $key)}; } else { // 与上一断点比较仅当列数/边距变化时才在该断点下输出 $changes: (); // ... 比较 get-column-count / get-margin include breakpoint($key) { each $name, $value in $changes { --cds-#{$name}: #{$value}; } } } } }这套变化才输出的机制直接呼应 ARCHITECTURE.md 中margin 在不同断点间正确变化的测试点例如sm的 margin 是 0md变为 16pxmax变为 24px只有变化点才会生成覆盖规则。4.2 基础网格类.cds--css-grid.#{$prefix}--css-grid { --cds-grid-gutter-start: calc(var(--cds-grid-gutter) / 2); --cds-grid-gutter-end: calc(var(--cds-grid-gutter) / 2); // 单独的 column hang 变量因为 gutter-start 会被动态置 0 --cds-grid-column-hang: calc(var(--cds-grid-gutter) / 2); display: grid; grid-template-columns: repeat(var(--cds-grid-columns), minmax(0, 1fr)); inline-size: 100%; margin-inline: auto; max-inline-size: get-grid-width($breakpoints, largest-breakpoint-name($breakpoints)); padding-inline: var(--cds-grid-margin); }要点轨道数量完全由--cds-grid-columns驱动断点变化只需改变自定义属性max-inline-size取最大断点max1584px的宽度容器居中容器左右内边距使用--cds-grid-margin与断点联动。4.3 列与 gutter 模式非对称的关键列的 margin 使用自定义属性--cds-grid-gutter-start / --cds-grid-gutter-end分别控制两侧.#{$prefix}--css-grid-column { // grid-mode-start/end 记录当前网格设置供 subgrid 反向还原外层 padding --cds-grid-mode-start: var(--cds-grid-gutter-start); --cds-grid-mode-end: var(--cds-grid-gutter-end); margin-inline: var(--cds-grid-gutter-start) var(--cds-grid-gutter-end); [dirrtl] { margin-inline: var(--cds-grid-gutter-end) var(--cds-grid-gutter-start); } }三个 gutter 模式通过覆盖自定义属性实现正是 ARCHITECTURE.md 描述的不对称性// Narrow完全去掉前导 gutter .#{$prefix}--css-grid--narrow { --cds-grid-gutter-start: 0rem; } // Condensedgutter 降为 1pxcolumn-hang 补偿差值 .#{$prefix}--css-grid--condensed { --cds-grid-gutter: #{$grid-gutter-condensed}; --cds-grid-column-hang: #{math.div($grid-gutter, 2) - math.div($grid-gutter-condensed, 2)}; }注意condensed还同步调整了--cds-grid-column-hang因为 gutter 从 32px 降到 1px若要让文字仍与常规网格对齐hang 值需要补上(32-1)/2 15.5px的差。这种以自定义属性为中心的差异化正是为什么不能简单用grid-gap的原因。4.4 行间距Row gapCSS Grid 实现不直接用row-gap属性的通用值而是按 gutter 模式精确匹配总 gutter.#{$prefix}--css-grid--with-row-gap { row-gap: var(--cds-grid-gutter); // wide2rem } // narrow 模式为 0 1rem 1rem 总 gutter .#{$prefix}--css-grid--narrow.#{$prefix}--css-grid--with-row-gap { row-gap: calc(var(--cds-grid-gutter) / 2); }4.5 对齐类.#{$prefix}--css-grid--start { margin-inline-start: 0; } .#{$prefix}--css-grid--end { margin-inline-end: 0; }用于把网格容器对齐到起始或结束方向配合全宽、自定义布局使用。4.6 Subgrid嵌套网格的关键Subgrid 允许在列内再开一层网格且自动与父网格对齐。实现思路是子网格重新声明grid-template-columns: repeat(var(--cds-grid-columns), minmax(0, 1fr))并用负 margin 抵消父级列的 padding.#{$prefix}--subgrid { display: grid; grid-template-columns: repeat(var(--cds-grid-columns), minmax(0, 1fr)); margin-inline: calc(var(--cds-grid-mode-start) * -1) calc(var(--cds-grid-mode-end) * -1); [dirrtl] { margin-inline: calc(var(--cds-grid-mode-end) * -1) calc(var(--cds-grid-mode-start) * -1); } }这就是--cds-grid-mode-start/end存在的意义_css-grid.scss注释明确指出它们用于捕获网格设置使 subgrid 能通过反向还原外层 padding 来正确嵌入父网格。三种 gutter 模式同样有对应的 subgrid 变体cds--subgrid--widegutter-start end 16pxcolumn-hang 0cds--subgrid--narrowgutter-start 0gutter-end 16pxcolumn-hang 16pxcds--subgrid--condensedgutter 变为 1px两侧各 0.5pxcolumn-hang 补偿 15.5px以及对应的行间距变体cds--subgrid--with-row-gapnarrow 为1rem、condensed 为1px。4.7 列 hang让内容对齐回网格当列处于 narrow / condensed 模式导致前导 gutter 缺失或变小时用cds--grid-column-hang把文字拉回与网格线对齐.#{$prefix}--grid-column-hang { margin-inline-start: var(--cds-grid-column-hang); [dirrtl] { margin-inline: initial var(--cds-grid-column-hang); } }ARCHITECTURE.md 的测试清单里专门有一条column hang 辅助类必须正确计算需要补充的 margin使文本对齐到网格。4.8 列 span 类无条件与响应式无条件 span 类通过循环生成cds--col-span-{0..16}for $i from 0 through get-grid-columns($breakpoints) { .#{$prefix}--col-span-#{$i} { include -column-span($i); } }-column-span的语义packages/grid/scss/_css-grid.scss 中的私有 mixinmixin -column-span($i) { if $i 0 { display: none; // span 为 0 表示隐藏内容 } else { --cds-grid-columns: #{$i}; display: block; grid-column: span $i / span $i; } }响应式 span 类cds--{sm,md,lg,xlg,max}:col-span-{0..16}则按断点包裹最小断点不包裹其余包在breakpoint($name)内。注意类名中的冒号需要转义写法.#{$prefix}--#{$name}\:col-span-#{$i}。ARCHITECTURE.md 测试清单中明确列出了这三类 span 断言无条件生效的 span 类按断点条件生效的 span 类span 为 0 时正确隐藏内容对应-column-span(0)的display: none。4.9 百分比 span 类cds--col-span-100等价于grid-column: 1 / -1横跨整行cds--col-span-75 / 50 / 25则按当前断点列数乘以 0.75 / 0.5 / 0.25 得出实际跨度并在断点列数变化时重新计算-percent-column-span会比较前后断点的 span不同才输出。它们同时维护--cds-grid-columns保证嵌套 subgrid 依旧对齐。4.10 列 offset 类start / end无条件 start/end 类的取值刻意避开无意义的边缘值源码注释对此有说明cds--col-start-{1..16}对应网格线 1 到 16不从column-count 1开始因为从网格末端开始意味着列没有可用宽度cds--col-end-{2..17}从 2 开始结束于第 1 条线的列没有宽度到column-count 1网格线从 1 编号另有cds--col-start-auto/cds--col-end-auto与对应的响应式cds--{bp}:col-start-{n}/cds--{bp}:col-end-{n}。完整的 CSS Grid 类清单可查阅 packages/grid/docs/sass.md 的 Classes 表格含cds--css-grid、cds--css-grid-column、cds--subgrid、cds--grid-column-hang等。五、实战用法示例5.1 基础 CSS Grid 布局参考 packages/grid/docs/sass.md 的示例用css-grid()mixin 输出样式后HTML 如下div classcds--css-grid div classcds--css-grid-column cds--col-span-4Span 4 columns/div div classcds--css-grid-column cds--col-span-2Span 2 columns/div /div5.2 跨断点的响应式列控制div classcds--css-grid div classcds--css-grid-column cds--sm:col-span-4 cds--lg:col-span-8 sm 下占 4 列lg 起占 8 列 /div div classcds--css-grid-column cds--sm:col-span-4 cds--lg:col-span-8 同样跨断点变化 /div /div5.3 组合 gutter 模式、hang 与 subgriddiv classcds--css-grid cds--css-grid--narrow div classcds--css-grid-column cds--col-span-8 !-- 前导 gutter 被去掉文字用 hang 对齐回网格线 -- p classcds--grid-column-hangnarrow 网格中的对齐文本/p !-- 内部再嵌套一层 subgrid自动与父网格列对齐 -- div classcds--subgrid cds--subgrid--wide div classcds--css-grid-column cds--col-span-4A/div div classcds--css-grid-column cds--col-span-4B/div /div /div /div这些组合场景正是 ARCHITECTURE.md 所强调的不同 gutter 模式在 subgrid 中应能干净地交互并支持任意层级嵌套。六、测试策略从架构文档到实现验证ARCHITECTURE.md 的 Testing 一节列出该实现需要验证的广泛场景其中许多已落在examples/previewcss-grid preview中文档也明确说明未来应更显式地测试。这些 ad-hoc 用例包括$grid-breakpoints中的网格定义与 IDL 规范一致每个断点的列数正确网格 margin 随断点正确变化三种 gutter 模式wide / narrow / condensed行为正确span 类无条件、按断点条件、span 0 隐藏offset 类无条件与按断点条件百分比 span 类无条件与按断点条件不同 gutter 模式在 subgrid 中干净交互、支持任意层级嵌套column hang 类正确计算补足 margin使文本对齐网格RTL 方向下布局正确对齐全宽网格允许跨越max断点的最大宽度网格类可配合自定义组件对齐到网格。仓库中已有的自动化验证是 packages/grid/tests/scss-test.js它通过carbon/test-utils/scss的SassRenderer编译index.scss断言公开变量集合与两个核心 mixin 存在expect(Object.keys(unwrap(variables))).toMatchSnapshot(); expect(unwrap(mixins)).toEqual({ css-grid: true, flex-grid: true, });也就是说css-grid与flex-grid两个 mixin 及$prefix、$grid-gutter等变量是包的公开 API 契约任何回归都会让快照测试失败。而 preview 示例位于 packages/grid/examples/preview其中src/pages/index.js提供了 flex-grid 的 Auto columns、Column span per breakpoint、Hide column per breakpoint、Offset、Condensed / Narrow / No gutters / Hang、Aspect ratio 等真实渲染案例可作为手工回归 CSS Grid 行为尤其是 gutter 与 hang的参考页面。七、与 Flex Grid 的关系虽然 ARCHITECTURE.md 聚焦 CSS Grid 实现但carbon/grid同时提供基于 Flexbox 的网格见 packages/grid/scss/_flex-grid.scss入口 README 默认介绍的就是它。Flex Grid 提供cds--grid/cds--row/cds--col三个基础类与cds--col-{bp}-{span}、cds--offset-{bp}-{n}、cds--no-gutter、cds--hang--start/end等工具类其实现借鉴了 Bootstrap v4 的网格 mixin 思路源码注释有注明。两类网格共享同一套$grid-breakpoints与 gutter 变量可依据项目需要二选一或同时输出。八、小结carbon/grid的 CSS Grid 实现展示了一个值得借鉴的架构取舍为了支撑不对称的 gutter 模式wide / narrow / condensed与 RTL、hang、subgrid 等复杂交互放弃grid-gap的便捷转而用 CSS 自定义属性--cds-grid-gutter-start/end、--cds-grid-columns、--cds-grid-mode-start/end、--cds-grid-column-hang实现逐格控制 断点联动。其核心价值在于配置驱动所有断点、列数、margin、gutter 均来自$grid-breakpoints等可覆盖变量最小化输出仅在值变化的断点处生成覆盖规则语义清晰的工具类体系无条件 / 响应式 span、offset、百分比 span、hang、subgrid 一应俱全测试与示例并重既有公开 API 快照测试也有覆盖 gutter / hang / 隐藏列等场景的 preview 页面。对于需要自建设计系统网格的团队这份架构文档与其源码实现packages/grid/scss/_css-grid.scss、packages/grid/scss/_config.scss是一份高密度、可复制的参考。【免费下载链接】carbonA design system built by IBM项目地址: https://gitcode.com/GitHub_Trending/carbo/carbon创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表