bem-site / bem-forum-content-en

Content BEM forum for English speak users
3 stars 0 forks source link

Mixes #4

Open tadatuta opened 9 years ago

tadatuta commented 9 years ago

In the simplest form a block is represented by a single DOM node but generally blocks and DOM nodes are not the same thing.

Mixes are just the way to use several blocks and/or elements on the same DOM node.

That makes possible

For instance, we have a universal block logo (a block with a logotype) that could be used anywhere on a page: in header, inside copy, in the footer, etc. We can specify its width, height and a background image but what we do with its margins that differ from case to case?

Of course, we could use modifiers like logo_type_header or logo_type_content to do so. However it is wrong semantically, because such things should lay down within a scope of parent block’s responsibility. And giving the right to define such things to an inner block contradicts the whole idea of blocks independency.

That's where mixes shine:

<div class="header">
    <div class="header__logo logo"></div>
</div>
.logo
{
    width: 120px;
    height: 60px;
    background-image: url(logo.svg);
}

.header__logo
{
    margin: 12px 26px;
}

As you know there's no global modifiers in BEM methodology but again — mixes gives us possibility to achieve the same result with several different blocks on same DOM node.

Also you may want to use mixes to provide some arbitrary block with JS logic.

A single DOM node can represent:

So try to develop your blocks as small and reusable as possible and then combine them in any way you want to build huge applications!

hudochenkov commented 9 years ago

Can you give some examples of using <div class="menu menu__layout"></div>? I can't imagine any case when block's element need to be on the same node with parent.

qfox commented 9 years ago

@hudochenkov What we using now for tabs:

<div class="tab tab__caption" data-bem='{tab:{id: 1}}'>...</div>
<div class="tab tab__caption" data-bem='{tab:{id: 2}}'>...</div>
<!-- any code -->
<div class="tab tab__body" data-bem='{tab:{id: 1}}'>...</div>
<div class="tab tab__body" data-bem='{tab:{id: 2}}'>...</div>

Each block splitted on 2 dom nodes and has 1 element in each other. Caption listening for user events (e.g. click). And when tab becoming active we add a class to both elements activate it. And vice-versa if tab becoming inactive.

For i-bem.js it can be code like:

DOM.decl('tab', {
  onSetMod: {
    js: function () { // constructor
      this.bindTo(this.elem('caption'), this._onClick);
      this.__self.registerTab(this); // static (shared) storage
    },
    active: function (mod, val) {
      this.setMod(this.elem('caption'), mod, val);
      this.setMod(this.elem('body'), mod, val);
    },
    _onClick: function (e) {
      this._tabs
          .map(function (tab) {
              tab.setMod('active', this === tab);
          }, this);
    }
  }
}, {
  _tabs: [],
  registerTab: function (tab) {
    this._tabs.push(tab);
  }
});

Simple.

qfox commented 9 years ago

@hudochenkov Or if you don't need DOM nodes, but need to split some handlers out:

<div class="b b__click-me">click me</div>
<!-- later: -->
<div class="b"><!-- code --><span class="b__click-me">click me</span><!-- more code --></div>

One JS class, several cases. DRY ONW.

hudochenkov commented 9 years ago

Thank you, this makes sense.

abacaj commented 9 years ago

Interesting...

What if .logo is defined at the bottom or @imported after .header in Sass we run into issues, very common in a large application to import files in the wrong order or when combining blocks, especially with many developers.

The .header__logo styles will not get applied for example:

.header__logo {
  margin: 12px 26px;
}

// imported later wins the margin specificity battle as it comes after .header__logo
.logo {
  margin: 100px;
}

My proposed solution:

// will overwrite .logo no matter where it is
.header__logo.logo {
  margin: 12px 26px;
}

// imported later
.logo { 
 margin: 100px;
}

Is this a viable solution?

tadatuta commented 9 years ago

I think it's a way to go but we use build system (ENB) with special tech to explicitly describe dependencies of a block which also guarantee the order.

hendore commented 9 years ago

I've just discovered this when trying to make a block from an existing block but in an independent way, at first I had the following

<article class="b-blog-post">
    <div class="b-media">
        <img class="b-media__image" src="...">
        <div class="b-media__content">
            <h1 class="b-blog-post__title">Some awesome blog article</h1>
            <div class="b-blog-post__statistics">
                <span class="b-blog-post__stat">12 comments</span>
                <span class="b-blog-post__stat">last updated 3 hours ago</span>
            </div>
        </div>
    </div>
</article>

However, using mixes to join the media and blog-post would result in the following

<article class="b-blog-post b-media">
    <img class="b-media__image" src="...">
    <div class="b-media__content">
        <h1 class="b-blog-post__title">Some awesome blog article</h1>
        <div class="b-blog-post__statistics">
            <span class="b-blog-post__stat">12 comments</span>
            <span class="b-blog-post__stat">last updated 3 hours ago</span>
       </div>
    </div>
</article>

Is this the correct way to approach this? The other alternative is to ignore the existing b-media block and again implement those rules in the b-blog-article block?

tadatuta commented 9 years ago

@hendore sure

ltarasiewicz commented 8 years ago

Hello, I've just joined the BEM forum. I'm really happy to come accross this whole idea. I'm still getting up to speed with it and have one question relating to this thread.

    <div class="content-section__alpha">
        <div class="content-section__step step"> <-- a mix
            <div class="step__image">

            </div>
            <h2 class="step__heading"></h2>
            <div class="step__instruction">

            </div>
        </div>
    </div>

In the above example the element content-section__step as well as the block step are the same node. Within that node I have step__image, step__heading and step__instruction. Is that correct? Or maybe the elements of step should also be marked as elements of content-section so then it would look like this:

    <div class="content-section__alpha">
        <div class="content-section__step step"> <-- a mix
            <div class="content-section__step step__image">

            </div>
            <h2 class="content-section__heading step__heading"></h2>
            <div class=content-section__instruction "step__instruction">

            </div>
        </div>
    </div>
tadatuta commented 8 years ago

@ltarasiewicz Hi! It depends on what exactly you're going to do with step__image, step__heading and step__instruction. Try to imagine it outside of content-section. Block step with all its elements should still look the same. And if in this mental experiment you find out that some of step elements have styles which are just for content-section context (e.g. for positioning inside content-section), you should mix them with content-section elements and move these styles there.

Also take a look at https://en.bem.info/method/key-concepts/#mix and https://en.bem.info/faq/#mixes

ltarasiewicz commented 8 years ago

Hi @tadatuta,

This makes perfect sense. Thanks for this explanation!