尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

CSS编程实践:从自定义属性到容器查询的工程化之路

CSS编程实践:从自定义属性到容器查询的工程化之路 1. CSS 编程从“调样式”到“写逻辑”的转变1.1 为什么我觉得 CSS 已经进入了“编程”阶段很多前端开发者对 CSS 的认知还停留在“改改颜色、调调间距”的阶段觉得它不过是一堆属性和值的罗列谈不上什么编程。但如果你最近两三年打开过真实项目的样式表或者维护过一套被多人修改过的设计系统看法大概率会变。CSS 早就不是“选择器加声明块”那么简单了变量、函数、逻辑判断、状态管理、数学计算、甚至是原生的嵌套写法这些能力叠加在一起之后它的复杂度已经和一门小型编程语言相当。我见过不少团队工程化做得不错组件抽象也清晰但样式这边始终是一团乱麻。上千行的样式文件里充斥着重复的色值、凭空出现的魔数、互相覆盖的类名改一个按钮颜色要在五个文件里翻找。问题的根源不在于开发者不努力而在于大家没有用“编程”的思维来对待 CSS。所谓 CSS 编程就是把它当成一套有变量、有逻辑、有状态、有作用域规则的体系来设计和维护。这篇文章我想分享的就是我自己在做项目过程中沉淀下来的一套 CSS 编程方法论从核心能力的使用到布局选型的决策逻辑再到工程化组织和排障经验。不保证解决所有问题但至少能让你在写样式的时候少走几条弯路。1.2 CSS 编程和传统编程的相同与不同先说相同点。CSS 有变量自定义属性有函数calc、clamp、min、max有条件逻辑媒体查询、容器查询、选择器状态有组合和继承机制甚至还有类似于“接口”和“实现”的设计模式用类名和行为钩子做抽象。你可以像写代码一样去复用、去抽象、去分层。再说不同点。CSS 最特殊的地方在于它是声明式的、随文档结构展开的、并且天生依赖全局环境。你写一段 JS 逻辑出错通常是逻辑错误而写一段 CSS出错往往是因为某些隐式规则——层叠、继承、优先级、格式化上下文——和你以为的不一样。所以 CSS 编程的核心挑战不是“写出来”而是“控制全局影响”。理解了这一层你在做技术选型和写样式的时候就会发现很多坑其实是可以提前避开的。接下来我从最基础的变量与计算开始讲这些能力是整个 CSS 编程体系的基石。2. 现代 CSS 的核心能力变量、计算与函数2.1 自定义属性CSS 世界的“变量”自定义属性也叫 CSS 变量语法上是用--开头的属性名定义的。定义在哪个选择器上哪个选择器及其后代元素就可以通过var()引用它这就形成了天然的作用域。我第一次真正感受到它的威力是在重构一套主题系统的时候。原来项目里的主题色写死了十几个类似的色值深蓝、浅蓝、深一点的蓝、带透明度的蓝……每次改品牌色都要全局搜索替换。用自定义属性以后我只需要在根选择器上定义一组基础 token所有组件统一引用。比如:root { --color-brand: #2b6cb0; --color-brand-hover: #2c5282; --space-base: 8px; --radius-md: 6px; } .button { background: var(--color-brand); padding: var(--space-base) calc(var(--space-base) * 2); border-radius: var(--radius-md); }这不仅是“换个写法”那么简单。自定义属性带来的真正价值有三个第一个是单一数据源。一个值只定义一次引用处全部同步彻底告别“全局搜索替换颜色”的噩梦。第二个是运行时可变。自定义属性不参与层叠优先级计算它可以在运行时被 JS 或者内联样式直接修改而无需重写整个样式表。这意味着主题切换、暗色模式、用户偏好调整都可以在不触碰 JS 逻辑的前提下完成。比如这样.card { background: var(--surface-color); } [data-themedark] .card { --surface-color: #1a202c; }第三个是组合能力。变量可以参与计算可以嵌套引用可以组成类似“设计 token 映射”的体系。比如间距体系可以用基础单位衍生出多个级别这样调度起来非常规整。使用自定义属性有两个需要特别注意的地方。一是在keyframes动画中关键帧内部无法引用外层选择器上定义的变量需要在关键帧块内部重新定义或者用普通属性替代。二是自定义属性是继承属性容易在深层组件里无意中被覆盖命名时要给变量加上组件前缀。比如组件dialog内部的变量就叫--dialog-width别叫--width。2.2 calc() 与 clamp()让 CSS 具备运算能力CSS 的计算能力在过去几年提升得很快现在你可以在样式表里做单位混合运算。calc()最经典的使用场景就是“撑满减去某个固定宽度”。比如侧边栏场景.main-area { width: calc(100% - 260px); margin-left: 260px; }这种写法比 flex 布局还要精准尤其是在组件内部需要严格对齐某个设计稿时非常有用。calc()支持加减乘除参与运算的两边至少有一边是用了变量的否则没有意义。要注意的是加法和减法两侧必须有一个是数字无单位的值或长度值乘法在浏览器里支持还不稳定一般不推荐生产环境使用。clamp()是min()和max()的升级版它一次能限制一个值的上下边界。最典型的应用是流式字号h2 { font-size: clamp(1.5rem, 0.5rem 3vw, 2.5rem); }意思是最小 1.5rem随着视口宽度线性增长但封顶 2.5rem。这在响应式排版里非常好用少写两段媒体查询。clamp()还可以用在间距和宽度上比如卡片宽度width: clamp(280px, 50%, 480px)效果是窄屏不挤、宽屏不散。实用经验是不要滥用clamp()。如果只是“窗口变窄时缩小一点”直接用一个百分比或者 flex 的basis往往更简单。clamp()适合那种有明确上下限约束、并且中间过程需要平缓过渡的场景。另外clamp()的三参数里中间那一个千万别写成固定值否则整个函数就退化成常量了。2.3 CSS 函数与内置逻辑近两年 CSS 还在不断引入更复杂的内置函数比如min()、max()、attr()、env()以及更高级的颜色函数color-mix()、light-dark()。这些函数让 CSS 具备了更强的表达力。举一个我常用的例子color-mix()可以在不改动原色值的情况下快速生成衍生色。比如给按钮做 hover 色.button:hover { background: color-mix(in srgb, var(--color-brand), #000 10%); }意思是把品牌色往黑色方向混合 10%。这样整个系统的 hover 状态就都是“同一个色相、统一加深 10%”完全由主题统一派生不用再各个组件手动维护一套 hover 色。env()能读取设备环境变量比如 iOS 的 safe-area-inset-top用来处理刘海屏和底部安全区非常可靠.fixed-bottom-bar { padding-bottom: env(safe-area-inset-bottom, 20px); }这些函数的本质是“把重复的逻辑封装成表达式”这正是编程语言里函数存在的意义。当你开始像组织代码那样组织这些函数调用和变量引用时CSS 的维护成本会急剧下降。3. 布局系统的编程思维Flexbox 与 Grid 的搭配决策3.1 Flexbox 的真实定位一维排列Flexbox 是我每天都在用的布局方案但对它的定位我要先说得直白一点——它是一个一维布局系统。说它一维是因为它擅长处理“一行内几个元素怎么排”或者“一列内纵向怎么排”的问题。我见过一些项目把 Flexbox 当成万能布局工具什么界面都用 flex 套结果嵌套层级深得离谱样式难调、间距难控、性能也受影响。Flexbox 最漂亮的用法是处理排列和分配而不是画网格骨架。比如让子元素水平居中并保持间距.toolbar { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }gap属性在 Flexbox 中已经全面支持不需要再用 margin 模拟间距了。flex-wrap: wrap使得子项在空间不足时自动换行在处理标签列表或动态长度的内容时特别省心。再比如经典的侧边栏加内容的弹性布局.page { display: flex; } .sidebar { flex: 0 0 260px; } .content { flex: 1 1 auto; min-width: 0; }这里min-width: 0是关键后面我会在排障部分详细展开。Flexbox 的编程思维在于它处理的是“分配比例”和“排列顺序”而不是像表格那样把内容按行列硬塞。3.2 Grid这才是真正的二维布局系统如果说 Flexbox 是“一排萝卜排排坐”Grid 就是“一张地图上自由画格子”。Grid 是真正的二维布局系统它能同时控制行和列这是它与 Flexbox 最根本的区别。Grid 的核心概念是“轨道”。你可以用像素、百分比、分数单位和auto来定义行和列。最常用的单位是fr它表示可用空间的一份。比如.dashboard { display: grid; grid-template-columns: 1fr 2fr 1fr; gap: 24px; }这表示三列宽度比为 1:2:1间距 24px。比用 flex 加百分比更简洁也更稳定。Grid 命名的grid-template-areas是我特别推崇的功能它能把整体布局“可视化”.app-shell { display: grid; grid-template-columns: 240px 1fr; grid-template-rows: 64px 1fr; grid-template-areas: header header sidebar main; } .header { grid-area: header; } .sidebar { grid-area: sidebar; } .main { grid-area: main; }这一下就把整个页面骨架说清楚了。哪块是头、哪块是侧边栏、哪块是主体一目了然。这种方式的好处是布局结构调整时只需要改动grid-template-areas的映射关系不需要去改每个元素的位置属性。真正的“布局逻辑集中管理”。Grid 的子元素还能用span关键字跨行跨列.featured-card { grid-column: span 2; }这种表达很直观。你不需要去计算第几行到第几行只需要说“这个元素跨两列”整体可读性非常高。3.3 选型逻辑什么时候用 Flexbox什么时候用 Grid这个问题我在团队里被问过无数次。我给出的判断标准非常简单如果布局是“一行/一列内排列多个元素”用 Flexbox。如果布局是“同时控制多行多列形成完整网格”用 Grid。如果两者皆可优先用约束更自然的那一个。比如导航栏里的按钮组用 flex页面主体区域的卡片墙用 grid。还有一个实用判断法想象自己在纸上画线。如果只需要画一条线来规划位置就是一维用 flex如果需要画横竖两方向的线才能确定区域就是二维用 grid。这个类比我在给新人讲布局时屡试不爽。不建议的做法是在 Grid 容器上再包一层 Flexbox 容器或者 Flexbox 嵌套 Grid 嵌套 Flexbox 再嵌套 Grid。嵌套太深不仅可读性差而且查找 bug 的时候特别要命。一般组件内部处理自身内容用 flex 就够了页面级别的骨架交给 grid各自管好自己那一层。4. 响应式编程从媒体查询到容器查询4.1 媒体查询的局限性媒体查询是响应式设计的元老级工具到目前为止兼容性也最好。不过我越用越觉得media有个天然的缺陷——它以视口为参照而不是以组件为参照。什么意思呢一个卡片组件如果它出现在页面宽度的半栏里视口大了它也跟着大如果出现在三栏小屏里它可能缩得很小。用media去适配它本质上是在猜测这组件在某个视口宽度下到底有多大。组件一旦换个容器摆放原来的媒体查询可能就完全失效了。这就是媒体查询的“全局视角”带来的问题。过去解决这个问题的方式是把组件响应式拆成多个版本或者干脆让组件内部不设断点全部用百分比和弹性布局去“自适应”。这能用但不够优雅也不够灵活。4.2 容器查询解决的真问题容器查询container解决的正是这个痛点。它的思路是让组件根据“最近容器”的尺寸去做响应而不是根据视口。某个容器声明为查询容器之后里面的元素就可以按容器的宽度来应用样式。看看这个例子.card-list { container-type: inline-size; } container (min-width: 400px) { .card { display: flex; flex-direction: row; } }当.card-list的宽度超过 400px 时里面的卡片自动切换为横向布局。这个组件放在侧边栏它就在侧边栏宽度下适配放在主内容区就在主内容区宽度下适配。同一个组件在不同容器里呈现出完全一致又完全适配的表现。容器查询用得好的项目响应式代码可以削减一半以上。因为组件不再关心“整个屏幕多宽”只关心“我所在的区域多宽”区域宽度有一套组件就在所有场景下保持一致。这在设计系统里尤其值钱卡片在列表页、详情页、弹窗里复用时体验是统一的。使用容器查询有一个坑是容器本身必须把container-type声明好否则内部的container查询无效。container-type: inline-size会阻止容器尺寸受内容影响如果容器内部有长文本设置本身也要注意必要时把overflow放到内容层处理。4.3 响应式策略的编程化重组把媒体查询和容器查询放在一起我更倾向于这样组织响应式策略页面级框架布局侧边栏、顶栏、主区用media处理因为它们才真正跟视口强相关。组件内部细节卡片方向、表格列显隐、字体尺寸用container处理因为它们跟容器强相关。组件需要多尺寸档位时用自定义属性作为“档位开关”比如.card { --row-direction: column; } container (min-width: 400px) { .card { --row-direction: row; } }这样组件里的其他规则可以引用这个变量来统一调整细节不需要在每个断点里重复列出所有属性。这个做法类似代码里的“配置常量”思想让响应式逻辑变得可理解、可维护。5. 状态管理CSS 如何管理“状态”5.1 类名与数据属性的状态表达前端世界里状态往往被当作 JS 的专利。但实际上CSS 是天然的“状态机”描述语言——它本身就在根据不同的条件切换样式。最基础的表达方式就是类名.button.is-active { border-color: var(--color-brand); box-shadow: 0 0 0 3px color-mix(in srgb, var(--color-brand), transparent 70%); }用.is-active这类状态类名来表达“这个按钮处于激活状态”是 CSS 编程里最基本也最稳定的一种状态管理模式。它的优势是状态表达直接、JS 控制简单只需要classList.add或classList.remove而且对搜索引擎和无障碍工具都很友好。除了类名我更推荐用>[data-stateloading] .spinner { display: block; } [data-statesuccess] .success-icon { display: block; }这种写法比is-loading、is-success这样一长串类名更清楚。尤其是当你有很多互斥状态时>div classaccordion-item style--open: 1然后 CSS 里这样写.accordion-body { --target-height: calc(var(--open) * 999px); max-height: var(--target-height); overflow: hidden; transition: max-height 0.3s ease; }当--open为 1 时max-height计算成一个足够大的值面板展开为 0 时直接变成 0。整个展开逻辑没有一行 JS。这个手法我用过很多次代价是max-height那段计算有点“魔法”需要写注释但效果确实很平滑。另一个典型用法是主题状态。把暗色模式作为一个布尔变量挂在根节点上html>.card { background: light-dark(#ffffff, #1e293b); color: light-dark(#0f172a, #e2e8f0); }在根节点声明color-scheme: light dark;后light-dark()会根据系统的亮暗偏好自动取值。这让主题系统变得空前简洁。5.3 用结构选择器减少 JS 干预CSS 里最被低估的状态表达方式是结构选择器。很多状态根本不需要 JS 参与——它们天然由 DOM 结构决定。比如“列表为空”的情况可以直接用:empty.tip-list:empty::after { content: 暂无数据; color: var(--text-muted); }再比如表单的必填校验状态可以用:required和:valid、:invalidinput:invalid .error-message { display: block; } input:valid .error-message { display: none; }这样表单校验的样式反馈完全由浏览器决定JS 只需要专注于真正的业务逻辑UI 反馈让 CSS 自己接管。我见过有项目为这种提示写了几十行 JS 控制显隐实际上 CSS 原生就支持。结构选择器的编程思路是把“状态源”放在 DOM 本身CSS 只负责根据结构分派样式。这能显著减少 JS 与 CSS 的耦合也给状态管理提供了更可靠的基础。6. 动画逻辑时间轴、插值与缓动的工程化6.1 关键帧的“状态机”思路CSS 动画的关键帧本质上是一个状态机——定义起始状态、结束状态和中间过程。写动画与写业务逻辑不同它要求你对“中间帧”有直觉这是 CSS 编程里颇具技术含量的部分。一个标准的 fade-in 动画是这样写的keyframes fadeInUp { from { opacity: 0; transform: translateY(12px); } to { opacity: 1; transform: translateY(0); } } .animate-in { animation: fadeInUp 0.4s ease-out both; }看似简单但有几个细节非常关键。animation-fill-mode: both表示动画开始前和结束后都把关键帧的状态应用到元素上。没有它动画开始前元素会暴露初始状态结束后又弹回原始位置视觉抖动的体验很难看。多个关键帧百分比可以模拟更复杂的运动keyframes pop { 0% { transform: scale(0.6); opacity: 0; } 70% { transform: scale(1.05); opacity: 1; } 100% { transform: scale(1); } }这种“回弹”效果比线性缩放更生动。设计动画时我的思路是先确定关键节点的表现再通过中间帧制造“弹性”和“重量感”。6.2 贝塞尔曲线与缓动函数缓动函数决定了动画“在不同时间点的速度变化”。大多数时候使用预置的ease、ease-in、ease-out就够用但想要精确控制动画节奏时就得用cubic-bezier()。它的四个参数定义了一个三维贝塞尔曲线的两个控制点体现速度曲线。我一般用这几个值cubic-bezier(0.34, 1.56, 0.64, 1)带轻微回弹的进入效果适合卡片弹出。cubic-bezier(0.16, 1, 0.3, 1)先快后慢、干净利落的退出效果适合弹窗关闭。cubic-bezier(0.4, 0, 0.2, 1)Material Design 标准曲线适合大多数界面动效。用缓动要注意同一动画的进入与退出应该使用对称的曲线视觉上才顺畅。比如进入的一段用ease-out退出的反向动画就用ease-in而不是两端都用同一个函数。6.3 动画暂停、反向与播放控制的工程实现有些人觉得 CSS 动画一执行就控制不了其实不是。animation-play-state可以让动画暂停和继续.card:hover .play-icon { animation-play-state: paused; }更实用的场景是“倒计时动画控制”。比如一个需要 5 秒完成的进度条动画用户点击暂停再点击继续用 CSS 本身就可以办到。我常用这个来实现加载状态的“悬停暂停”提升交互感。animation-direction可以控制动画方向reverse表示反向运行alternate表示来回往返。做呼吸灯或者循环漂浮效果用alternate比写两个关键帧更省事keyframes breathe { from { transform: scale(1); } to { transform: scale(1.05); } } .breathe { animation: breathe 2s ease-in-out infinite alternate; }工程化的一点建议是动画时长和缓动函数尽量集中管理用自定义属性或者类名统一控制比如.dur-fast、.dur-slow。用户对动画时长非常敏感太快觉得突兀太慢觉得拖沓。统一的时长体系能让整套界面的节奏感保持一致这是动效质量的重要一环。7. 工程化实践CSS 项目的组织与可维护性7.1 命名规范BEM 的实际价值很多项目样式失控最直接的原因就是类名体系混乱。BEMBlock Element Modifier是我目前觉得最适合 CSS 编程实践的命名方案因为它强制你把“组件结构”和“状态变化”写进类名里代码的可读性因此大幅提升。看一个实际的 BEM 例子.card {} .card__title {} .card__content {} .card--compact {}Block 是组件名Element 是内部子元素Modifier 是变体。这样一眼就能看出来.card__title是属于.card的.card--compact是这个组件的紧凑款。BEM 的价值还体现在作用域隔离上。没有 BEM 之前一个.title类名可能出现在十个组件里改样式时到处是惊喜。有了 BEM每个组件有独立的命名空间样式覆盖的问题会少很多。当然BEM 也有副作用——类名长、HTML 繁琐。这是取舍问题。我的态度是组件库的公共样式和长期维护的业务系统用 BEM 值得一次性原型或简单的静态页面用简洁类名更快不必教条化。7.2 CSS 与设计系统Token 的基本架构设计系统中的 token 本质上是“跨层级的变量声明”。它分三级原始值十六进制色值、具体字号、间距数值。语义 token--color-primary、--font-heading映射到设计意图。组件 token--button-bg、--dialog-radius绑定到具体组件。我在项目里通常维护一份tokens.css专门放原始值和语义 token组件样式引用这些语义 token。比如按钮组件不直接引用一个十六进制颜色而是引用var(--color-primary)。这样品牌色一旦变化不需要改按钮组件——只改 token 就行。这样一个 token 体系的好处是UI 一致性不再是口号而是有结构保证的。新来的人看样式代码首先读 tokens 文件就能理解整套设计语言。7.3 性能优化代价最小收益最大的几个动作CSS 性能不如 JS 那样容易定量但有一些铁律值得记住。一是避免深层嵌套比如body .main .content .item .link { }。选择器匹配是自右向左的深层嵌套会显著增加判定次数但现代浏览器优化得很好深度嵌套的唯一真实后果是代码可读性差——这反而是我更要劝退的原因。写 CSS 时尽量保持选择器平铺且语义明确。二是避免大量通配符选择器。* { box-sizing: border-box; }这种重置是值得的但业务代码里尽量别用*做样式选择匹配成本高且不易追踪。三是对长列表项使用content-visibility: auto。这个属性可以让屏幕外的内容跳过渲染。我用的一个场景是超长表格渲染速度提升非常明显table tbody tr { content-visibility: auto; contain-intrinsic-size: 0 48px; }contain-intrinsic-size提供占位高度避免滚动条跳动。这是近两年性价比极高的一项性能优化。域名拆分的 CDN、压缩合并这类传统优化已经很成熟了不再展开。我把这些工作当作“日常编码习惯”而不是“优化专项”因为从工程角度看预防的性能问题远比修复多。8. 排障心得我踩过的一些真实坑8.1 层叠上下文看不见的覆盖问题我曾经花了一个下午排查一个问题一个弹窗的遮罩层明明是最后一个 DOM 节点却被页面主体挡住了。查了半天发现是主体区域有transform: translateZ(0)——它建立了新的层叠上下文把整个主体的层叠顺序提前了。这类问题的本质是只要某个元素建立了层叠上下文比如有transform、opacity、filter、z-index配合定位它在全局层叠顺序中的权重就变了。解决方式是给弹窗容器设置一个足够高的z-index并且这层关系要在组件注释里写清楚避免后来者踩坑。排查层叠问题的技巧也很直接在开发者工具里逐个给元素添加z-index和position组合快速定位是谁盖住了谁。看到transform或者filter属性时就要小心它很可能是层叠上下文失控的元凶。8.2 百分比高度失效的根源另一个常见问题是子元素height: 50%没效果。原因是父元素没有明确高度百分比高度无法解析。大多数时候我们需要的是让容器高度由内容撑开这时候百分比计算就没有意义。一种解法是使用 flex 的align-items: stretch让子元素跟随容器拉伸这是更自然的做法。另一种是用绝对定位 inset 精确占位。但最贴近“编程思维”的做法是检查整个高度链——谁定义高度、谁撑开高度、谁依赖百分比——把高度关系画成一条链链路里每一环都清楚问题自解。8.3 flex 子项的 min-width 陷阱flex 子项默认的min-width是auto这意味着内容太多时子项不会小于其内容宽度。这导致一个很隐蔽的 bug一个 flex 容器里的两个子项明明设置了flex: 1 1 0右边那个的内容却把整个容器撑破。解决办法是给子项加min-width: 0.flex-item { flex: 1 1 0; min-width: 0; }这个属性我几乎在同场景下每次都要加与其说它是例外不如说它是 flex 布局的标准配置。理解了min-width: auto的语义之后这个问题就不再是玄学而是必然结果。8.4 margin 合并外边距合并是 CSS 里另一个老生常谈的坑。两个相邻块级元素的上下 margin 不会相加而是取较大值。第一反应是“这不符合自然认知”但 CSS 就是这样的规则。解决方案是用 flex 或 grid 的gap代替 margin这也是我坚持使用gap的原因之一。另外父元素和第一个子元素之间的 margin 会“逃逸”到父元素外面表现成父元素被往下推了一段。解决方式是为父元素建立 BFC比如overflow: hidden或者改用 padding。8.5 调试工具的使用习惯排查 CSS 问题的另一半功夫在开发者工具的使用上。我调试样式时很少靠肉眼观察反复改代码。第一步是打开 Elements 面板选中目标元素查看 Computed 计算值。许多“为什么宽度不对”的问题看计算值往往直接有答案。第二步是临时禁用样式在 Style 面板里逐个取消勾选嫌疑属性观察变化。这一招比反复改代码刷新快得多。第三步是使用:hover强制状态来调试伪类。勾选 Force 状态:hover、:active可以精确检查交互态样式。最后一个习惯是善用“高亮所有元素”的 debugging 样式* { outline: 1px solid rgba(255, 0, 0, 0.3) !important; }临时加上这一条就能快速看清所有盒模型的边界。我个人更常用的方式是在关键的组件外层五分钟里用不同的背景色标记区域迅速判断是布局问题还是视觉问题。定位到边界之后再考虑要不要进一步动代码。写在最后的一点体会写 CSS 和写其他代码一样真正难的从来不是某个 API 或某个属性而是在遇到问题时能快速判断问题出在哪一层——是布局约束、是层叠顺序、是继承机制还是计算值和预设不符。CSS 编程的本质就是把这一堆看似零散的规则组织成可理解、可维护、可预测的系统。我自己也在持续学习尤其是容器查询和原生嵌套语法这两块随着浏览器支持度提升创作方式还会继续演进。如果你也在搭建自己的 CSS 工程体系建议从自定义属性、容器查询、规范命名这三件事入手先把地基打好后面的改造会轻松很多。这套路子在多个项目上验证过希望你也能少踩几个坑。
返回列表