/brightspot-base

Primary LanguageLessMIT LicenseMIT


DO NOT USE THIS REPOSITORY. USE BRIGHTSPOT EXPRESS INSTEAD.


Brightspot Base


Stack


Naming

Based on BEM:

  • Block: "A logically and functionally independent page component, the equivalent of a component in Web Components. A block encapsulates behavior (JavaScript), templates, styles (CSS), and other implementation technologies. Blocks being independent allows for their re-use, as well as facilitating the project development and support process."
  • Element: "A constituent part of a block that can't be used outside of it."

Naming Rules

Java-like:

  • PascalCase block names, e.g. ListPromo.
  • Suffix block names with their parent names, e.g. ListPromo which extends Promo.
  • camelCase element names, e.g. title.
  • Prefix element names with their parent names (blocks and elements), separated by a dash, e.g. ListPromo-title, ListPromo-items-item.

Naming Example - Handlebars

<div class="ListPromo">
    <div class="ListPromo-title">{{title}}</div>
    <ul class="ListPromo-items">
        {{#each items}}
            <li class="ListPromo-items-item">{{this}}</li>
        {{/each}}
    </ul>
    <div class="ListPromo-cta">{{cta}}</div>
</div>

Names can be generated automatically via BEM helpers, which are explained later.


Naming Example - Less

.ListPromo {
    border: solid 1px black;
    padding: 1em;

    &-title {
        font-size: 2em;
    }

    &-items {
        list-style: none;
        padding: 0;

        &-item {
            margin-top: .5em;
        }
    }
}

Naming Example - Less Alternative

.ListPromo {
    border: solid 1px black;
    padding: 1em;
}

.ListPromo-title {
    font-size: 2em;
}

.ListPromo-items {
    list-style: none;
    padding: 0;
}

.ListPromo-items-item {
    margin-top: .5em;
}

Only use this style when using Base blocks, not when creating them.


Organization

  • Handlebars, Less, and JavaScript files all together in the same directory, e.g. ArticleMain files in main.
  • One block per file.
  • Flat as possible by minimizing the number of nested directories.
  • Logically group blocks, e.g. all promo blocks in promo.

Usage


Usage Example

To use ListPromo as is, specify it directly in the Styleguide example JSON:

{
    "_template": "ListPromo",
    "title": "foo",
    "items": [ "bar" ],
    "cta": "qux"
}

HTML output:

<div class="ListPromo">
    <div class="ListPromo-title">foo</div>
    <ul class="ListPromo-items">
        <li>bar</li>
    </ul>
    <div class="ListPromo-cta">qux</div>
</div>

Usage Example - Copy

To create a wide version of ListPromo named WideListPromo, use the {{#defineBlock}} helper and reference the path to the template you want to extend. Make sure to define your new defineBlockContainer and/or defineBlockBody in the extending template:

WideListPromo.hbs

{{#defineBlock "WideListPromo" extend="ListPromo.hbs"}}
    {{#defineBlockContainer}}
        {{#defineBlockBody}}
            <div class="{{blockName}}">
                {{element "title"}}
                {{element "items"}}
                {{element "cta"}}
            </div>
        {{/defineBlockBody}}
    {{/defineBlockContainer}}
{{/defineBlock}}

HTML output:

<div class="WideListPromo">
    <div class="WideListPromo-title">...</div>
    <ul class="WideListPromo-items">...</ul>
    <div class="WideListPromo-cta">...</div>
</div>

Usage Example - Reorder

To move the cta element in between the title element and the items element, use the {{element}} helper:

CtaFirstListPromo.hbs

{{#defineBlock "CtaFirstListPromo" extend="ListPromo.hbs"}}
    {{#defineBlockContainer}}
        {{#defineBlockBody}}
            <div class="{{blockName}}">
                {{element "title"}}
                {{element "cta"}}
                {{element "items"}}
            </div>
        {{/defineBlockBody}}
    {{/defineBlockContainer}}
{{/defineBlock}}

HTML output:

<div class="CtaFirstListPromo">
    <div class="CtaFirstListPromo-title">...</div>
    <div class="CtaFirstListPromo-cta">...</div>
    <ul class="CtaFirstListPromo-items">...</ul>
</div>

Usage Example - Add

To add the subTitle element below the title element:

SubTitledListPromo.hbs

{{#defineBlock "SubTitledListPromo" extend="ListPromo.hbs"}}
    {{#defineBlockContainer}}
        {{#defineBlockBody}}
            <div class="{{blockName}}">
                {{element "title"}}
                {{#with subTitle}}
                    <div class="{{blockName}}-subTitle">{{this}}</div>
                {{/with}}
                {{element "items"}}
                {{element "cta"}}
            </div>
        {{/defineBlockBody}}
    {{/defineBlockContainer}}
{{/defineBlock}}

HTML output:

<div class="SubTitledListPromo">
    <div class="SubTitledListPromo-title">...</div>
    <div class="SubTitledListPromo-subTitle">...</div>
    <ul class="SubTitledListPromo-items">...</ul>
    <div class="SubTitledListPromo-cta">...</div>
</div>

Usage Example - Style All

Change all promo titles to 3em:

All.less

@import 'base/promo/Promo';
@import 'promo/Promo';
@import 'base/promo/ImagePromo';
@import 'base/promo/ListPromo';

promo/Promo.less

.Promo-title {
    font-size: 3em;
}

Usage Example - Style One

Change just list promo titles to 3em:

All.less

@import 'base/promo/Promo';
@import 'base/promo/ImagePromo';
@import 'base/promo/ListPromo';
@import 'promo/ListPromo';

promo/ListPromo.less

.ListPromo-title {
    font-size: 3em;
}

Creating Base Blocks


Handlebars Example - Not Reusable

<div class="ListPromo">
    <div class="ListPromo-title">{{title}}</div>
    <ul class="ListPromo-items">
        {{#each items}}
            <li class="ListPromo-items-item">{{this}}</li>
        {{/each}}
    </ul>
    <div class="ListPromo-cta">{{cta}}</div>
</div>

Handlebars - Reusability

BEM helpers:

  • defineBlock: Defines a block.
  • blockName: Returns the current block name.
  • defineBlockContainer: Marks the template as the block container.
  • defineBlockBody: Marks the template as the block body.
  • defineElement: Defines an element within a block.
  • elementName: Returns the current element name.
  • element: Renders the named element.

Handlebars Example - Reusable

{{#defineBlock "ListPromo"}}
    {{#defineElement "title"}}
        <div class="{{elementName}}">{{this}}</div>
    {{/defineElement}}

    {{#defineElement "items"}}
        <ul class="{{elementName}}">
            {{#each this}}
                <li class="{{elementName}}-item">{{this}}</li>
            {{/each}}
        </ul>
    {{/defineElement}}

    {{#defineElement "cta"}}
        <div class="{{elementName}}">{{this}}</div>
    {{/defineElement}}

    {{#defineBlockContainer}}
        {{#defineBlockBody}}
            <div class="{{blockName}}">
                {{element "title"}}
                {{element "items"}}
                {{element "cta"}}
            </div>
        {{/defineBlockBody}}
    {{/defineBlockContainer}}
{{/defineBlock}}

Less Example - Parent

Promo.less

.Promo {
    &-title {
        &:extend(.Promo-title all);
        font-size: 2em;
    }

    &-cta {
        &:extend(.Promo-cta all);
        border: 5px solid black;
    }
}

Less Example - Simple Extend

ImagePromo.less

.ImagePromo {
    &:extend(.Promo all);
}

Less Example - Extend & Add

ListPromo.less

.ListPromo {
    &:extend(.Promo all);

    &-items {
        &:extend(.ListPromo-items all);
        list-style: none;
        padding: 0;

        &-item {
            &:extend(.ListPromo-items-item all);
            margin-top: .5em;
        }
    }
}

Bonus


Save yourself time and keystrokes when writing Base handlebars helpers in your favorite editor.