一个元素的 BEM 方法是否可以根据块的不同而不同?
BEM approach for an element than can belong and look different depending on the Block?
假设我有一个样式化的复选框(想想 material 设计,其中有一点要实现所需的复选框样式)。哪个块负责修改 parent-dependent child 块?
示例组件 - 复选框
所以我有以下内容:
<div class="Checkbox">
<label class="Checkbox__label">
<input class="Checkbox__input" type="checkbox" checked=true />
<span class="Checkbox__icon glyphicon glyphicon-ok"></span>
<span class="Checkbox__text">{label}</span>
</label>
</div>
我为基本复选框设计了块中的每个元素。在应用程序的上下文中,复选框块可以存在于许多其他块中(具有它们自己的 BEM 结构)。
其他块的例子
复选框在 "Compact Panel":
中的外观略有不同
<div class="Panel Panel--compact">
<p>Disclaimer.. [text]</p>
<Checkbox label="I Agree" />
</div>
选项一 - Parent "knows" 关于 child
所以.. Compact Panel
应该是各种 children 块中的 "aware",并设置它们的样式,所以:
// Checkbox.scss
.Checkbox {
margin: 15px;
// .. other
}
// Panel.scss
.Panel {
&.Panel--compact {
margin: 0;
padding: 2px;
}
&.Panel--compact .Checkbox {
margin: 0;
padding: 1px;
}
}
选项二 - Child "knows" 关于 parent
或者,面板的感知度为零,复选框检查 parent 范围。
// Checkbox.scss
.Checkbox {
margin: 15px;
padding: 15px;
// .. other
}
.Panel.Panel--compact .Checkbox {
margin: 0;
padding: 1px;
}
// Panel.scss
.Panel {
&.Panel--compact {
margin: 0;
padding: 2px;
}
}
选项三 - ?
也许还有其他选择。
我最近开始了一个 React 项目,我想分享我在样式化 React 组件方面的经验。
React 组件中的级联样式确实令人困惑。所以第一件事是尝试为每个元素编写 class 。如果你愿意,你可以使用定义组件的 main class 进行包装。
.panel {
.checkbox {
}
}
我用css-modules。这太棒了。此工具可以创建唯一的 classes,因此您无需担心在其他组件中重复相同的名称。您可以从他们的 git 页面了解更多信息,网上有很多文章。
如果您正在使用 sass 并且需要使用变量,您可以在不同的文件夹(比如 ../assets/variables.scss
)中定义一个单独的 sass 文件,在您的组件 scss 文件中可以导入它并使用所有变量。 mixin 和函数也是如此。
在您的情况下,不必担心父子关系,为您需要设置样式的所有元素编写单个 classes。有时您可能需要使用父对象定位子对象。
根据this documentation,在BEM中应该避免使用嵌套选择器,但是"is appropriate for changing elements depending on the state of a block or its assigned theme"。这意味着您可以使用块的修饰符为其元素设置样式,但我找不到任何提及使用块的修饰符为其子块设置样式的内容。
我认为最好的方法是为 .Pannel
和 .Checkbox
块添加修饰符,例如:.Panel--compact
和 .Checkbox--context-compact
。这样你就不会在它们之间创建依赖关系,这是 BEM 原则。
如果您无法根据其上下文向 .Checkbox
添加修饰符,我认为最接近的选项是第二个选项,子项的行为取决于其父项的状态。
通常使用 BEM,如果它们 看起来 不同,那么它们 是 不同的。
通常。
使用 BEM 处理上下文和状态有许多不同的选择。每个都有不同的优缺点,因此您使用哪个在很大程度上取决于您的用例。
我要提到的第一个选项是使用后代选择器。您已经确定了这个选择,并且正在 运行 解决 "where does the code belong?"
的常见问题
对于以下示例,我将依赖 LESS 语法,这只是为了让我更容易在代码中演示关系。
后代选择器
如果您要使用后代选择器,我建议将代码与 child 块.
分组
widget.less
.widget {
&__container {
...
}
...
}
checkbox.less
.checkbox {
&__label {
...
}
...
// using inversion to override checkbox styles in a widget
// this will render to `.widget .checkbox` instead of
// `.checkbox .widget` due to the `&` placement
.widget & {
}
}
我建议将样式与内部块相关联的原因是样式会影响复选框,并且级联顺序很重要。
如果样式与 parent 关联,相对于 child 样式重新排序 parent 样式可能会对样式的呈现方式产生不利影响。
考虑这个包含顺序:
site.less
@import 'widget';
@import 'checkbox';
如果样式是小部件的一部分,它们可以被 checkbox.less
中具有相同特异性的选择器覆盖。
修饰符
我建议使用状态修饰符。我通常不认为位置或上下文是 "state",因此修饰符可能不合适。此外,同一元素上的多个修饰符可能难以推理,因此也难以设计样式。
假设您没有在 checkbox
块上使用修饰符,那么在面板中使用修饰符的情况下添加修饰符可能会更简单。
.checkbox {
&__label {
...defaults...
}
...defaults...
&--alt {
&__label {
...overrides...
}
...overrides...
}
}
当然,这需要针对面板中使用的特定情况更新标记,但它也让您可以在其他地方使用具有相同样式的复选框。
不同的选择器
我要重申我的第一点:如果他们看起来不同,他们是不同的。
这并不意味着您必须从头开始选择复选框。 BEM 允许面向 object 的样式。想出一个新名字,扩展*复选框:
checkbox.less
.checkbox {
&__label {
...
}
...
}
复选框-2.less
@import (reference) 'checkbox';
.checkbox-2 {
.checkbox;
&__label {
...overrides...
}
...overrides...
}
* 在 LESS 中,我为此使用了一个 mixin,因为它通常比使用语言的 :extend
特性更适合扩展和覆盖样式。请随意使用 :extend
功能,请注意选择器顺序很重要。
重构需求
有时我 运行 我 想要 使用后代选择器或修饰符,因为我需要碰撞一个块以便在容器中定位。
在这些情况下,我经常发现 容器本身 才是应该更改的。当我需要更新 child 以具有不同的内容时,我通常可以说它是容器:
- 边距
- 填充
- 位置
- 上、右、下、左
- 宽度
- 身高
- 弹性
重构伴随着其他挑战,但是我经常最终使用容器 div 来规范化包含其他块的块的插入区域; YMMV.
tl;dr:我应该选择哪个?
你能(合理地)更新标记吗?
是: 如果您可以轻松更新标记以使用不同的 类,我建议 扩展您的 checkbox
块作为一个新区块。虽然命名事物很困难,所以一定要在某处记录下哪个是哪个。
NO: 如果您无法轻松更新 类,使用修饰符也不是一个好的选择。我建议跳过那个,回到好的 后代选择器。在 BEM 中,您确实 想要避免后代选择器,但有时它们是完成这项工作的正确工具。
这是“正确”的方式:
我很惊讶为什么接受的答案没有提到 BEM“混合”。
在这些情况下,即当一个块在另一个块中使用时应该更改样式时,the official BEM documentation recommends 在同一 DOM 节点上使用块和元素 class。
因此,在您的情况下,这将是标记:
<div class="Panel Panel--compact">
<p>Disclaimer.. [text]</p>
<div class="Checkbox Panel__checkbox" /> <!-- Here is the change -->
</div>
这将是 CSS/SCSS:
// Checkbox.scss
.Checkbox {
margin: 15px;
padding: 15px;
// .. other
}
// Panel.scss
.Panel {
&__checkbox {
margin: 0;
padding: 2px;
}
}
真的就是这么简单。其他答案不必要地使这个问题过于复杂,我不知道为什么,BEM 文档对这个问题有明确的答案。您还可以在网上找到许多解释我刚才描述的技术的文章。
假设我有一个样式化的复选框(想想 material 设计,其中有一点要实现所需的复选框样式)。哪个块负责修改 parent-dependent child 块?
示例组件 - 复选框
所以我有以下内容:
<div class="Checkbox">
<label class="Checkbox__label">
<input class="Checkbox__input" type="checkbox" checked=true />
<span class="Checkbox__icon glyphicon glyphicon-ok"></span>
<span class="Checkbox__text">{label}</span>
</label>
</div>
我为基本复选框设计了块中的每个元素。在应用程序的上下文中,复选框块可以存在于许多其他块中(具有它们自己的 BEM 结构)。
其他块的例子
复选框在 "Compact Panel":
中的外观略有不同<div class="Panel Panel--compact">
<p>Disclaimer.. [text]</p>
<Checkbox label="I Agree" />
</div>
选项一 - Parent "knows" 关于 child
所以.. Compact Panel
应该是各种 children 块中的 "aware",并设置它们的样式,所以:
// Checkbox.scss
.Checkbox {
margin: 15px;
// .. other
}
// Panel.scss
.Panel {
&.Panel--compact {
margin: 0;
padding: 2px;
}
&.Panel--compact .Checkbox {
margin: 0;
padding: 1px;
}
}
选项二 - Child "knows" 关于 parent
或者,面板的感知度为零,复选框检查 parent 范围。
// Checkbox.scss
.Checkbox {
margin: 15px;
padding: 15px;
// .. other
}
.Panel.Panel--compact .Checkbox {
margin: 0;
padding: 1px;
}
// Panel.scss
.Panel {
&.Panel--compact {
margin: 0;
padding: 2px;
}
}
选项三 - ?
也许还有其他选择。
我最近开始了一个 React 项目,我想分享我在样式化 React 组件方面的经验。
React 组件中的级联样式确实令人困惑。所以第一件事是尝试为每个元素编写 class 。如果你愿意,你可以使用定义组件的 main class 进行包装。
.panel {
.checkbox {
}
}
我用css-modules。这太棒了。此工具可以创建唯一的 classes,因此您无需担心在其他组件中重复相同的名称。您可以从他们的 git 页面了解更多信息,网上有很多文章。
如果您正在使用 sass 并且需要使用变量,您可以在不同的文件夹(比如 ../assets/variables.scss
)中定义一个单独的 sass 文件,在您的组件 scss 文件中可以导入它并使用所有变量。 mixin 和函数也是如此。
在您的情况下,不必担心父子关系,为您需要设置样式的所有元素编写单个 classes。有时您可能需要使用父对象定位子对象。
根据this documentation,在BEM中应该避免使用嵌套选择器,但是"is appropriate for changing elements depending on the state of a block or its assigned theme"。这意味着您可以使用块的修饰符为其元素设置样式,但我找不到任何提及使用块的修饰符为其子块设置样式的内容。
我认为最好的方法是为 .Pannel
和 .Checkbox
块添加修饰符,例如:.Panel--compact
和 .Checkbox--context-compact
。这样你就不会在它们之间创建依赖关系,这是 BEM 原则。
如果您无法根据其上下文向 .Checkbox
添加修饰符,我认为最接近的选项是第二个选项,子项的行为取决于其父项的状态。
通常使用 BEM,如果它们 看起来 不同,那么它们 是 不同的。
通常。
使用 BEM 处理上下文和状态有许多不同的选择。每个都有不同的优缺点,因此您使用哪个在很大程度上取决于您的用例。
我要提到的第一个选项是使用后代选择器。您已经确定了这个选择,并且正在 运行 解决 "where does the code belong?"
的常见问题对于以下示例,我将依赖 LESS 语法,这只是为了让我更容易在代码中演示关系。
后代选择器
如果您要使用后代选择器,我建议将代码与 child 块.
分组 widget.less.widget {
&__container {
...
}
...
}
checkbox.less
.checkbox {
&__label {
...
}
...
// using inversion to override checkbox styles in a widget
// this will render to `.widget .checkbox` instead of
// `.checkbox .widget` due to the `&` placement
.widget & {
}
}
我建议将样式与内部块相关联的原因是样式会影响复选框,并且级联顺序很重要。
如果样式与 parent 关联,相对于 child 样式重新排序 parent 样式可能会对样式的呈现方式产生不利影响。
考虑这个包含顺序:
site.less@import 'widget';
@import 'checkbox';
如果样式是小部件的一部分,它们可以被 checkbox.less
中具有相同特异性的选择器覆盖。
修饰符
我建议使用状态修饰符。我通常不认为位置或上下文是 "state",因此修饰符可能不合适。此外,同一元素上的多个修饰符可能难以推理,因此也难以设计样式。
假设您没有在 checkbox
块上使用修饰符,那么在面板中使用修饰符的情况下添加修饰符可能会更简单。
.checkbox {
&__label {
...defaults...
}
...defaults...
&--alt {
&__label {
...overrides...
}
...overrides...
}
}
当然,这需要针对面板中使用的特定情况更新标记,但它也让您可以在其他地方使用具有相同样式的复选框。
不同的选择器
我要重申我的第一点:如果他们看起来不同,他们是不同的。
这并不意味着您必须从头开始选择复选框。 BEM 允许面向 object 的样式。想出一个新名字,扩展*复选框:
checkbox.less.checkbox {
&__label {
...
}
...
}
复选框-2.less
@import (reference) 'checkbox';
.checkbox-2 {
.checkbox;
&__label {
...overrides...
}
...overrides...
}
* 在 LESS 中,我为此使用了一个 mixin,因为它通常比使用语言的 :extend
特性更适合扩展和覆盖样式。请随意使用 :extend
功能,请注意选择器顺序很重要。
重构需求
有时我 运行 我 想要 使用后代选择器或修饰符,因为我需要碰撞一个块以便在容器中定位。
在这些情况下,我经常发现 容器本身 才是应该更改的。当我需要更新 child 以具有不同的内容时,我通常可以说它是容器:
- 边距
- 填充
- 位置
- 上、右、下、左
- 宽度
- 身高
- 弹性
重构伴随着其他挑战,但是我经常最终使用容器 div 来规范化包含其他块的块的插入区域; YMMV.
tl;dr:我应该选择哪个?
你能(合理地)更新标记吗?
是: 如果您可以轻松更新标记以使用不同的 类,我建议 扩展您的 checkbox
块作为一个新区块。虽然命名事物很困难,所以一定要在某处记录下哪个是哪个。
NO: 如果您无法轻松更新 类,使用修饰符也不是一个好的选择。我建议跳过那个,回到好的 后代选择器。在 BEM 中,您确实 想要避免后代选择器,但有时它们是完成这项工作的正确工具。
这是“正确”的方式:
我很惊讶为什么接受的答案没有提到 BEM“混合”。 在这些情况下,即当一个块在另一个块中使用时应该更改样式时,the official BEM documentation recommends 在同一 DOM 节点上使用块和元素 class。
因此,在您的情况下,这将是标记:
<div class="Panel Panel--compact">
<p>Disclaimer.. [text]</p>
<div class="Checkbox Panel__checkbox" /> <!-- Here is the change -->
</div>
这将是 CSS/SCSS:
// Checkbox.scss
.Checkbox {
margin: 15px;
padding: 15px;
// .. other
}
// Panel.scss
.Panel {
&__checkbox {
margin: 0;
padding: 2px;
}
}
真的就是这么简单。其他答案不必要地使这个问题过于复杂,我不知道为什么,BEM 文档对这个问题有明确的答案。您还可以在网上找到许多解释我刚才描述的技术的文章。