SMACSS和BEM:如何将模块定位在模块内?

时间:2014-07-13 16:39:46

标签: css bem smacss

  

注意:我使用模块这个词,在BEM中称为。同样使用修改后的BEM命名约定BLOCK__ELEMENT--MODIFIER,也请在答案中使用。


假设我有一个.btn模块,看起来像这样:

.btn {
  background: red;
  text-align: center;
  font-family: Arial;

  i {
    width:15px;
    height:15px;
  }
}

我需要创建一个.popup-dialog模块,其中包含.btn

.popup-dialog {
  ...
  .btn {
    position: absolute;
    top: 10px;
    right: 10px;
  }
}

在SMACSS和BEM中,您应该如何处理模块内的模块定位?


在您的回答中,请确定正确的解决方案,并分析以下方法:(请注意以下所有示例都是基于或修改上述CSS)


方法1

[覆盖.btn]

内的原始.popup-dialog课程

CSS

.popup-dialog {
  ...
  .btn {  // override the original .btn class
    position: absolute;
    top: 10px;
    right: 10px;
  }
}

标记

<div class="popup-dialog">
  ...
  <button class="btn"><i class="close-ico"></i> close</btn>
</div>

方法2

[在.popup-dialog]

中添加一个子类

CSS

.popup-dialog {
  ...
  .popup-dialog__btn {
    position: absolute;
    top: 10px;
    right: 10px;
  }
}

标记

<div class="popup-dialog">
  ...
  <button class="btn popup-dialog__btn"><i class="close-ico"></i> close</btn>
</div>

方法3

[带有修饰符的子类.btn]

CSS

.btn--dialog-close {
  position: absolute;
  top: 10px;
  right: 10px;
}

标记

<div class="popup-dialog">
  ...
  <button class="btn btn--dialog-close"><i class="close-ico"></i> close</btn>
</div>

方法4

[子类.btn,布局类]

CSS

.l-dialog-btn {       // layout
  position: absolute;
  top: 10px;
  right: 10px;
}

标记

<div class="popup-dialog">
  ...
  <button class="btn l-dialog-btn"><i class="close-ico"></i> close</btn>
</div>

4 个答案:

答案 0 :(得分:16)

在最近的一个大型项目中,我一直在努力解决这个问题,但我赞赏你在SO上引起关注。

我担心没有一个正确的&#39;问题的解决方案,它将基于意见。但是,我会尽量做到客观,并对你的四个方法有所了解,这四个方法对我的团队有用,什么没有用。

此外,我还假设以下内容:

  • 您熟悉SMACCS方法(您阅读本书并在至少一个项目中实施)。
  • 您只对CSS类名使用(修改过的)BEM命名约定,但不使用BEM方法开发堆栈的其余部分。

方法1

显然是最糟糕的方法,并且存在一些缺陷:

  • 通过使用基于上下文的选择器,它在.popup-dialog.btn之间创建紧密耦合。
  • 您将来可能会遇到特殊性问题,假设您将来会在.btn中添加其他.popup-dialog个元素。

如果某种程度上你需要不加改变地使用类名,我建议至少通过使用直接子选择器来降低适用性的深度。

CSS:

.popup-dialog {...}

.popup-dialog > .btn {
  position: absolute;
  top: 10px;
  right: 10px;
}

方法2

这实际上非常接近我们的解决方案。我们在项目中设置了以下规则,并证明它是健壮的:&#34;模块不能有外部布局,但可以布局其子模块&#34;。这很大程度上受到SUITCSS框架的@necolas惯例的启发。注意:我们使用的是概念,而不是语法。

https://github.com/suitcss/suit/blob/master/doc/components.md#styling-dependencies

我们在这里选择了第二个选项,并将子模块包装在其他容器元素中。是的,它创建了更多标记,但是在使用第三方内容时我们仍然可以应用布局,我们无法控制HTML(嵌入来自其他网站等)。

CSS:

.popup-dialog {...}

.popup-dialog__wrap-btn {
  position: absolute;
  top: 10px;
  right: 10px;
}

HTML:

<div class="popup-dialog">
  ...
  <div class="popup-dialog__wrap-btn">
    <button class="btn"><i class="close-ico"></i> close</button>
  </div>
</div>

方法3

这可能看起来很干净(延伸而不是覆盖),但不是。它将布局与模块样式混合在一起。如果你有另一个模块需要为关闭按钮设置不同的布局,那么.btn--dialog-close上的布局样式将来没用。

方法4

这与方法3基本相同,但语法不同。布局类必须不知道它所布置的内容。另外,我并不热衷于书中建议的l-prefix语法。根据我的经验,它会产生更多的混乱而不是它的帮助。我们的团队完全放弃了它,我们只是把所有东西视为模块。但是,如果我需要坚持下去,我会尝试从模块中完全抽象出布局,这样你就可以获得有用且可重复使用的东西。

CSS:

.l-pane {
  position: relative;
  ...
}

.l-pane__item {
  position: absolute;
}

.l-pane__item--top-right {
  top: 10px;
  right: 10px;
}

.popup-dialog { // dialog skin
  ...
}

.btn { // button skin
  ...
}

HTML:

<div class="popup-dialog l-pane">
  <div class="l-pane__item l-pane__item--top-right">
    <button class="btn"><i class="close-ico"></i> close</button>
  </div>
</div>

对于这种方法,我不会对任何人造成错误,但根据我的经验,并非所有布局都可以以合理的方式抽象,并且必须单独设置。这也使其他开发人员更难理解。我从这个假设中排除了网格布局,它们很容易掌握并且非常有用。

你有它。由于上述原因,我建议尝试修改方法2。

希望能提供帮助。

答案 1 :(得分:2)

<强> BEM

如果你不在.btn内修改.popup-dialog,那么第一种方法是最好的。

如果您需要进行一些.btn修改,根据BEM方法,您必须使用修饰符类,例如.btn_size_s

如果你的修改没有直接与.btn连接,并且你怀疑将来是否可以重复使用,例如你必须只在弹出窗口中将.btn浮动到右边,你可以使用mixin like { {1}}

<强> SMACSS

再次,如果您需要将一个区块放在其他第一个方法中。

如果您需要任何修改,有两种方法:子类和后代选择器。

如果您将来可以重复使用修改 - 请使用子类,例如.popup-dialog__btn。 如果修改与某个特定模块紧密相关 - 更好地使用后代选择器。

<强>更新

添加几点以澄清我的答案:

首先,方法4是不可接受的 - 你将模块与布局混合是不好的做法,作为负责网格和页面截面几何的布局类,模块独立于布局,应该对它所放置的部分一无所知。

现在让我评论其他方法,它的最佳用法是什么:

方法1 - 请考虑以下情况:您有.btn-size-s模块,其中包含&#39;关闭&#39; Popup模块。 Button Popup不做任何修改,没有修改,没有浮点数或边距,它只是它的孩子。这种方法最好是这种情况。

方法2 - 另一种情况:Button有子Popup,但我们必须在右侧添加额外的上边距并浮动Button。正如您所看到的,这种修改与Button紧密结合,并且对其他模块无用。这样的本地&#39;修改这种方法的最佳用法。在BEM中这种方法也称为 mix

方法3 - 最终案例:Popup带有子按钮,但我们需要更大的Popup,这样的修改按钮可以重复使用,可能对其他模块和页面有用。 在BEM中,它被称为修饰符

要标记A2和A3之间的主要区别,请从Button移除Button并将其放在其他位置。 A3仍会影响Popup A2不会

因此,要将模块用作,您可以使用 A1或A2 ,在模块修改时应使用A3,与上下文无关。

答案 2 :(得分:0)

另一种惯例可能符合您的需求:https://ncss.io

<强>目标:

CSS的可预测语法,提供有关HTML模板的语义信息。

  • 哪些标签,组件和部分受到影响
  • 一个班级与另一个班级的关系是什么

示例:

<div class="modal-dialog">
  ...
  <div class="wrapper-button-dialog">
    <button class="button-dialog">close</button>
  </div>
</div>

答案 3 :(得分:0)

首先,我想澄清一下,根据BEM的定义,按钮是 ELEMENT 而不是 BLOCK 。因此,如果您使用BEM方法解决此问题,那么此问题会变得更简单一些。

其次,我同意mlnmln的解决方案(方法2 ),因为它定义了块内的元素变化,这对于块本身是唯一的。但是,如果弹出对话框块之外存在类似此按钮的元素变体,那么您可能需要采用方法3 并应用允许全局使用的命名约定。