bem-constructor 0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 8d21417bbbbf72744f4f58dbf75f61313f4f0c0f
4
+ data.tar.gz: 4e6598835ffaaac7ed06cb3cfea6d8b9f14b0e7c
5
+ SHA512:
6
+ metadata.gz: b6ea8f0194709514cf397580c63858d5c5a4ea54e2e2d9f3db9de0f049db8dcf1df6399267aa19a96020d6b022a16aafbe9da568d45748bf04fc5c2a0e9e988d
7
+ data.tar.gz: 6e1f86e490a5d18ece7bfc4d9f68f9402b42de7f6595220fb0623f66daac14350a7c09db82f1ebceb2458c241f894d7b6063dc7bc9f512f0c223c519a382ddb5
data/CHANGELOG.md ADDED
@@ -0,0 +1,3 @@
1
+ ## 0.1.0
2
+
3
+ * Initial release
data/README.md ADDED
@@ -0,0 +1,282 @@
1
+ # BEM Constructor
2
+
3
+ BEM Constructor is a Sass library for building immutable and namespaced BEM-style CSS objects.
4
+
5
+ By enforcing a consistent and programatic way of defining objects (blocks, elements and modifiers) it ensures a more structured, robust and secure object codebase that is easy to understand and maintain. Objects defined using the constructor are impossible to modify and reassign by mistake or omission.
6
+
7
+ Jump to [🍔 The Burger Example™](#example) to see the mixins in action.
8
+
9
+ ----
10
+
11
+ ## Key ideas
12
+
13
+ The key ideas behind this library are well explained by Harry Roberts in his articles [Immutable CSS](http://csswizardry.com/2015/03/immutable-css/), [More Transparent UI Code with Namespaces](http://csswizardry.com/2015/03/more-transparent-ui-code-with-namespaces/) and [MindBEMding – getting your head ’round BEM syntax](http://csswizardry.com/2013/01/mindbemding-getting-your-head-round-bem-syntax/),
14
+
15
+ ### 1. Immutability
16
+
17
+ Some CSS objects in your project shouldn't be able to change (mutate). They have a very specific role and you need to make sure they're not reassigned somewhere else in your codebase. In order to ensure immutability you'll need three things: a way of defining those objects, a way of recognising them and a way to guarantee you won't be able to modify them later on. By constructing objects programatically you can be confident that they are assigned once and just once.
18
+
19
+ ### 2. Namespacing
20
+
21
+ Objects have a clear function. Whethere they are components, utilities o dirty hacks, we need a consistent way of telling them apart. By namespacing objects, our UI code becomes more transparent and understandable. BEM Constructor supports the following object types:
22
+
23
+ - Objects
24
+ - Components
25
+ - Utilities
26
+ - Themes
27
+ - States
28
+ - Scopes
29
+ - Hacks
30
+
31
+ Read [Harry's post on namespaces](http://csswizardry.com/2015/03/more-transparent-ui-code-with-namespaces/) to get a deep look at why and how they are used.
32
+
33
+
34
+ ### 3. BEM structure
35
+
36
+ BEM objects are composed of a block and any number of elements and/or modifiers. Using the BEM syntax for naming classes you'll produce structured code that helps you and other developers understand at a glance the relationship between those classes. The BEM constructor takes care of generating bem-compliant selectors.
37
+
38
+ ----
39
+
40
+ ## Installation
41
+
42
+ There are 3 ways of installing BEM Constructor:
43
+
44
+ ### Download
45
+
46
+ Download the Sass files in [stylesheets/](/stylesheets/) and place them in your Sass directory.
47
+
48
+ ### Bower
49
+
50
+ Run the following command:
51
+
52
+ bower install --save-dev bem-constructor
53
+
54
+ ### Compass extension
55
+
56
+ 1. `gem install bem-constructor`
57
+ 2. Add `require 'bem-constructor'` to your `config.rb`
58
+
59
+ ----
60
+
61
+ ## Usage
62
+
63
+ Import it into your main stylesheet:
64
+
65
+ @import 'bem-constructor';
66
+
67
+ ### block($name, $type)
68
+
69
+ Constructs a new block element of a given type. `$type` being one of: `'object'`, `'component'` or `'utility'`.
70
+
71
+ @include block($name, $type) { ... }
72
+
73
+
74
+ #### object($name)
75
+
76
+ A shortcut for `block($name, $type: 'object')`
77
+
78
+ #### component($name)
79
+
80
+ A shortcut for `block($name, $type: 'component')`
81
+
82
+ #### utility($name)
83
+
84
+ A shortcut for `block($name, $type: 'utility')`
85
+
86
+
87
+ ### element($name...)
88
+
89
+ Creates a new element of the parent block. It should always be nested within a block constructor. You can create multiple elements at once by passing a comma separated list (Arglist) of element names.
90
+
91
+ @include element($name...) { ... }
92
+
93
+
94
+ ### modifier($name...)
95
+
96
+ Creates a new modifier of the parent block or element. Should always be nested within a block or element constructor. You can declare multiple modifiers at once by passing a comma separated list (Arglist) of modifier names.
97
+
98
+ @include modifier($name...) { ... }
99
+
100
+
101
+ ### modifies-element($modified-elements...)
102
+
103
+ When declaring a block modifier, a state you may need to target and modify some of the block elements too. Use the following mixin to scope the ruleset to those elements.
104
+
105
+ @include modifies-element($modified-elements...) { ... }
106
+
107
+
108
+ ### theme($themes...)
109
+
110
+ Style your objects given a parent theme class.
111
+
112
+ @include theme($themes...) { ... }
113
+
114
+ ### state($states...)
115
+
116
+ Modifies objects by appending a state class
117
+
118
+ @include state($states...) { ... }
119
+
120
+ ### hack()
121
+
122
+ Signals that the following code is a hack.
123
+
124
+ @include hack() { ... }
125
+
126
+
127
+ ### scope($name)
128
+
129
+ Creates a new scope
130
+
131
+ Scopes allow you to isolate code you don't have control over.
132
+
133
+ @include scope($name) { ... }
134
+
135
+
136
+ ----
137
+
138
+ ## Options
139
+
140
+ ### Namespaces
141
+
142
+ Switch namespaces on/off by setting the following variable:
143
+
144
+ $bem-use-namespaces: false; // defaults to true
145
+
146
+ Override the default block namespaces:
147
+
148
+ $bem-block-namespaces: (
149
+ 'object': 'obj', // defaults to 'o'
150
+ 'component': 'comp', // defaults to 'c'
151
+ 'utility': 'helper', // defaults to 'u'
152
+ );
153
+
154
+ Override the default theme namespace:
155
+
156
+ $bem-theme-namespace: 'theme'; // defaults to 't'
157
+
158
+ Override the default state namespace:
159
+
160
+ $bem-state-namespace: 'has'; // defaults to 'is'
161
+
162
+ Override the default hack namespace:
163
+
164
+ $bem-hack-namespace: 'it-wasnt-me-'; // defaults to '_'
165
+
166
+
167
+
168
+ ### BEM separators
169
+
170
+ By default BEM Constructor uses the following BEM convention:
171
+ - Two underscores (__) for elements
172
+ - Two hyphens for modifiers (--).
173
+
174
+ You can customize them to whatever fits you needs:
175
+
176
+ $bem-element-separator: '-'; // Defaults to '__'
177
+
178
+ $bem-modifier-separator: '-_-_'; // Defaults to '--'
179
+
180
+
181
+
182
+ ----
183
+
184
+ ##<a name="example"></a> 🍔 The Burger Example™
185
+
186
+
187
+ *Disclaimer: the following Sass code may not compile into a real burger.*
188
+
189
+ ```` scss
190
+
191
+ @include object('burger') {
192
+ texture: juicy;
193
+
194
+ @include element('lettuce', 'tomato') {
195
+ quality: fresh;
196
+ }
197
+
198
+ @include element('cheese') {
199
+ type: gouda;
200
+
201
+ @include modifier('parmigiano') {
202
+ type: parmigiano;
203
+ }
204
+ }
205
+
206
+ @include element('extra-topping') {
207
+ ingredient: bacon;
208
+ }
209
+
210
+ @include element('meat') {
211
+ type: beef;
212
+ }
213
+
214
+ @include modifier('veggie') {
215
+ texture: smooth;
216
+
217
+ @include modifies-element('meat') {
218
+ type: lentils;
219
+ }
220
+
221
+ @include modifies-element('extra-topping') {
222
+ ingredient: avocado;
223
+
224
+ @include hack() {
225
+ ingredient: bacon;
226
+ }
227
+ }
228
+ }
229
+
230
+ @include theme('mexican') {
231
+ spicy: hell-yeah;
232
+ }
233
+
234
+ @include state('cold') {
235
+ taste: terrible;
236
+ }
237
+ }
238
+
239
+ The compiled CSS:
240
+
241
+ ```` css
242
+
243
+ /* The main Burger object */
244
+ .o-burger { texture: juicy; }
245
+
246
+ /* lettuce and Tomato elements */
247
+ .o-burger__lettuce, .o-burger__tomato { quality: fresh; }
248
+
249
+ /* Cheese element */
250
+ .o-burger__cheese { type: gouda; }
251
+
252
+ /* Cheese modifier */
253
+ .o-burger__cheese--parmigiano { type: parmigiano; }
254
+
255
+ /* Extra topping element */
256
+ .o-burger__extra-topping { ingredient: bacon; }
257
+
258
+ /* Meat element */
259
+ .o-burger__meat { type: beef; }
260
+
261
+ /* Veggie Burger block modifier */
262
+ .o-burger--veggie { texture: smooth; }
263
+
264
+ /* Veggie Burger block modifier modifies the Meat element too */
265
+ .o-burger--veggie .o-burger__meat { type: lentils; }
266
+
267
+ /* Veggie Burger block modifier modifies the Extra Topping element too */
268
+ .o-burger--veggie .o-burger__extra-topping { ingredient: avocado; }
269
+
270
+ /* But we are hackers and couldn't resist adding some Bacon back */
271
+ ._o-burger--veggie .o-burger__extra-topping { ingredient: bacon; }
272
+
273
+ /* When the party Theme is Mexican, we make everything spicy */
274
+ .t-mexican .o-burger { spicy: hell-yeah; }
275
+
276
+ /* And we're all sad when the burge Is Cold */
277
+ .o-burger.is-cold { taste: terrible; }
278
+
279
+
280
+ ## This is overkill, who is this for?
281
+
282
+ If constructing objects programatically seems too verbose or abstract to you that's perfectly OK. This tool is not for everybody. However if you need to enforce a strict way of writing BEM objects in your project, want to make sure they won't mutate and thus produce more secure CSS, then this tool might help you.
@@ -0,0 +1,8 @@
1
+ require 'compass'
2
+ extension_path = File.expand_path(File.join(File.dirname(__FILE__), ".."))
3
+ Compass::Frameworks.register('bem-constructor', :path => extension_path)
4
+
5
+ module QuantityQueries
6
+ VERSION = "0.1"
7
+ DATE = "2015-03-24"
8
+ end
@@ -0,0 +1,37 @@
1
+ // -----------------------------------------------------------------------------
2
+ // BEM constructor
3
+ // -----------------------------------------------------------------------------
4
+ // Table of contents
5
+ // 1. Default settings
6
+ // 2. Imports
7
+
8
+ // -----------------------------------------------------------------------------
9
+ // 1. Default settings
10
+ // -----------------------------------------------------------------------------
11
+
12
+ /// Use namespaced class names
13
+ /// @public
14
+ $bem-use-namespaces: true !default;
15
+
16
+ /// Bem style element separator
17
+ /// @public
18
+ $bem-element-separator: '__' !default;
19
+
20
+ /// Bem style modifier separator
21
+ /// @public
22
+ $bem-modifier-separator: '--' !default;
23
+
24
+ // -----------------------------------------------------------------------------
25
+ // 2. Imports
26
+ // -----------------------------------------------------------------------------
27
+
28
+ @import 'logger';
29
+ @import 'error-checks';
30
+ @import 'block';
31
+ @import 'element';
32
+ @import 'modifier';
33
+ @import 'modifies-element';
34
+ @import 'scope';
35
+ @import 'theme';
36
+ @import 'state';
37
+ @import 'hack';
@@ -0,0 +1,91 @@
1
+ // -----------------------------------------------------------------------------
2
+ // Block constructor
3
+ // -----------------------------------------------------------------------------
4
+
5
+ /// Set namespaces for each block type
6
+ /// @public
7
+
8
+ $bem-block-namespaces: (
9
+ 'utility': 'u',
10
+ 'object': 'o',
11
+ 'component': 'c',
12
+ ) !default;
13
+
14
+ /// Initializes a new block object
15
+ /// @private
16
+ /// @param {String} $block - Name for the new block
17
+ /// @param {String} $type - Block type: (utility, object or component)
18
+ /// @returns The final selector for the new block object
19
+
20
+ @function _block($name, $type) {
21
+
22
+ // Log new block
23
+ $new-block: _bem-log-block($name);
24
+
25
+ // Error check
26
+ $outside-check: should-not-be-called-within('scope', 'block');
27
+
28
+ // Set namespace
29
+ $namespace: '';
30
+
31
+ @if $bem-use-namespaces {
32
+ @if not map-has-key($bem-block-namespaces, $type) {
33
+ @error '`#{$type}` is not a valid `$type` for `block()`';
34
+ }
35
+ $namespace: map-get($bem-block-namespaces, $type) + '-';
36
+ }
37
+
38
+ $selector: '.' + $namespace + $name;
39
+ $set-current: set-current-context('block', $name, $selector);
40
+
41
+ @return $selector;
42
+ }
43
+
44
+
45
+ /// Creates a block object with the given type
46
+ /// @param {String} $block - Name for the new block
47
+ /// @param {String} $type - Block type: (utility, object or component)
48
+
49
+ @mixin block($name, $type) {
50
+
51
+ // Write block selector
52
+ @at-root #{_block($name, $type)} {
53
+ @content;
54
+ }
55
+
56
+ // Clear $_bem-current-context block after creation
57
+ $unset-current: unset-current-context('block');
58
+ }
59
+
60
+
61
+ // -----------------------------------------------------------------------------
62
+ // 2. Utility alias
63
+ // -----------------------------------------------------------------------------
64
+
65
+ @mixin utility($name) {
66
+ @include block($name, 'utility') {
67
+ @content;
68
+ }
69
+ }
70
+
71
+
72
+ // -----------------------------------------------------------------------------
73
+ // 3. Object alias
74
+ // -----------------------------------------------------------------------------
75
+
76
+ @mixin object($name) {
77
+ @include block($name, 'object') {
78
+ @content;
79
+ }
80
+ }
81
+
82
+
83
+ // -----------------------------------------------------------------------------
84
+ // 4. Component alias
85
+ // -----------------------------------------------------------------------------
86
+
87
+ @mixin component($name) {
88
+ @include block($name, 'component') {
89
+ @content;
90
+ }
91
+ }
@@ -0,0 +1,43 @@
1
+ // ----------------------------------------------------------------------
2
+ // Element constructor
3
+ // ----------------------------------------------------------------------
4
+
5
+ /// Initializes a new element for the current block
6
+ /// @private
7
+ /// @param {String | Arglist} $elements - List of new element names
8
+ /// @returns The final selector for the new element(s)
9
+
10
+ @function _element($elements...) {
11
+
12
+ // Log new element(s)
13
+ $new-element: _bem-log-element($elements...);
14
+
15
+ // Error checks
16
+ $inside-check: should-be-called-within('block');
17
+ $outside-check: should-not-be-called-within('modifier', 'state', 'element');
18
+
19
+ $selector: ();
20
+
21
+ @each $element in $elements {
22
+ $e: #{&}#{$bem-element-separator}#{$element};
23
+ $selector: append($selector, $e, 'comma');
24
+ }
25
+
26
+ $set-current: set-current-context('element', $elements, $selector);
27
+
28
+ @return $selector;
29
+ }
30
+
31
+
32
+ /// Creates new element(s)
33
+ /// @param {String | Arglist} $elements - Name of the new element(s)
34
+
35
+ @mixin element($elements...) {
36
+
37
+ @at-root #{_element($elements...)} {
38
+ @content;
39
+ }
40
+
41
+ // Clear $_bem-current-context element after creation
42
+ $unset-current: unset-current-context('element');
43
+ }
@@ -0,0 +1,50 @@
1
+ // -----------------------------------------------------------------------------
2
+ // Error checks
3
+ // -----------------------------------------------------------------------------
4
+ // Table of contents:
5
+ // 1. Within
6
+ // 2. Outside
7
+
8
+ // -----------------------------------------------------------------------------
9
+ // 1. Within
10
+ // -----------------------------------------------------------------------------
11
+
12
+ /// Checks that it's being created within all of the passed $objs...
13
+ @function _should-be-called-within($objs...) {
14
+
15
+ $found: false;
16
+
17
+ @each $obj in $objs {
18
+ @if map-get($_bem-current-context, $obj) != null {
19
+ $found: true;
20
+ }
21
+ }
22
+
23
+ @if not $found {
24
+ @error 'It should be called within #{inspect($objs)}';
25
+ }
26
+
27
+ @return true;
28
+ }
29
+
30
+
31
+ // -----------------------------------------------------------------------------
32
+ // 2. Outside
33
+ // -----------------------------------------------------------------------------
34
+
35
+ /// Checks that it's being created outside all of the passed $objs...
36
+ @function _should-not-be-called-within($objs...) {
37
+
38
+ $found: false;
39
+
40
+ @each $obj in $objs {
41
+ @if map-get($_bem-current-context, $obj) != null {
42
+ $found: true;
43
+ }
44
+ }
45
+ @if $found {
46
+ @error 'It should not be called within #{inspect($objs)}';
47
+ }
48
+
49
+ @return true;
50
+ }
@@ -0,0 +1,38 @@
1
+ // -----------------------------------------------------------------------------
2
+ // 11. Hack constructor
3
+ // -----------------------------------------------------------------------------
4
+
5
+ /// Hack namespace prepended to the selector
6
+ $hack-namespace: '_' !default;
7
+
8
+ @function _hack() {
9
+
10
+ // You may not hack a hack
11
+ $recursive-check: should-not-be-called-recursively('hack');
12
+
13
+ $selector: ();
14
+ $namespace: if($bem-use-namespaces, $hack-namespace, '');
15
+
16
+ @each $s in & {
17
+ $selector-to-str: inspect(nth($s, 1));
18
+ $selector-without-dot: str-slice($selector-to-str, 2, -1);
19
+ $new-selector: '.' + $namespace + $selector-without-dot;
20
+ $sl: selector-replace($s, nth($s, 1), $new-selector);;
21
+ $selector: append($selector, $sl, 'comma');
22
+ }
23
+
24
+ $set-current: set-current-context('hack', 'some-hack', $selector);
25
+
26
+ @return $selector;
27
+
28
+ }
29
+
30
+ @mixin hack() {
31
+
32
+ @at-root #{_hack()} {
33
+ @content;
34
+ }
35
+
36
+ $unset-current: unset-current-context('hack');
37
+
38
+ }
@@ -0,0 +1,7 @@
1
+ // -----------------------------------------------------------------------------
2
+ // Logger
3
+ // -----------------------------------------------------------------------------
4
+
5
+ @import '_block';
6
+ @import '_element';
7
+ @import '_modifier';
@@ -0,0 +1,12 @@
1
+ // -----------------------------------------------------------------------------
2
+ // Logger
3
+ // -----------------------------------------------------------------------------
4
+
5
+ /// Stores the whole BEM structure
6
+ $_bem-log: () !global;
7
+
8
+ @import 'logger/_context-logger';
9
+ @import 'logger/_block-logger';
10
+ @import 'logger/_element-logger';
11
+ @import 'logger/_modifier-logger';
12
+ @import 'logger/_scope-logger';
@@ -0,0 +1,49 @@
1
+ // ----------------------------------------------------------------------
2
+ // Modifier constructor
3
+ // ----------------------------------------------------------------------
4
+
5
+ /// Initializes a new modifier for the current block or element(s)
6
+ /// @private
7
+ /// @param {String | Arglist} $modifiers - List of new modifier names
8
+ /// @returns The final selector for the new modifier(s)
9
+
10
+
11
+ @function _modifier($modifiers...) {
12
+
13
+ // Log new modifier(s)
14
+ $new-modifier: _bem-log-modifier($modifiers...);
15
+
16
+ // Error checks
17
+ $inside-check: _should-be-called-within('block');
18
+ $outside-check: _should-not-be-called-within('modifier');
19
+
20
+ $selector: ();
21
+
22
+ @each $modifier in $modifiers {
23
+ $new-selector: ();
24
+
25
+ @each $sel in & {
26
+ $modified-selector: #{$sel}#{$bem-modifier-separator}#{$modifier};
27
+ $new-selector: append($new-selector, $modified-selector, 'comma');
28
+ }
29
+
30
+ $selector: append($selector, $new-selector, 'comma');
31
+ }
32
+
33
+ $set-current: set-current-context('modifier', $modifiers, $selector);
34
+
35
+ @return $selector;
36
+ }
37
+
38
+
39
+ /// Creates new modifier(s)
40
+ /// @param {String | Arglist} $modifiers - Name of the new modifier(s)
41
+
42
+ @mixin modifier($modifiers...) {
43
+
44
+ @at-root #{_modifier($modifiers...)} {
45
+ @content;
46
+ }
47
+
48
+ $unset-current: unset-current-context('modifier');
49
+ }
@@ -0,0 +1,37 @@
1
+ // ----------------------------------------------------------------------
2
+ // Element modifier
3
+ // ----------------------------------------------------------------------
4
+
5
+ /// Scopes the @content ruleset to an element of the block being modified
6
+ /// @private
7
+ /// @param {String | Arglist} $modified-elements - List of elements that should be modified
8
+ /// @returns The final selector for the element(s) modified by the block modifier
9
+
10
+ @function _modifies-element($modified-element...) {
11
+
12
+ $inside-check: should-be-called-within('modifier', 'state');
13
+ $outside-check: should-not-be-called-within('element');
14
+
15
+ $selectors: ();
16
+
17
+ @each $element in $modified-element {
18
+ $element: map-get(map-get($_bem-current-context, 'block'), 'selector') + $bem-element-separator + $element;
19
+ $selectors: append($selectors, $element, 'comma');
20
+ }
21
+
22
+ $selector: selector-nest(&, $selectors);
23
+
24
+ $set-current: set-current-context('modifies-element', $modified-element, $selector);
25
+
26
+ @return $selector;
27
+ }
28
+
29
+
30
+ /// Scopes the @content ruleset to an element of the block being modified
31
+ /// @param {String | Arglist} $modified-elements - Name of the element(s) that should be modified
32
+
33
+ @mixin modifies-element($modified-elements...) {
34
+ @at-root #{_modifies-element($modified-elements...)} {
35
+ @content;
36
+ }
37
+ }
@@ -0,0 +1,40 @@
1
+ // -----------------------------------------------------------------------------
2
+ // Scope constructor
3
+ // -----------------------------------------------------------------------------
4
+
5
+ /// Set namespace for scopes
6
+ /// @public
7
+
8
+ $bem-scope-namespace: 's';
9
+
10
+ /// Initializes a new scope object
11
+ /// @private
12
+ /// @param {String} $scope - Name for the new scope
13
+ /// @returns The final selector for the new scope object
14
+
15
+ @function _scope($scope) {
16
+
17
+ // Log new block
18
+ $new-scope: _bem-log-scope($scope);
19
+
20
+ // Check it's not called inside another scope
21
+ // and it's called at the root-level
22
+ $recursive-check: should-not-be-called-recursively('scope');
23
+ $outside-check: should-not-be-called-within('block');
24
+
25
+ $namespace: if($bem-use-namespaces, $bem-scope-namespace + '-', '');
26
+ $selector: '.' + $namespace + $scope;
27
+
28
+ $set-current: set-current-context('scope', $scope, $selector);
29
+
30
+ @return $selector;
31
+ }
32
+
33
+ @mixin scope($scope) {
34
+
35
+ @at-root #{_scope($scope)} {
36
+ @content;
37
+ }
38
+
39
+ $unset-current: unset-current-context('scope');
40
+ }
@@ -0,0 +1,28 @@
1
+ // -----------------------------------------------------------------------------
2
+ // State constructor
3
+ // -----------------------------------------------------------------------------
4
+
5
+ $state-namespace: 'is' !default;
6
+
7
+ @function _state($states...) {
8
+ $selector: ();
9
+ $namespace: if($bem-use-namespaces, $state-namespace + '-', '');
10
+
11
+ @each $state in $states {
12
+ $s: selector-append(&, '.#{$namespace}#{$state}');
13
+ $selector: append($selector, $s, 'comma');
14
+ }
15
+
16
+ $set-current: set-current-context('state', $states, $selector);
17
+
18
+ @return $selector;
19
+ }
20
+
21
+ @mixin state($states...) {
22
+
23
+ @at-root #{_state($states...)} {
24
+ @content;
25
+ }
26
+
27
+ $unset-state: unset-current-context('state');
28
+ }
@@ -0,0 +1,33 @@
1
+ // -----------------------------------------------------------------------------
2
+ // Theme constructor
3
+ // -----------------------------------------------------------------------------
4
+
5
+ $bem-theme-namespace: 't' !default;
6
+
7
+ @function _theme($themes...) {
8
+
9
+ // If you try to hack a hack you can break the internet.
10
+ // So please, no one try it.
11
+ $recursive-test: should-not-be-called-recursively('theme');
12
+
13
+ $selector: ();
14
+ $namespace: if($bem-use-namespaces, $bem-theme-namespace + '-', '');
15
+
16
+ @each $theme in $themes {
17
+ $t: selector-nest('.#{$namespace}#{$theme}', &);
18
+ $selector: append($selector, $t, 'comma');
19
+ }
20
+
21
+ $set-current: set-current-context('theme', $themes, $selector);
22
+
23
+ @return $selector;
24
+ }
25
+
26
+ @mixin theme($themes...) {
27
+
28
+ @at-root #{_theme($themes...)} {
29
+ @content;
30
+ }
31
+
32
+ $unset-current: unset-current-context('theme');
33
+ }
@@ -0,0 +1,30 @@
1
+ // -----------------------------------------------------------------------------
2
+ // Block Logger
3
+ // -----------------------------------------------------------------------------
4
+
5
+ /// Find if a given $block has already been created
6
+ /// @param {String} $block - Name of the block
7
+
8
+ @function block-exists($block) {
9
+ @return map-has-key($_bem-log, $block);
10
+ }
11
+
12
+ /// Log the new $block
13
+ /// @param {String} $block - Block name
14
+
15
+ @function _bem-log-block($block) {
16
+
17
+ // Check if the block has already been created
18
+ @if block-exists($block) {
19
+ @error '`#{$block}` block has already been created';
20
+ }
21
+
22
+ // Initialize a new block map
23
+ $new-block: ($block: ('elements': (), 'modifiers': ()));
24
+
25
+ // Update bem log with new block
26
+ $_bem-log: map-merge($_bem-log, $new-block) !global;
27
+
28
+ // Everything OK
29
+ @return true;
30
+ }
@@ -0,0 +1,40 @@
1
+ // -----------------------------------------------------------------------------
2
+ // Context logger
3
+ // -----------------------------------------------------------------------------
4
+ // Table of contents:
5
+ // 1. Store current context
6
+ // 2. Clear current context
7
+
8
+
9
+ /// Used to stores the current object being constructed
10
+ /// @private
11
+
12
+ $_bem-current-context: () !global;
13
+
14
+
15
+ // -----------------------------------------------------------------------------
16
+ // 1. Store current context
17
+ // -----------------------------------------------------------------------------
18
+
19
+ /// Sets the current object, stores name and generated selector
20
+
21
+ @function set-current-context($obj, $name, $selector) {
22
+ $new-current: (#{$obj}: (name: $name, selector: $selector));
23
+ $_bem-current-context: map-merge($_bem-current-context, $new-current) !global;
24
+
25
+ @return $selector;
26
+ }
27
+
28
+
29
+ // -----------------------------------------------------------------------------
30
+ // 2. Clear current context
31
+ // -----------------------------------------------------------------------------
32
+
33
+ /// Clears the current object
34
+
35
+ @function unset-current-context($obj) {
36
+ $new-current: (#{$obj}: null);
37
+ $_bem-current-context: map-merge($_bem-current-context, $new-current) !global;
38
+
39
+ @return null;
40
+ }
@@ -0,0 +1,59 @@
1
+ // -----------------------------------------------------------------------------
2
+ // Element Logger
3
+ // -----------------------------------------------------------------------------
4
+
5
+ /// Find if the given $elements have already been created
6
+ /// @param {Arglist | String} $elements - A single or multiple element names
7
+
8
+ @function element-exists($elements...) {
9
+
10
+ // Get the current block name
11
+ // Then get the current block map
12
+ // Then get the current block element map
13
+ $current-block-name: map-get(map-get($_bem-current-context, 'block'), 'name');
14
+ $current-block: map-get($_bem-log, $current-block-name);
15
+ $current-elements: map-get($current-block, 'elements');
16
+
17
+ @each $element in $elements {
18
+ @if map-has-key($current-elements, $element) {
19
+ @return true;
20
+ }
21
+ }
22
+
23
+ @return false;
24
+ }
25
+
26
+
27
+ /// Log the new $elements
28
+ /// @param {Arglist | String} $elements - A single or multiple element names
29
+
30
+ @function _bem-log-element($elements...) {
31
+
32
+ // Check any $elements has already been defined for the current block
33
+ @if element-exists($elements...) {
34
+ @error 'One or more elements from `#{inspect($elements)}` have already been created';
35
+ }
36
+
37
+ // Find the current block name
38
+ // Then get the map for the current block
39
+ // Then get the element list
40
+ $current-block-name: map-get(map-get($_bem-current-context, 'block'), 'name');
41
+ $current-block: map-get($_bem-log, $current-block-name);
42
+ $current-elements: map-get($current-block, 'elements');
43
+
44
+ // For each possible name in $name
45
+ // Create an updated block map
46
+ // Add it to the list of elements
47
+ @each $element in $elements {
48
+ $updated: ($element: ('modifiers': ()));
49
+ $current-elements: map-merge($current-elements, $updated);
50
+ }
51
+
52
+ // Update the block
53
+ $updated-block: ($current-block-name: ('elements': ($current-elements), 'modifiers': map-get($current-block, 'modifiers')));
54
+
55
+ // Update the log
56
+ $_bem-log: map-merge($_bem-log, $updated-block) !global;
57
+
58
+ @return true;
59
+ }
@@ -0,0 +1,94 @@
1
+ // -----------------------------------------------------------------------------
2
+ // Modifier Logger
3
+ // -----------------------------------------------------------------------------
4
+
5
+ /// Find if the given $modifiers have already been created
6
+ /// @param {Arglist | String} $modifiers - A single or multiple modifier names
7
+
8
+ @function modifier-exists($modifiers...) {
9
+
10
+ // Get the current block name
11
+ // Then get the current block map
12
+ // Then get the current block modifiers map
13
+ $current-block-name: map-get(map-get($_bem-current-context, 'block'), 'name');
14
+ $current-block: map-get($_bem-log, $current-block-name);
15
+ $current-modifiers: map-get($current-block, 'modifiers');
16
+
17
+ @each $modifier in $modifiers {
18
+ @if map-has-key($current-modifiers, $modifier) {
19
+ @return true;
20
+ }
21
+ }
22
+
23
+ @return false;
24
+ }
25
+
26
+ /// Log the new $modifiers
27
+ /// @param {Arglist | String} $modifiers - A single or multiple modifier names
28
+
29
+
30
+ @function _bem-log-modifier($modifiers...) {
31
+
32
+ // Check if the modifier has already been defined for the current block or element
33
+ @if modifier-exists($modifiers...) {
34
+ @error 'One or more elements from `#{inspect($modifiers)}` have already been created';
35
+ }
36
+
37
+ // Find the current block name
38
+ $current-block-name: map-get(map-get($_bem-current-context, 'block'), 'name');
39
+ $current-item-name: $current-block-name;
40
+
41
+ // Get the map for the current block
42
+ $current-block: map-get($_bem-log, $current-block-name);
43
+ $current-item: $current-block;
44
+
45
+ // Get the map for the current block modifiers
46
+ $current-block-modifiers: map-get($current-block, 'modifiers');
47
+ $current-item-modifiers: $current-block-modifiers;
48
+
49
+ // Check whether the current context is a block or an element
50
+ $context-type: if(map-get($_bem-current-context, 'element') == null, 'block', 'element');
51
+
52
+ // Update item modifier list if within an Element
53
+ @if $context-type == 'element' {
54
+ // @todo: should work if there are multiple current items
55
+ $current-item-name: nth(map-get(map-get($_bem-current-context, 'element'), 'name'),1);
56
+ $current-item: map-get(map-get($current-block, 'elements'), $current-item-name);
57
+ $current-item-modifiers: map-get($current-item, 'modifiers');
58
+ }
59
+
60
+ // For each possible name in $name
61
+ @each $modifier in $modifiers {
62
+
63
+ // Create an updated block/element map
64
+ $updated: ();
65
+
66
+ @if $context-type == 'element' {
67
+ $updated: (#{$modifier}: ('modified-by': ()))
68
+ } @else {
69
+ $modifies-element: map-get($_bem-current-context, 'modifies-element');
70
+ $updated: (#{$modifier}: ('modifies-element': ()));
71
+ }
72
+
73
+ // Add it to the list of modifiers
74
+ $current-item-modifiers: map-merge($current-item-modifiers, $updated);
75
+ }
76
+
77
+ $updated-block: ();
78
+
79
+ @if $context-type == 'element' {
80
+ // update the element map;
81
+ $updated-item: (#{$current-item-name}: ('modifiers': $current-item-modifiers));
82
+ // @error $updated-item;
83
+ $updated-elements: map-merge(map-get($current-block, 'elements'), $updated-item);
84
+ $updated-block: (#{$current-block-name}: ('modifiers': map-get($current-block, 'modifiers'), 'elements': $updated-elements));
85
+ } @else {
86
+ $updated-block: (#{$current-block-name}: ('modifiers': ($current-item-modifiers), 'elements': map-get($current-block, 'elements')));;
87
+ }
88
+
89
+ // // Update the log
90
+ $_bem-log: map-merge($_bem-log, $updated-block) !global;
91
+
92
+ @return true;
93
+
94
+ }
@@ -0,0 +1,30 @@
1
+ // -----------------------------------------------------------------------------
2
+ // Scope Logger
3
+ // -----------------------------------------------------------------------------
4
+
5
+ /// Find if a given $scope has already been created
6
+ /// @param {String} $scope - Name of the scope
7
+
8
+ @function scope-exists($scope) {
9
+ @return map-has-key($_bem-log, $scope);
10
+ }
11
+
12
+ /// Log the new $scope
13
+ /// @param {String} $scope - scope name
14
+
15
+ @function _bem-log-scope($scope) {
16
+
17
+ // Check if the scope has already been created
18
+ @if scope-exists($scope) {
19
+ @error '`#{$scope}` scope has already been created';
20
+ }
21
+
22
+ // Initialize a new scope map
23
+ $new-scope: ($scope: ());
24
+
25
+ // Update bem log with new scope
26
+ $_bem-log: map-merge($_bem-log, $new-scope) !global;
27
+
28
+ // Everything OK
29
+ @return true;
30
+ }
@@ -0,0 +1,26 @@
1
+ .o-burger {
2
+ texture: juicy; }
3
+ .o-burger__letuce, .o-burger__tomato {
4
+ quality: fresh; }
5
+ .o-burger__cheese {
6
+ type: gouda; }
7
+ .o-burger__cheese--parmigiano {
8
+ type: parmigiano; }
9
+ .o-burger__extra-topping {
10
+ ingredient: bacon; }
11
+ .o-burger__meat {
12
+ type: beef; }
13
+ .o-burger--veggie {
14
+ texture: smooth; }
15
+ .o-burger--veggie .o-burger__meat {
16
+ type: lentils; }
17
+ .o-burger--veggie .o-burger__extra-topping {
18
+ ingredient: avocado; }
19
+ ._o-burger--veggie .o-burger__extra-topping {
20
+ ingredient: bacon; }
21
+ .t-mexican .o-burger {
22
+ spicy: hell-yeah; }
23
+ .o-burger.is-cold {
24
+ taste: terrible; }
25
+
26
+ /*# sourceMappingURL=test.css.map */
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "mappings": "AAmDa,SAAwB;EC9CjC,OAAO,EAAE,KAAK;EC+BL,oCAA0B;ID5B/B,OAAO,EAAE,KAAK;EC4BT,iBAA0B;IDxB/B,IAAI,EAAE,KAAK;IE+BN,6BAA4B;MF5B7B,IAAI,EAAE,UAAU;ECqBf,wBAA0B;IDhB/B,UAAU,EAAE,KAAK;ECgBZ,eAA0B;IDZ/B,IAAI,EAAE,IAAI;EEmBL,iBAA4B;IFfjC,OAAO,EAAE,MAAM;IGKV,iCAA4C;MHF7C,IAAI,EAAE,OAAO;IGEZ,0CAA4C;MHE7C,UAAU,EAAE,OAAO;MIJlB,2CAAW;QJOR,UAAU,EAAE,KAAK;EKXpB,oBAAsB;ILiB3B,KAAK,EAAE,SAAS;EMtBX,iBAAsB;IN0B3B,KAAK,EAAE,QAAQ",
4
+ "sources": ["_block.scss","test.scss","_element.scss","_modifier.scss","_modifies-element.scss","_hack.scss","_theme.scss","_state.scss"],
5
+ "names": [],
6
+ "file": "test.css"
7
+ }
@@ -0,0 +1,59 @@
1
+ @import 'bem-constructor';
2
+
3
+ // The Burger Test™
4
+
5
+ @include object('burger') {
6
+ texture: juicy;
7
+
8
+ @include element('letuce', 'tomato') {
9
+ quality: fresh;
10
+ }
11
+
12
+ @include element('cheese') {
13
+ type: gouda;
14
+
15
+ @include modifier('parmigiano') {
16
+ type: parmigiano;
17
+ }
18
+ }
19
+
20
+ @include element('extra-topping') {
21
+ ingredient: bacon;
22
+ }
23
+
24
+ @include element('meat') {
25
+ type: beef;
26
+ }
27
+
28
+ @include modifier('veggie') {
29
+ texture: smooth;
30
+
31
+ @include modifies-element('meat') {
32
+ type: lentils;
33
+ }
34
+
35
+ @include modifies-element('extra-topping') {
36
+ ingredient: avocado;
37
+
38
+ @include hack() {
39
+ ingredient: bacon;
40
+ }
41
+ }
42
+ }
43
+
44
+ @include theme('mexican') {
45
+ spicy: hell-yeah;
46
+ }
47
+
48
+ @include state('cold') {
49
+ taste: terrible;
50
+ }
51
+ }
52
+
53
+ // @include scope(test) {
54
+ // a: shite
55
+ // }
56
+
57
+ // .bem-log {
58
+ // log: inspect($_bem-log);
59
+ // }
metadata ADDED
@@ -0,0 +1,99 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: bem-constructor
3
+ version: !ruby/object:Gem::Version
4
+ version: '0.1'
5
+ platform: ruby
6
+ authors:
7
+ - Daniel Guillan
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-03-24 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: sass
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '3.4'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '3.4'
27
+ - !ruby/object:Gem::Dependency
28
+ name: compass
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.0'
41
+ description: A Sass library for building immutable and namespaced BEM-style CSS objects
42
+ email:
43
+ - daniel.guillan@gmail.com
44
+ executables: []
45
+ extensions: []
46
+ extra_rdoc_files: []
47
+ files:
48
+ - CHANGELOG.md
49
+ - README.md
50
+ - lib/bem-constructor.rb
51
+ - stylesheets/_bem-constructor.scss
52
+ - stylesheets/_block.scss
53
+ - stylesheets/_element.scss
54
+ - stylesheets/_error-checks.scss
55
+ - stylesheets/_hack.scss
56
+ - stylesheets/_index.scss
57
+ - stylesheets/_logger.scss
58
+ - stylesheets/_modifier.scss
59
+ - stylesheets/_modifies-element.scss
60
+ - stylesheets/_scope.scss
61
+ - stylesheets/_state.scss
62
+ - stylesheets/_theme.scss
63
+ - stylesheets/logger/_block-logger.scss
64
+ - stylesheets/logger/_context-logger.scss
65
+ - stylesheets/logger/_element-logger.scss
66
+ - stylesheets/logger/_modifier-logger.scss
67
+ - stylesheets/logger/_scope-logger.scss
68
+ - stylesheets/test.css
69
+ - stylesheets/test.css.map
70
+ - stylesheets/test.scss
71
+ homepage: https://github.com/danielguillan/bem-constructor
72
+ licenses:
73
+ - MIT
74
+ metadata: {}
75
+ post_install_message:
76
+ rdoc_options: []
77
+ require_paths:
78
+ - lib
79
+ required_ruby_version: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - ">="
82
+ - !ruby/object:Gem::Version
83
+ version: '0'
84
+ required_rubygems_version: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - ">="
87
+ - !ruby/object:Gem::Version
88
+ version: 1.3.6
89
+ requirements: []
90
+ rubyforge_project: bem-constructor
91
+ rubygems_version: 2.2.2
92
+ signing_key:
93
+ specification_version: 4
94
+ summary: BEM Constructor is a Sass library for building immutable and namespaced BEM-style
95
+ CSS objects. By enforcing a consistent and programatic way of defining objects (blocks,
96
+ elements and modifiers) it ensures a more structured, robust and secure object codebase
97
+ that is easy to understand and maintain. Objects defined using the constructor are
98
+ impossible to modify and reassign by mistake or omission.
99
+ test_files: []