dashy 0.0.1 → 0.0.2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: f00a1bf21b2a312b22a76c281eb8baefab10e532
4
- data.tar.gz: f2c80c1c86bdf524bcbe639a2fdd4a3e54fdfa28
3
+ metadata.gz: 7c08b11379b7751916a6e456a4e7c435ef52c998
4
+ data.tar.gz: 40e2f13a9566ab716975a357d6a8b2dce5bfc0a3
5
5
  SHA512:
6
- metadata.gz: 2c86ae1d952fbfd586dca71b94f97cca13342704ba10b9618a6f6082c81963fd4c16bd113ffa39ea9c3c0388cb7b6984071073ecb28e208b0b40ff10f7ea3f1f
7
- data.tar.gz: e187aaa08aca674ef10379dd8d4a896f77b32353b1af1d334922e5b07f0867aa0d64903b53c745b014419ed1b258b52b5016ea9e64e8d2cbace59634c2dd3b7b
6
+ metadata.gz: 82451fa0a98c000f3e4427df0e6f937f36d21e422f5f3cf1774b54f3ca2aebd22a9104ca4803cadec59a3aa43a420b28f51f3045492a956710b87489ccebae3c
7
+ data.tar.gz: f0e7fd2f26d29801eb65e58603dd7ccb6e6a45359d3a256d2f8f0135a313db82f6a1d019732a2a2647b346ff6a59d313b44ac4e18b3350bb7c481ceb40117d8c
@@ -1,14 +1,14 @@
1
- @import "dash/color";
2
- @import "dash/corner";
3
- @import "dash/default";
4
- @import "dash/define";
5
- @import "dash/direction";
6
- @import "dash/font-family";
7
- @import "dash/is";
8
- @import "dash/line-height";
9
- @import "dash/list";
10
- @import "dash/number";
11
- @import "dash/opposite";
12
- @import "dash/select";
13
- @import "dash/set";
14
- @import "dash/side";
1
+ @import "dashy/color";
2
+ @import "dashy/corner";
3
+ @import "dashy/default";
4
+ @import "dashy/define";
5
+ @import "dashy/direction";
6
+ @import "dashy/font-family";
7
+ @import "dashy/is";
8
+ @import "dashy/line-height";
9
+ @import "dashy/list";
10
+ @import "dashy/number";
11
+ @import "dashy/opposite";
12
+ @import "dashy/select";
13
+ @import "dashy/set";
14
+ @import "dashy/side";
@@ -111,7 +111,8 @@
111
111
 
112
112
  $define-grid-span-widths:
113
113
  (008.33% ("1/12"))
114
- (009.09% ("1/11"))
114
+ (009.09% ("1/10"))
115
+ (010.00% ("1/11"))
115
116
  (011.11% ("1/9"))
116
117
  (012.50% ("1/8"))
117
118
  (014.29% ("1/7"))
data/lib/dashy/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  module Dashy
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.2"
3
3
  end
4
4
 
5
5
  # Gem structure adapted from thoughtbot/bourbon...
data/lib/dashy.rb CHANGED
@@ -8,6 +8,8 @@ unless defined?(Sass)
8
8
  require 'sass'
9
9
  end
10
10
 
11
+ require "sass/script/functions/dashy"
12
+
11
13
  module Dashy
12
14
  if defined?(Rails) && defined?(Rails::Engine)
13
15
  class Engine < ::Rails::Engine
@@ -0,0 +1,287 @@
1
+ require 'sass/script/functions'
2
+
3
+ class Sass::Script::Value::String
4
+
5
+ prepend Module.new {
6
+
7
+ def to_s opt = {}
8
+ return @value.gsub(/(?<!\\)(?=[\$\%\/\?\!])/, "\\") if :selector == @type
9
+ return super
10
+ end
11
+
12
+ }
13
+
14
+ end
15
+
16
+ module Sass
17
+ module Script
18
+ module Functions
19
+
20
+ # Defined directly on `Functions` so we can use `declare`.
21
+
22
+ def is_wildcard value
23
+ '*' == value.to_s
24
+ end
25
+
26
+ def select *selectors
27
+ this = select_identifier ""
28
+ select_as this, *selectors
29
+ end
30
+ declare :select, [], :var_args => true
31
+
32
+ def select_as separator, *selectors
33
+ unquote = method :unquote
34
+ separator = separator.value
35
+ selectors = selectors.first if 1 == selectors.length
36
+ selectors = selectors.to_a.map { |selector| selector.to_a.map &unquote }
37
+ selectors = selectors.shift.product *selectors
38
+ Sass::Script::Value::List.new selectors.map! { |selector|
39
+ # selector = selector.reject &method(:is_wildcard) unless 1 == selector.length && is_wildcard(selector.first)
40
+ selector = selector.join separator
41
+ selector.gsub! /(?<=[\w\-\]])\*/, '' # Remove any wildcards after any words or attributes.
42
+ selector.gsub! /\*(?=[\w\.\#\[])/, '' # Remove any wildcards before any tags, classes, ids or attributes.
43
+ select_identifier selector
44
+ }, :comma
45
+ end
46
+ declare :select_with, [:string], :var_args => true
47
+
48
+ def select_as_descendants *selectors
49
+ descendants = select_identifier " "
50
+ select_as descendants, *selectors
51
+ end
52
+ declare :select_as_descendants, [], :var_args => true
53
+
54
+ def select_as_children *selectors
55
+ children = select_identifier ">"
56
+ select_as children, *selectors
57
+ end
58
+ declare :select_as_children, [], :var_args => true
59
+
60
+ def select_as_adjacent_siblings *selectors
61
+ adjacent_siblings = select_identifier "+"
62
+ select_as adjacent_siblings, *selectors
63
+ end
64
+ declare :select_as_adjacent_siblings, [], :var_args => true
65
+
66
+ def select_as_general_siblings *selectors
67
+ general_siblings = select_identifier "~"
68
+ select_as general_siblings, *selectors
69
+ end
70
+ declare :select_as_general_siblings, [], :var_args => true
71
+
72
+ def select_descendants selector = select_any
73
+ descendants = select_identifier " "
74
+ select descendants, selector
75
+ end
76
+ declare :select_descendants, [:string], :var_args => true
77
+
78
+ def select_children selector = select_any
79
+ children = select_identifier ">"
80
+ select children, selector
81
+ end
82
+ declare :select_children, [:string], :var_args => true
83
+
84
+ def select_adjacent_siblings selector = select_any
85
+ adjacent_siblings = select_identifier "+"
86
+ select adjacent_siblings, selector
87
+ end
88
+ declare :select_adjacent_siblings, [:string], :var_args => true
89
+
90
+ def select_general_siblings selector = select_any
91
+ general_siblings = select_identifier "~"
92
+ select general_siblings, selector
93
+ end
94
+ declare :select_general_siblings, [:string], :var_args => true
95
+
96
+ def select_either *selectors
97
+ selectors = selectors.first if 1 == selectors.length
98
+ selectors = selectors.map &:to_a
99
+ Sass::Script::Value::List.new selectors.flatten, :comma
100
+ end
101
+ declare :select_either, [], :var_args => true
102
+
103
+ def select_attribute attribute, *selectors
104
+ selectors = selectors.first if 1 == selectors.length
105
+ selectors = selectors.to_a.map { |selector| select_string "[#{ attribute }=\'#{ select_escaped! selector }\']" }
106
+ Sass::Script::Value::List.new selectors, :comma
107
+ end
108
+
109
+ def select_attribute_prefix attribute, *selectors
110
+ selectors = selectors.first if 1 == selectors.length
111
+ selectors = selectors.to_a.map { |selector|
112
+ [
113
+ (select_string "[#{ attribute }^=\'#{ select_escaped! selector }\']"),
114
+ (select_string "[#{ attribute }*=\' #{ select_escaped! selector }\']")
115
+ ]
116
+ }
117
+ Sass::Script::Value::List.new selectors.flatten, :comma
118
+ end
119
+
120
+ def select_attribute_suffix attribute, *selectors
121
+ selectors = selectors.first if 1 == selectors.length
122
+ selectors = selectors.to_a.map { |selector|
123
+ [
124
+ (select_string "[#{ attribute }$=\'#{ select_escaped! selector }\']"),
125
+ (select_string "[#{ attribute }*=\'#{ select_escaped! selector } \']")
126
+ ]
127
+ }
128
+ Sass::Script::Value::List.new selectors.flatten, :comma
129
+ end
130
+
131
+ def select_class_prefix *selectors
132
+ attribute = select_identifier "class"
133
+ select_attribute_prefix attribute, *selectors
134
+ end
135
+
136
+ def select_class_suffix *selectors
137
+ attribute = select_identifier "class"
138
+ select_attribute_suffix attribute, *selectors
139
+ end
140
+
141
+ def select_class selector
142
+ selectors = selector.to_a.map &:to_a
143
+ selectors = selectors.shift.product *selectors
144
+ selectors.map! { |selector| select_identifier "." + select_escaped!(selector.to_a.map { |s| s.to_s(quote: :none).gsub(/(?<!\\)(?=[.\$\%\/\?\!])/, "\\") }.join "-") }
145
+ Sass::Script::Value::List.new selectors, :comma
146
+ end
147
+
148
+ def select_classes *selectors
149
+ select_either *(selectors.map! &(method :select_class))
150
+ end
151
+
152
+ def select_pseudoclass selector
153
+ select_identifier ":" + (selector.to_a.join "-")
154
+ end
155
+
156
+ def select_pseudoclasses *selectors
157
+ select_either *(selectors.map! &(method :select_pseudoclass))
158
+ end
159
+
160
+ def select_quasiclass selector
161
+ select_either select_class(selector), select_pseudoclass(selector)
162
+ end
163
+
164
+ def select_quasiclasses *selectors
165
+ select_either *(selectors.map! &(method :select_quasiclass))
166
+ end
167
+
168
+ def select_adjacent_odd_siblings limit, *selectors
169
+ selectors.push select_identifier '*' if selectors.empty?
170
+ selectors = selectors.first if 1 == selectors.length
171
+ selectors = (0 .. limit.value).select { |i| 1 == i % 2 }.map { |i| select select_identifier(" + * " * i + " + "), *selectors }
172
+ select_either *selectors
173
+ end
174
+
175
+ def select_odd_siblings limit, *selectors
176
+ select_either select_first_sibling, select(select_first_sibling, select_adjacent_odd_siblings(limit, *selectors))
177
+ end
178
+
179
+ def select_odd_children limit, *selectors
180
+ select_children select_odd_siblings(limit, *selectors)
181
+ end
182
+
183
+ def select_adjacent_even_siblings limit, *selectors
184
+ selectors.push select_identifier '*' if selectors.empty?
185
+ selectors = selectors.first if 1 == selectors.length
186
+ selectors = (0 .. limit.value).select { |i| 0 == i % 2 }.map { |i| select select_identifier(" + * " * i + " + "), *selectors }
187
+ select_either *selectors
188
+ end
189
+
190
+ def select_even_siblings limit, *selectors
191
+ select select_first_sibling, select_adjacent_even_siblings(limit, *selectors)
192
+ end
193
+
194
+ def select_even_children limit, *selectors
195
+ select_children select_even_siblings(limit, *selectors)
196
+ end
197
+
198
+ def select_first_child
199
+ select_children select_first_sibling
200
+ end
201
+
202
+ def select_first_sibling
203
+ select_identifier ":first_child"
204
+ end
205
+
206
+ def select_any
207
+ select_identifier "*"
208
+ end
209
+
210
+ def select_string selector
211
+ Sass::Script::Value::String.new selector.to_s, :string
212
+ end
213
+
214
+ def select_identifier selector
215
+ Sass::Script::Value::String.new selector.to_s, :selector
216
+ end
217
+
218
+ def select_escaped! thing
219
+ thing.tap { |t|
220
+ case thing
221
+
222
+ when Sass::Script::Value::String
223
+ select_escaped! t.value
224
+
225
+ when String
226
+ t.gsub! /(?<!\\)(?=[\=\$\%\/\?\!\.])/, "\\"
227
+
228
+ end
229
+ }
230
+ end
231
+
232
+ def select_escaped_string selector
233
+ select_escaped! select_string selector
234
+ end
235
+
236
+ def select_escaped_identifier selector
237
+ select_escaped! select_identifier selector
238
+ end
239
+
240
+ def select_arguments namespace, *name_value_pairs
241
+ inputs = name_value_pairs.map { |name_value_pair|
242
+ name, value = name_value_pair.to_a
243
+ select select_identifier('input:checked'), select_attribute('form', namespace),
244
+ select_attribute('name', name),
245
+ select_attribute('value', value)
246
+ }
247
+ select_as_general_siblings *inputs
248
+
249
+ # $form: select-class($form);
250
+
251
+ # $left: select($input select-general-siblings($form));
252
+ # $right: select($form select-class($name $value));
253
+
254
+ # @return select-either($left, $right);
255
+ end
256
+
257
+ def select_lists *selectors
258
+ select_either select_ordered_lists(*selectors), select_unordered_lists(*selectors)
259
+ end
260
+
261
+ def select_ordered_lists *selectors
262
+ select select_identifier('ol'), *selectors
263
+ end
264
+
265
+ def select_unordered_lists *selectors
266
+ select select_identifier('ul'), *selectors
267
+ end
268
+
269
+ def list_of *lists
270
+ Sass::Script::Value::List.new lists.map { |list|
271
+ list = list.to_a
272
+ number = list.first
273
+ first, second, operator, *rest, last = list.map &:value
274
+ this = first
275
+ delta = second - first
276
+ case operator
277
+ when "_" then [].tap { |list| while this <= last; list << this; this += delta; end }
278
+ when "__" then [].tap { |list| while this < last; list << this; this += delta; end }
279
+ else list
280
+ end.map { |value| Sass::Script::Value::Number.new value, number.numerator_units, number.denominator_units }
281
+ }.flatten, :comma
282
+ end
283
+ declare :list_of, [:list], :var_args => true
284
+
285
+ end
286
+ end
287
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dashy
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alex Goldsmith
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-11-27 00:00:00.000000000 Z
11
+ date: 2014-12-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: sass
@@ -103,7 +103,7 @@ files:
103
103
  - lib/dashy/engine.rb
104
104
  - lib/dashy/generator.rb
105
105
  - lib/dashy/version.rb
106
- - lib/sass/script/functions.rb
106
+ - lib/sass/script/functions/dashy.rb
107
107
  - lib/tasks/install.rake
108
108
  - package.json
109
109
  - sache.json
@@ -1,275 +0,0 @@
1
- require 'sass/script/functions'
2
-
3
- Sass::Script::Value = Sass::Script unless Sass::Script.const_defined? :Value, false
4
-
5
- class Sass::Script::Value::String
6
-
7
- prepend Module.new {
8
-
9
- def to_s opt = {}
10
- return @value# if :selector == @type
11
- end
12
-
13
- }
14
-
15
- end
16
- module Sass::Script::Functions
17
-
18
- def is_wildcard value
19
- '*' == value.to_s
20
- end
21
-
22
- def select *selectors
23
- this = select_identifier ""
24
- select_as this, *selectors
25
- end
26
- declare :select, [], :var_args => true
27
-
28
- def select_as separator, *selectors
29
- unquote = method :unquote
30
- separator = separator.value
31
- selectors = selectors.first if 1 == selectors.length
32
- selectors = selectors.to_a.map { |selector| selector.to_a.map &unquote }
33
- selectors = selectors.shift.product *selectors
34
- Sass::Script::Value::List.new selectors.map! { |selector|
35
- # selector = selector.reject &method(:is_wildcard) unless 1 == selector.length && is_wildcard(selector.first)
36
- selector = selector.join separator
37
- selector.gsub! /(?<=[\w\-\]])\*/, '' # Remove any wildcards after any words or attributes.
38
- selector.gsub! /\*(?=[\w\.\#\[])/, '' # Remove any wildcards before any tags, classes, ids or attributes.
39
- select_identifier selector
40
- }, :comma
41
- end
42
- declare :select_with, [:string], :var_args => true
43
-
44
- def select_as_descendants *selectors
45
- descendants = select_identifier " "
46
- select_as descendants, *selectors
47
- end
48
- declare :select_as_descendants, [], :var_args => true
49
-
50
- def select_as_children *selectors
51
- children = select_identifier ">"
52
- select_as children, *selectors
53
- end
54
- declare :select_as_children, [], :var_args => true
55
-
56
- def select_as_adjacent_siblings *selectors
57
- adjacent_siblings = select_identifier "+"
58
- select_as adjacent_siblings, *selectors
59
- end
60
- declare :select_as_adjacent_siblings, [], :var_args => true
61
-
62
- def select_as_general_siblings *selectors
63
- general_siblings = select_identifier "~"
64
- select_as general_siblings, *selectors
65
- end
66
- declare :select_as_general_siblings, [], :var_args => true
67
-
68
- def select_descendants selector = select_any
69
- descendants = select_identifier " "
70
- select descendants, selector
71
- end
72
- declare :select_descendants, [:string], :var_args => true
73
-
74
- def select_children selector = select_any
75
- children = select_identifier ">"
76
- select children, selector
77
- end
78
- declare :select_children, [:string], :var_args => true
79
-
80
- def select_adjacent_siblings selector = select_any
81
- adjacent_siblings = select_identifier "+"
82
- select adjacent_siblings, selector
83
- end
84
- declare :select_adjacent_siblings, [:string], :var_args => true
85
-
86
- def select_general_siblings selector = select_any
87
- general_siblings = select_identifier "~"
88
- select general_siblings, selector
89
- end
90
- declare :select_general_siblings, [:string], :var_args => true
91
-
92
- def select_either *selectors
93
- selectors = selectors.first if 1 == selectors.length
94
- selectors = selectors.map &:to_a
95
- Sass::Script::Value::List.new selectors.flatten, :comma
96
- end
97
- declare :select_either, [], :var_args => true
98
-
99
- def select_attribute attribute, *selectors
100
- selectors = selectors.first if 1 == selectors.length
101
- selectors = selectors.to_a.map { |selector| select_string "[#{ attribute }=\"#{ select_escaped! selector }\"]" }
102
- Sass::Script::Value::List.new selectors, :comma
103
- end
104
-
105
- def select_spaced_attribute
106
- raise 'bwoon'
107
- end
108
-
109
- def select_attribute_prefix attribute, *selectors
110
- selectors = selectors.first if 1 == selectors.length
111
- selectors = selectors.to_a.map { |selector|
112
- [
113
- (select_string "[#{ attribute }^=\"#{ select_escaped! selector }\"]"),
114
- (select_string "[#{ attribute }*=\" #{ select_escaped! selector }\"]")
115
- ]
116
- }
117
- Sass::Script::Value::List.new selectors.flatten, :comma
118
- end
119
-
120
- def select_attribute_suffix attribute, *selectors
121
- selectors = selectors.first if 1 == selectors.length
122
- selectors = selectors.to_a.map { |selector|
123
- [
124
- (select_string "[#{ attribute }$=\"#{ select_escaped! selector }\"]"),
125
- (select_string "[#{ attribute }*=\"#{ select_escaped! selector } \"]")
126
- ]
127
- }
128
- Sass::Script::Value::List.new selectors.flatten, :comma
129
- end
130
-
131
- def select_class_prefix *selectors
132
- attribute = select_identifier "class"
133
- select_attribute_prefix attribute, *selectors
134
- end
135
-
136
- def select_class_suffix *selectors
137
- attribute = select_identifier "class"
138
- select_attribute_suffix attribute, *selectors
139
- end
140
-
141
- def select_class selector
142
- selectors = selector.to_a.map &:to_a
143
- selectors = selectors.shift.product *selectors
144
- selectors.map! { |selector| select_identifier "." + select_escaped!(selector.to_a.join "-") }
145
- Sass::Script::Value::List.new selectors, :comma
146
- end
147
-
148
- def select_classes *selectors
149
- select_either *(selectors.map! &(method :select_class))
150
- end
151
-
152
- def select_pseudoclass selector
153
- select_identifier ":" + (selector.to_a.join "-")
154
- end
155
-
156
- def select_pseudoclasses *selectors
157
- select_either *(selectors.map! &(method :select_pseudoclass))
158
- end
159
-
160
- def select_quasiclass selector
161
- select_either select_class(selector), select_pseudoclass(selector)
162
- end
163
-
164
- def select_quasiclasses *selectors
165
- select_either *(selectors.map! &(method :select_quasiclass))
166
- end
167
-
168
- def select_adjacent_odd_siblings limit, *selectors
169
- selectors.push select_identifier '*' if selectors.empty?
170
- selectors = selectors.first if 1 == selectors.length
171
- selectors = (0 .. limit.value).select { |i| 1 == i % 2 }.map { |i| select select_identifier(" + * " * i + " + "), *selectors }
172
- select_either *selectors
173
- end
174
-
175
- def select_odd_siblings limit, *selectors
176
- select_either select_first_sibling, select(select_first_sibling, select_adjacent_odd_siblings(limit, *selectors))
177
- end
178
-
179
- def select_odd_children limit, *selectors
180
- select_children select_odd_siblings(limit, *selectors)
181
- end
182
-
183
- def select_adjacent_even_siblings limit, *selectors
184
- selectors.push select_identifier '*' if selectors.empty?
185
- selectors = selectors.first if 1 == selectors.length
186
- selectors = (0 .. limit.value).select { |i| 0 == i % 2 }.map { |i| select select_identifier(" + * " * i + " + "), *selectors }
187
- select_either *selectors
188
- end
189
-
190
- def select_even_siblings limit, *selectors
191
- select select_first_sibling, select_adjacent_even_siblings(limit, *selectors)
192
- end
193
-
194
- def select_even_children limit, *selectors
195
- select_children select_even_siblings(limit, *selectors)
196
- end
197
-
198
- def select_first_child
199
- select_children select_first_sibling
200
- end
201
-
202
- def select_first_sibling
203
- select_identifier ":first_child"
204
- end
205
-
206
- def select_any
207
- select_identifier "*"
208
- end
209
-
210
- def select_string selector
211
- Sass::Script::Value::String.new selector.to_s, :string
212
- end
213
-
214
- def select_identifier selector
215
- Sass::Script::Value::String.new selector.to_s, :identifier
216
- end
217
-
218
- def select_escaped! thing
219
- thing.tap { |t|
220
- case thing
221
-
222
- when String
223
- t.gsub! /(?<!\\)(?=[\=\$\%\/\?\!\.])/, "\\"
224
-
225
- when Sass::Script::Value::String
226
- select_escaped! t.value
227
-
228
- end
229
- }
230
- end
231
-
232
- def select_escaped_string selector
233
- select_escaped! select_string selector
234
- end
235
-
236
- def select_escaped_identifier selector
237
- select_escaped! select_identifier selector
238
- end
239
-
240
- def select_arguments namespace, *name_value_pairs
241
- inputs = name_value_pairs.map { |name_value_pair|
242
- name, value = name_value_pair.to_a
243
- select select_identifier('input:checked'), select_attribute('form', namespace),
244
- select_attribute('name', name),
245
- select_attribute('value', value)
246
- }
247
- select_as_general_siblings *inputs
248
-
249
- # $form: select-class($form);
250
-
251
- # $left: select($input select-general-siblings($form));
252
- # $right: select($form select-class($name $value));
253
-
254
- # @return select-either($left, $right);
255
- end
256
-
257
- def list_of *lists
258
- Sass::Script::Value::List.new lists.map { |list|
259
- list = list.to_a
260
- number = list.first
261
- first, second, operator, *rest, last = list.map &:value
262
- this = first
263
- delta = second - first
264
- case operator
265
- when "_" then [].tap { |list| while this <= last; list << this; this += delta; end }
266
- when "__" then [].tap { |list| while this < last; list << this; this += delta; end }
267
- else list
268
- end.map { |value| Sass::Script::Value::Number.new value, number.numerator_units, number.denominator_units }
269
- }.flatten, :comma
270
- end
271
- declare :select_children, [:list], :var_args => true
272
-
273
- end
274
-
275
- # Gem structure adapted from thoughtbot/bourbon...