Harry Nicholls 最近写了一篇关于 使用函数式 CSS 简化样式 的文章,您一定要看看。 简而言之,函数式 CSS 是 原子 CSS 或 使用“辅助”或“实用”类 的另一个名称,这些类只会处理 padding
或 margin
,background-color
或 color
等。
Harry 完全喜欢在元素上添加多个这样的类。
所以,我在这里试图倡导的是利用其他人构建函数式 CSS 库所做的工作。 它们建立在设计的基础之上,人们花了许多时间思考这些库应该如何构建,以及哪些是最有用的类。
而且,不仅类有用,而且 Tachyons 背后的基本设计原则也有用。
这对我来说很有道理。 但是,Chris 指出,他还没有听说过函数式/原子 CSS 方法的缺点。
大规模重新设计会发生什么? 是相同的、时间和难度方面,还是您花更多时间拆除所有这些类? 当您需要一个不可用的样式时会发生什么? 自己写? 还是这会破坏所有这些的精神,把你置于危险的境地? 所有的类名可以有多复杂? 我可以想到我为其设计过样式的区域,这些区域有三个或更多媒体查询,它们会极大地重新设计元素。 将所有这些信息放在 HTML 中似乎会变得非常混乱。 一致性更难还是更容易?
这对我来说也很有道理,但问题是:我非常喜欢这两种方法,甚至将它们组合在同一个项目中。
在你生气之前,听我说
在 Gusto(我今天工作的公司),我一直在尝试设计一个使用这两种方法的系统,因为我真的相信它们可以彼此和谐共处。 它们各自解决了编写 CSS 的非常不同的用例。
以下是一个示例:假设我们在一个大型 React Web 应用程序中工作,并且我们的设计师交付了一个页面设计,其中一个段落和一个按钮需要在下方留出更多间距。 我们的代码如下所示
<p>Item 1 description goes here</p>
<Button>Checkout item</Button>
这正是函数式 CSS 解决的问题。 在 Gusto,我们会做类似的事情
<div class="margin-bottom-20px">
<p>Item 1 description goes here</p>
<button>Checkout item</button>
</div>
换句话说,我们使用函数式类来进行布局调整,这些调整可能特定于我们正在处理的特定功能。 但是! 该 Button
组件由一个普通的 CSS 文件组成。 在 btn.scss
中,我们有类似的代码,然后将其导入到我们的 btn.jsx
组件中
.btn {
padding: 10px 15px;
margin: 0 15px 10px;
// rest of the styles go here
}
我认为为自定义组件创建全新的 CSS 文件比尝试使用大量类(例如 margin-*
、padding-*
等)来创建这些组件要容易得多。 虽然,我们也可以在 btn.jsx
组件中使用函数式样式,如下所示
const Button = ({ onClick, className, children }) => {
return (
<button
className='padding-top-10px padding-bottom-10px padding-left-15px padding-right-15px margin-bottom-none margin-right-15px margin-left-15px margin-bottom-10px ${className}')}
onClick={onClick}
>
{children}
</button>
);
};
这不是一个现实的例子,因为我们只处理两个属性,我们可能希望为该按钮的背景色、文字颜色、悬停状态等设置样式。 而且,是的,我知道这些类名有点复杂,但我认为即使将垂直和水平类结合在一起,我的观点仍然成立。
所以 我认为 通过在此特定实例中将自定义样式写入单独的 CSS 文件,我们用函数式 CSS 解决了以下三个问题
- 可读性
- 管理属性依赖项
- 避免视觉设计不喜欢数学的痛苦事实
如您在前面的代码示例中看到的,很难阅读并立即看到哪些类已应用于按钮。 更多的类意味着更难扫描。
其次,许多 CSS 属性/值对是相互关联的。 例如,position: relative
和 position: absolute
。 在我们的样式表中,我希望能够看到这些依赖关系,我相信用函数式 CSS 做到这一点更难。 CSS 通常依赖于 CSS 的其他部分,并且用注释或属性/值分组来查看这些连接很重要。
最后,视觉设计是一个问题。 许多视觉设计需要不完美的数字,这些数字无法正确缩放。 使用函数式 CSS 系统,您可能希望使用以 10 为底或以 8 为底的系统,其中每个值都基于该比例。 但是,当您在视觉上将项目对齐时,您可能需要以一种与这些值不对齐的方式进行对齐。 这被称为 光学调整,这是因为我们的大脑很奇怪。 在数学上合理的,在视觉上往往不合理。 因此,在这种情况下,我们需要在按钮下方添加更多底部填充,以使文本看起来像是在中心位置。 使用函数式 CSS 方法,至少根据我的经验,很难整洁地做到这一点。
在需要平衡可读性、依赖关系和光学调整的那些情况下,在传统的样式表中编写常规 CSS 仍然是我最喜欢的事情。 但是,函数式 CSS 仍然非常优雅地解决了大量其他问题。
例如,我们在 Gusto 使用函数式类试图防止的是创建大量样式表,这些样式表执行大量非常具体或自定义的事情。 回到前面关于两个元素下方间距的示例
<div className='margin-bottom-20px'>
<p>Item 1 description goes here</p>
<Button>Checkout item</Button>
</div>
过去,我们的团队可能会写类似的东西
<div className='cool-feature-description-wrapper'>
<p>Item 1 description goes here</p>
<button>Checkout item</button>
</div>
需要在我们的应用程序中创建一个名为 cool_feature_description_wrapper.scss
的新 CSS 文件,如下所示
.cool-feature-description-wrapper {
margin-bottom: 20px;
}
我认为像这样的样式使我们的代码更难理解、更难阅读,并鼓励偏离我们的组件库。 通过将其替换为来自我们的函数式类库的类,它突然变得更容易阅读,并且将来更容易更改。 它还解决了我们特定需求的自定义解决方案,而无需分叉我们的样式库。
所以,我没有读到太多关于以这种方式平衡这两种方法的文章,尽管我认为有人已经深入地介绍了这个问题。 我真正相信,将这两种方法结合起来比尝试用一个技巧包来解决所有问题更有用。
我知道,对吧? 模棱两可的观点是最糟糕的。
我现在正在进行一个内部项目,在这个项目中我正在结合使用函数式 CSS 和常规 CSS。 例如,我将使用常规 CSS 来表示我知道将在网站的每个页面上都存在的对象或元素,或者以相同的方式呈现,无论何时添加到页面(例如,主导航或按钮之类的组件)。 由于这是一个有既定品牌等的内部项目,所以我觉得以这种方式“锁定”这些元素的样式很舒服。
但我会经常使用函数式 CSS 作为一种微调元素的方法,或者当我需要更多特定于页面的元素和模式时(例如,页面 A 上的画廊网格外观与页面 B 上的外观不同)。
我承认这有时是一个灰色地带,有时我会来回考虑是否更依赖函数式 CSS 或常规 CSS,但到目前为止,它似乎运行得很好。
神奇的词是“特定于页面的元素和模式”。 我认为 CSS 组织应该就是这样。
对我来说,有一个 CSS 类“margin-bottom-20px”(以及其他类似的东西),就像内联样式一样,就像有人发现“style”属性有毒一样,只是说…
“而且,是的,我知道这些类名有点复杂”
我仍然更喜欢使用 Harry Roberts 的 ITCCS 架构。 这样,您就可以在使用“组件”和“实用工具”层同时使用函数式 CSS 和常规 CSS 的情况下构建可扩展的设置。 我认为这是两全其美。 它提供了极大的灵活性,因为没有一个解决方案可以解决所有客户端/代码库问题;)
我与这个确切的问题作斗争!
我的主要区别是我喜欢函数式 CSS 来表示不会改变的样式,但添加交互状态、媒体查询和其他变化的变体可能会成为一个问题。 我在 CodePen 上写了一篇关于它 的文章,如果您有兴趣。
你好!,感谢分享!,你可以使用 classnames 来提高可读性
您是否查看过 Tailwind——它是实用优先的,但它鼓励您在建立模式后创建组件。
https://tailwind.org.cn/
我完全反对使用实用类,但我确实使用它们。
对我来说,样式具有意义,它应该在代码中可见:类名应该反映它们的用途,而不是它们的确切作用。
在你的例子中,我可能会有一个像 .my-component__spacing 这样的类名。所以它会说明这个“元素”意图进行间距,但它不会确切说明间距是多少。然后在样式表中,我会根据这个间距设置一个“神奇数字”,或者使用一个已经存在的 10 或任何其他圆形数字的间距类。
结果是:一个隐藏在具有意义的“面向对象”命名下的实用类。
这说得通吗?
我们已经使用完全相同的方法多年了,并且对此感到非常满意。将经典的 BEM 方法与我们称之为实用类的内容结合起来,就像拥有两全其美一样。我们通常在组件的修饰符数量会激增的地方引入实用类(例如间距、语义文本颜色、文本对齐等)。
函数式 CSS 是为不想编写 CSS 的人准备的。
这种方法最大的缺点是响应式设计。如今,网站元素的行为和外观发生了巨大的变化,直到最近才让人头疼。
对我来说,函数式 CSS 就像不知道 web 设计历史,忽略了内联样式带来的痛苦一样。
不,我不同意。我已经编写了 15 年以上的 CSS。我喜欢这种语言。但我几乎从未通过重新调整 CSS 来重新设计应用程序。我做过的几次,都很糟糕。存在局限性。
几乎总是,都需要对 HTML 和 CSS 进行调整,使两者再次同步。编写和重写 CSS 和标记钩子似乎已经过时了。CSS 是有限的。标记永远不会。让我们通过编写更少的 CSS 来节约资源。用类来组合。在需要时添加组件类和特殊花式。
实用类可以是响应式的(例如 .flex、.md–flex、.lg–flex)。可以说,这是一种更人性化的处理样式,尤其是布局的方法。我可以看出它的意图——.component-x 类完全不透明。
这些不是内联样式——此外,内联样式的真正问题是特异性。在有人将语义注入到论证中之前,类与语义的整个概念就很奇怪。我们有数据属性、角色、元素等;这些用于语义。CSS 用于布局和样式。让它做它该做的事。
我一直将这两种方法结合使用。我想我读的 CSS 相关内容不够多,因为我以前从未听说过函数式 CSS 或原子 CSS。我一直只称呼这些类本身,称之为实用 CSS。我也见过很多其他人这样做。
我同意,成为 CSS(或任何类型的代码样式)“纯粹主义者”没有意义——每种方法都有其用例,为什么不将它们结合起来呢?
我确实对“margin-bottom-20px”这样的类名感到好奇。为什么要使用类?我不是在讽刺——我真的好奇为什么这会比“style”属性更有效。难道它不会只是额外的字节(类定义加上长的类属性)?如果决定布局中的所有“间距”实际上应该是 18 像素而不是 20 像素怎么办?
组件是为我理想化的未来自我而准备的,而实用类是为我现实的未来自我而准备的。实用类在与大型团队合作时可以使事情变得更容易。它可以加快开发时间并有助于保持一致性。组件仍然是核心,但实用类非常有用。如果每个人都能完美地编辑 CSS,那么仅仅使用组件可能就足够了,但在实践中,这种情况很少见。
用 BEM 构建你的组件,并使用实用类将它们定位在你的布局中。
我的方法是这样的
如果可以用实用类来构建,就用实用类来构建它。
当模式出现时,开始抽象它们。根据技术栈的不同,这可能是组件或 BEM 类,它们会重用实用类(Tailwind 的 @apply 规则非常适合这种情况)。
我只将上下文无关的样式放入组件中。例如,我尽力避免在组件中添加边距、对齐或尺寸,这些可能会根据上下文发生变化。
例如,
card
组件会抽象出所有样式,以设计组件内部发生的事情。但根据使用它的上下文不同,它可能需要不同的外部间距,因此我会根据使用它的上下文混合使用正确的实用类:它可能在另一个地方看起来像<div class="c-card mx-4">
和<div class="c-card mx-2">
,其中需要更小的间距(mx 是 Tailwind 对水平边距的命名)。另一个简单的例子是文本大小的变化。c-button
包含定义按钮外观的基本样式。如果我需要一个更大的按钮,我只需添加相应的实用类c-button text-xl
,从而避免陷入 BEM 修饰符地狱(如果按钮的边距使用em
单位设置,它将随着文本大小自动缩放)。这正是我正在做的事情。它运作良好,对吧?!
我想我从 Harry Roberts 的 InuitCSS 中窃取了所有最好的部分,应用了一些额外的 flexbox,然后又偷用了一些 Tailwind 类名。
对我来说,实用类优先非常难以阅读,增加了认知负荷。作为一名开发者,我遇到过这样的类: “p-1, m-2, d-this, d-that”。我的大脑现在必须进行一些操作,来解码那些代码,我最终会花更多时间在应用程序的内容区域,而不是在应用程序的样式区域。
也就是说,我倾向于将两者混合使用,但更喜欢使用非常轻量级的实用类,原因有两个:(1)为了保持 HTML 的尽可能干净(特别是对于更复杂、业务逻辑更重的应用程序),以及(2)因为我喜欢使用 SASS 文件作为管理、迭代和扩展应用程序的主要控制中心和“界面”。
以下是一个语义和实用类的混合示例,我发现它是两者的优雅平衡
我们的内部团队正在使用函数式 CSS 和 Sass 的组合。我们没有在 HTML 中将 “bg-black” 作为类来编写,而是在 Sass 混合中编写它。
查看此处提供的文档:https://hrsetyono.github.io/edje/
我们已经看到了更快的开发时间和团队成员之间更好的理解。