如何在DoCSSa中处理延迟的对象

This article from Matthieu Larcher is also available in French.

Matthieu Larcher的这篇文章也提供法语版本

介绍 (Introduction)

DoCSSa is a Sass based architecture and methodology. You can read about it at docssa.info and get the source code of that documentation on the GitHub page. The source code makes use of the concepts, so it's a good starting point. In this article, we are going to focus on the Deferred Objects DoCSSa takes its name from: what do we mean by that and how to use them ?

DoCSSa是基于Sass的体系结构和方法。 您可以在docssa.info上进行阅读,并在GitHub页面上获取该文档的源代码。 源代码利用了这些概念,因此这是一个很好的起点。 在本文中,我们将集中讨论DoCSSa的名称来自于Deferred Objects :这是什么意思,以及如何使用它们?

什么是CSS对象? (What is a CSS Object ?)

An object, in the CSS world, is basically a set of rules to apply, a repeating visual pattern. OOCSS for example defines a "media" object. It represents some rules applied to the image and text inside a block in order to create a specific look. That look can then be applied easily to any context. The point is to stay DRY so that we can keep code maintenance easy instead of rewriting the same rules over and over, all over the place.

在CSS世界中,对象基本上是一组要应用的规则,即重复的视觉模式。 例如,OOCSS定义了一个“媒体”对象。 它代表一些规则,这些规则适用于块内的图像和文本,以创建特定外观。 然后,可以轻松地将该外观应用于任何上下文。 关键是保持DRY状态,以便我们可以轻松维护代码,而不必一遍又一遍地重写相同的规则。

OOCSS media object example

While OOCSS is good and brought some solid principles to the CSS world, it also suffers from a few issues. Most notably, it allows for CSS leakage, and mixes presentation with content.

尽管OOCSS很好,并为CSS世界带来了一些扎实的原则,但它也遇到了一些问题。 最值得注意的是,它允许CSS泄漏,并将演示与内容混合在一起。

A CSS leakage is an unintended side effect of a CSS rule. For example, if you have a HTML markup that looks like this:

CSS泄漏是CSS规则的意外副作用。 例如,如果您有一个如下所示HTML标记:

<section class="section">
    <h3 class="title">My title</h3>
    <p>some content</p>
</section>


You may set a rule that looks like so:

您可以设置如下规则:

.section .title {
    margin-top: 20px;
}


But if at some point you add some content and end up with the following markup:

但是,如果您在某些时候添加了一些内容并最终得到以下标记:

<section class="section">
    <h3 class="title">My title</h3>
    <p>some content</p>

    <div class="nestedContent">
        <h5 class="title">My nested content title</h5>
        <p>some other content</p>
    </div>

</section>


You are now victim of a CSS leakage: the title of your nestedContent now has a margin-top of 20px. That may not be what you had in mind at all when you wrote it. This is somewhat a contrived example, but in real world with some more complex markup, this tends to happened a very easily. Hopefully, Some smart guys at Yandex came around and suggested the BEM system. One of its key parts is a naming convention that would have us rewrite our example this way:

您现在是CSS泄漏的受害者:nestedContent的标题的边距顶部现在为20px。 编写时可能根本就没有这想法。 这是一个人为的例子,但是在现实世界中,标记有些复杂,这往往很容易发生。 希望,Yandex的一些聪明人能够提出建议BEM系统 。 它的关键部分之一是命名约定,可以让我们以这种方式重写示例:

<section class="section">
    <h3 class="section_title">My title</h3>
    <p>some content</p>

    <div class="nestedContent">
        <h5 class="nestedContent_title">My nested content title</h5>
        <p>some othe content</p>
    </div>

</section>


.section_title {
    margin-top: 20px;
}


No more CSS leakage!

不再有CSS泄漏!

BEM stands for Block-Element-Modifier. So, that class naming convention goes hand in hand with a way of thinking your markup as components (Blocks and Elements). We could say that a "Block" is an object, that needs to apply some CSS rules. The Elements constituting the Block need to have a set of rules that matches those given to the Block to make a component.

BEM代表块元素修饰符。 因此,该类命名约定与将标记视为组件(块和元素)的方式紧密相关。 我们可以说“块”是一个对象,需要应用一些CSS规则。 构成块的元素需要具有一组与分配给块的规则相匹配的规则以构成组件。

Another important aspect to handle is the Single Responsibility Principle (SRP). SRP is a wide spread concept that states that everything should do one and only one thing, and do it well. There are many possible interpretations to this, one of which is the following: HTML's responsibility is to provide content while CSS provides appearance (and JS provides behavior). If at some point your HTML markup is saying

处理的另一个重要方面是单一责任原则 (SRP)。 SRP是一个广泛传播的概念,它指出一切都应该做一件事情,并且一件事情要做得很好。 有许多可能的解释,其中之一是:HTML的责任是提供内容,而CSS提供外观 (而JS提供行为)。 如果您HTML标记在某个时候说

<div class="roundCorners redBackground">


you are in for a hell lot of maintenance problems. What if you want to remove the round corners on that div ? Do you dig into the RTE database to remove all the unwanted roundCorners classes ? What if you want to change the color ? Do you change your CSS to say ".redBackground {color: blue;}" ? Those helpers are very helpful indeed, but we really need to dissociate them from the markup (content). That's the only way we can respect the SRP and preserve our future coworkers' mental health. Hopefully, it is totally possible to keep your HTML class names semantic and bind some DRY components to it, and DoCSSa is here to demonstrate it.

您遇到了很多维护问题。 如果要删除该div上的圆角怎么办? 您是否深入RTE数据库以删除所有不需要的roundCorners类? 如果要更改颜色怎么办? 您是否将CSS更改为“ .redBackground {color:blue;}”? 这些帮手确实很有帮助,但是我们确实需要将它们与标记(内容)分离。 这是我们尊重SRP并维护未来同事的心理健康的唯一方法。 希望完全有可能保留您HTML类名的语义并将其绑定一些DRY组件,DoCSSa在此进行了演示。

从OOCSS到DoCSSa (From OOCSS to DoCSSa)

Let's take a basic example. It has a top menu and contains a form with a "submit" and a "back" button.

让我们举一个基本的例子。 它具有顶部菜单,并包含带有“提交”和“返回”按钮的表单。

example wireframe

As you can see, all the buttons look the same. But that doesn't mean you have to change your markup (i.e. "content") if you want to style your "back" buttons differently than your "submit" buttons. They have different meaning, so they should bear different classes reflecting their respective roles. Only CSS is concerned about how they look. Let's look at what the HTML markup may look like in a traditional way:

如您所见,所有按钮看起来都一样。 但这并不意味着您要更改“返回”按钮的样式与“提交”按钮的样式不同,就不必更改标记(即“内容”)。 它们具有不同的含义,因此它们应具有不同的类别以反映各自的角色。 只有CSS关心它们的外观。 让我们来看一下传统方式下HTML标记:

<ul>
    <li>
        <a href="#part-1" class="button">part 1</a>
    </li>
    <li>
        <a href="#part-2" class="button">part 2</a>
    </li>
    <li>
        <a href="#part-3" class="button">part 3</a>
    </li>
</ul>
<form action="#">
    <div>
        <div>
            <label for="input-firstName">First name</label>
            <input type="text" name="firstName" id="input-firstName">
        </div>
        <div>
            <label for="input-lastName">Last name</label>
            <input type="text" name="lastName" id="input-lastName">
        </div>
        <div>
            <label for="input-address">Address</label>
            <input type="text" name="address" id="input-address">
        </div>
        <div>
            <button class="button">Back</button>
            <button class="button" type="submit">Submit</button>
        </div>
    </div>
</form>


And the CSS associated with it:

以及与之关联CSS:

.button {
    box-sizing: border-box;
    display: inline-block;
    padding: 10px;
    border-radius: 5px;
    background: #449888; 
    background: linear-gradient(to bottom,  #449888 0%,#a8d5cd 50%,#449888 51%,#5cbcaa 100%); 
    border: 2px solid #449888;
    color: #000;
    text-decoration: none;
    cursor: pointer;
    font-weight: bold;
    color: #fff;
    text-shadow: 0 -1px 0 #449888;
}
.button:hover {
    background: #5cbcaa;
    background: linear-gradient(to bottom,  #5cbcaa 0%,#a8d5cd 50%,#5cbcaa 51%,#5cbcaa 100%);
}


As you can see, this example uses the class "button" in the HTML everywhere we want to display a button. Doing this, we are exposing ourselves to the various problems we talked about earlier. Now Let's see how we would rewrite it according to DoCSSa.

如您所见,此示例在我们要显示按钮的所有地方都使用HTML中的“按钮”类。 这样做,我们使自己暴露于我们之前谈到的各种问题。 现在,让我们看看如何根据DoCSSa重写它。

The markup itself won't change much. We will just use classes reflecting the intent of the element instead of the classes reflecting its appearance. Here's how it could look like:

标记本身不会有太大变化。 我们将只使用反映元素意图的类,而不是反映元素外观的类。 看起来像这样:

<ul>
    <li>
        <a href="#part-1" class="navMenu_link">part 1</a>
    </li>
    <li>
        <a href="#part-2" class="navMenu_link">part 2</a>
    </li>
    <li>
        <a href="#part-3" class="navMenu_link">part 3</a>
    </li>
</ul>
<form action="#">
    <div>
        <div>
            <label for="input-firstName">First name</label>
            <input type="text" name="firstName" id="input-firstName">
        </div>
        <div>
            <label for="input-lastName">Last name</label>
            <input type="text" name="lastName" id="input-lastName">
        </div>
        <div>
            <label for="input-address">Address</label>
            <input type="text" name="address" id="input-address">
        </div>
        <div>
            <button class="backBtn">Back</button>
            <button class="submitBtn" type="submit">Submit</button>
        </div>
    </div>
</form>


Then we will create our "button" component:

然后,我们将创建“按钮”组件:

// in components/buttons/_button.scss

//在components / buttons / _button.scss中

/* **** BUTTON COMPONENT **** */

// define component placeholders for component contents (no selector here)
@include define('button') {
  %button {
    box-sizing: border-box;
    display: inline-block;
    padding: 10px;
    cursor: pointer;
  }
}

// map the placeholders content to some selectors through a mixin
@mixin example($selector, $defaultSkin: true) {

  #{$selector} {
    @extend %button;
  }

}

/* **** BUTTON COMPONENT SKIN **** */

// define component placeholders for component skin (no selector here)
@include define('button_skin_default') {
  %button-skin-default {    
    border-radius: 5px;
    background: #449888; 
    background: linear-gradient(to bottom,  #449888 0%,#a8d5cd 50%,#449888 51%,#5cbcaa 100%); 
    border: 2px solid #449888;
    text-decoration: none;
    font-weight: bold;
    color: #fff;
    text-shadow: 0 -1px 0 #449888;
  }

  %button-skin-default__altState {
    background: #5cbcaa;
    background: linear-gradient(to bottom,  #5cbcaa 0%,#a8d5cd 50%,#5cbcaa 51%,#5cbcaa 100%);
  }
}

// provide a default skin for the component
// only visual changes that don't affect the component layout should be in here
@mixin button-skin-default($selector) {

    #{$selector} {
      @extend %button-skin-default;
    }

    #{$selector}:hover {
      @extend %button-skin-default__altState;
    }

}


Granted, it is a bit more verbose than the traditionnal version. But it also goes a long way when it comes to site maintenance or regression avoidance. Plus you don't have to define new components everyday. Once you have them you can use them (or not) any time you like. You can even re-use the components in any other project, as the skin is decoupled from the structure (ui-components libraries anyone?).

当然,它比传统版本更冗长。 但是,在站点维护或避免回归方面也有很长的路要走。 另外,您不必每天定义新的组件。 拥有它们后,您可以随时使用它们(或不可以)。 您甚至可以在任何其他项目中重复使用组件,因为皮肤与结构分离(ui-components库有人吗?)。

Once you have defined your components, using them is as easy as it comes, and you avoid a lot of the CSS pitfalls. Here's how we'd apply the "button" behaviour to our elements:

一旦定义了组件,就可以轻松使用它们,并且避免了很多CSS陷阱。 这是我们将“按钮”行为应用于元素的方式:

// in specifics/_someSpecificFile.scss

//在details / _someSpecificFile.scss中

@include button('.navMenu_link');
@include button('.backBtn');
@include button('.submitBtn');


See how easy that is ? We have the same look as before, but now changing the style of any of the items can be done on the SCSS side only. It can be done in a matter of seconds, without any change to the HTML markup.

看到那有多么容易? 我们的外观与以前相同,但是现在更改任何项的样式只能在SCSS一侧进行。 只需几秒钟即可完成,而无需更改HTML标记。

BEM是您的朋友 (BEM is your friend)

In order to bind components to class names, we need some class names to hook onto. Those classes should represent the role of the elements holding them, along with the minimum required info about their context (the "Block" they relate to). For example, if an Unordered List represents a navigation menu, it most probably holds some List Items that are by definition items of that menu. There is also very likely a link in each item that is a link of the navigation menu. Thanks to BEM, we already have a convention describing a way to make that relation explicit in the markup. Here is how we would represent the menu we just described in HTML:

为了将组件绑定到类名,我们需要一些类名来挂钩。 这些类应该代表持有它们的元素的角色,以及有关其上下文的最低要求信息(它们所涉及的“块”)。 例如,如果“无序列表”表示导航菜单,则它很可能包含某些“列表项”,这些列表项是该菜单的定义项。 每个项目中也很可能有一个链接,即导航菜单的链接。 多亏了BEM,我们已经有了一个约定,描述了一种在标记中明确表示该关系的方法。 这是我们将如何表示我们刚刚用HTML描述的菜单:

<ul class="navMenu">
    <li class="navMenu_item">
        <a href="#part-1" class="navMenu_link">part 1</a>
    </li>
    <li class="navMenu_item">
        <a href="#part-2" class="navMenu_link">part 2</a>
    </li>
    <li class="navMenu_item">
        <a href="#part-3" class="navMenu_link">part 3</a>
    </li>
</ul>


As you can see, we define a "navMenu" element, its items and its links in a straightforward way.

如您所见,我们以一种简单的方式定义了一个“ navMenu”元素,其项目及其链接。

Note that a common mistake when beginning with BEM is to try to reflect all the HTML nesting in the class names. In that logic, "navMenu_link" would have been named "navMenu_item_link". This is totally unnecessary and would clutter the markup, making it less flexible. Our class names are already longer than before, and a bit verbose sometimes. There is no need to make it worse when it doesn't bring anything to the table.

请注意,从BEM开始的一个常见错误是试图在类名中反映所有HTML嵌套。 按照这种逻辑,“ navMenu_link”将被命名为“ navMenu_item_link”。 这是完全不必要的,并且会使标记混乱,使其灵活性降低。 我们的班级名称已经比以前更长,有时还会有些冗长。 当它什么也没做的时候,没有必要变得更糟。

Once you get the hang of it, you will place css "hooks" on every class that may need styling. As long as those classes represent the role of the element, you won't run out of ideas on how to name it. Once you've done that, you'll be free to bind any DoCSSa component to them. Of course, the component will need to be fit for the Block you want to apply it to. Using a "button" component on our "navMenu" probably won't generate a useful CSS. But applying it to the "navMenu_link" will.

一旦掌握了要点,就可以在可能需要样式的每个类上放置css“钩子”。 只要这些类代表元素的角色,您就不会用完如何命名元素的想法。 完成此操作后,您可以自由将任何DoCSSa组件绑定到它们。 当然,该组件将需要适合您要对其应用的块。 在“ navMenu”上使用“按钮”组件可能不会生成有用CSS。 但是将其应用于“ navMenu_link”即可。

As an exercise, you can try to apply the same kind of naming to the form example used above. The more you do it, the more you'll find the correct balance between generic roles and specific blocks. The classes should stay as semantic and flexible as possible.

作为练习,您可以尝试对上述表单示例应用相同的命名方式。 您做得越多,您就会在通用角色和特定功能块之间找到正确的平衡。 这些类应尽可能保持语义和灵活性。

DoCSSa is all about providing an architecture and methodology front-end web developers can build upon. It should bend to your own specific needs. It should get enriched by your own experience. And most of all it should help you keep your sites maintainable and fast to develop. We're looking forward for your feedback. What do you plan to do with DoCSSa ?

DoCSSa旨在提供前端Web开发人员可以建立的体系结构和方法。 它应该满足您自己的特定需求。 它应该通过您自己的经验来丰富。 最重要的是,它应该可以帮助您保持网站的可维护性和快速开发。 我们期待您的反馈。 您打算如何使用DoCSSa?

翻译自: https://davidwalsh.name/deferred-objects-docssa

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值