mtg_card_maker 0.1.0

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.
Files changed (43) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +16 -0
  3. data/CODE_OF_CONDUCT.md +131 -0
  4. data/LICENSE.txt +20 -0
  5. data/README.md +305 -0
  6. data/bin/mtg_card_maker +7 -0
  7. data/lib/mtg_card_maker/base_card.rb +210 -0
  8. data/lib/mtg_card_maker/cli.rb +221 -0
  9. data/lib/mtg_card_maker/color_palette.rb +135 -0
  10. data/lib/mtg_card_maker/color_scheme.rb +305 -0
  11. data/lib/mtg_card_maker/core_ext/deep_merge.rb +21 -0
  12. data/lib/mtg_card_maker/fonts/Goudy Mediaeval DemiBold.ttf +0 -0
  13. data/lib/mtg_card_maker/fonts/goudy_base64.txt +1 -0
  14. data/lib/mtg_card_maker/icon_service.rb +95 -0
  15. data/lib/mtg_card_maker/icons/black.svg +1 -0
  16. data/lib/mtg_card_maker/icons/blue.svg +1 -0
  17. data/lib/mtg_card_maker/icons/colorless.svg +1 -0
  18. data/lib/mtg_card_maker/icons/green.svg +1 -0
  19. data/lib/mtg_card_maker/icons/jsharp.svg +1 -0
  20. data/lib/mtg_card_maker/icons/qrcode.svg +1 -0
  21. data/lib/mtg_card_maker/icons/red.svg +1 -0
  22. data/lib/mtg_card_maker/icons/white.svg +1 -0
  23. data/lib/mtg_card_maker/layer_config.rb +289 -0
  24. data/lib/mtg_card_maker/layer_factory.rb +122 -0
  25. data/lib/mtg_card_maker/layer_initializer.rb +12 -0
  26. data/lib/mtg_card_maker/layers/art_layer.rb +63 -0
  27. data/lib/mtg_card_maker/layers/border_layer.rb +166 -0
  28. data/lib/mtg_card_maker/layers/frame_layer.rb +62 -0
  29. data/lib/mtg_card_maker/layers/name_layer.rb +82 -0
  30. data/lib/mtg_card_maker/layers/power_layer.rb +69 -0
  31. data/lib/mtg_card_maker/layers/text_box_layer.rb +107 -0
  32. data/lib/mtg_card_maker/layers/type_line_layer.rb +86 -0
  33. data/lib/mtg_card_maker/mana_cost.rb +220 -0
  34. data/lib/mtg_card_maker/metallic_renderer.rb +174 -0
  35. data/lib/mtg_card_maker/sprite_sheet_assets.rb +158 -0
  36. data/lib/mtg_card_maker/sprite_sheet_builder.rb +90 -0
  37. data/lib/mtg_card_maker/sprite_sheet_service.rb +126 -0
  38. data/lib/mtg_card_maker/svg_gradient_service.rb +159 -0
  39. data/lib/mtg_card_maker/text_rendering_service.rb +160 -0
  40. data/lib/mtg_card_maker/version.rb +8 -0
  41. data/lib/mtg_card_maker.rb +268 -0
  42. data/sig/mtg_card_maker.rbs +4 -0
  43. metadata +149 -0
@@ -0,0 +1,82 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../mana_cost'
4
+
5
+ module MtgCardMaker
6
+ # NameLayer is a specialized layer
7
+ # This is the area that surrounds the name and cost of the card
8
+ class NameLayer < BaseLayer
9
+ include LayerInitializer
10
+ attr_reader :name, :cost, :color_scheme
11
+
12
+ def initialize(dimensions:, name:, cost:, color: nil, color_scheme: DEFAULT_COLOR_SCHEME)
13
+ frame_color = initialize_layer_color(color, color_scheme, :background_color)
14
+ super(dimensions: dimensions, color: frame_color)
15
+ @name = name
16
+ @cost = cost
17
+ @color_scheme = color_scheme
18
+ end
19
+
20
+ def render
21
+ # Ensure gradients are defined for this color scheme
22
+ SvgGradientService.define_all_gradients(svg, color_scheme)
23
+
24
+ svg.g do
25
+ render_name_area
26
+ render_card_name
27
+ render_mana_cost if cost && !cost.empty?
28
+ end
29
+ end
30
+
31
+ private
32
+
33
+ def render_name_area
34
+ layer_config = LayerConfig.default
35
+ stroke_width = layer_config.stroke_width
36
+ corners = layer_config.corner_radius(:name)
37
+
38
+ svg.rect x: x, y: y, width: width, height: height,
39
+ fill: "url(##{SvgGradientService.name_gradient_id(color_scheme)})",
40
+ stroke: ColorPalette::FRAME_STROKE_COLOR,
41
+ stroke_width: stroke_width,
42
+ rx: corners[:x], ry: corners[:y], mask: "url(##{@mask_id})"
43
+ end
44
+
45
+ def render_card_name
46
+ layer_config = LayerConfig.default
47
+ text_service = TextRenderingService.new(
48
+ text: name,
49
+ layer_config: layer_config,
50
+ x: layer_config.text_x_position(x),
51
+ y: layer_config.text_y_position(y, :name_area, height),
52
+ font_size: layer_config.font_size(:name),
53
+ available_width: layer_config.text_width(width, :name_area),
54
+ css_class: layer_config.css_class(:card_name)
55
+ )
56
+ lines = text_service.wrapped_text_lines
57
+ lines.each do |line, attrs|
58
+ svg.text line, attrs
59
+ end
60
+ end
61
+
62
+ def render_mana_cost
63
+ mana_cost = ManaCost.new(cost)
64
+ cost_x = cost_position_x(mana_cost)
65
+ cost_y = cost_position_y
66
+ svg.g transform: "translate(#{cost_x}, #{cost_y})" do
67
+ svg << mana_cost.to_svg
68
+ end
69
+ end
70
+
71
+ def cost_position_x(mana_cost)
72
+ layer_config = LayerConfig.default
73
+ config = layer_config.mana_cost_config
74
+ base_x = x + width - config[:margin] - config[:circle_radius]
75
+ base_x - (config[:circle_spacing] * (mana_cost.elements.length - 1))
76
+ end
77
+
78
+ def cost_position_y
79
+ y + (height / 2) - 2
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MtgCardMaker
4
+ # PowerLayer is a specialized layer
5
+ # This is the area that surrounds the power and toughness of the card
6
+ class PowerLayer < BaseLayer
7
+ include LayerInitializer
8
+ attr_reader :power, :toughness, :color_scheme
9
+
10
+ def initialize(dimensions:, power:, toughness:, color: nil, color_scheme: DEFAULT_COLOR_SCHEME)
11
+ frame_color = initialize_layer_color(color, color_scheme, :background_color)
12
+ super(dimensions: dimensions, color: frame_color)
13
+ @power = power
14
+ @toughness = toughness
15
+ @color_scheme = color_scheme
16
+ end
17
+
18
+ def render
19
+ # Don't render if power or toughness are nil, empty, or invalid
20
+ return if power.nil? || toughness.nil? || power.to_s.strip.empty? || toughness.to_s.strip.empty?
21
+
22
+ # Ensure gradients are defined for this color scheme
23
+ SvgGradientService.define_all_gradients(svg, color_scheme)
24
+
25
+ layer_config = LayerConfig.default
26
+ stroke_width = layer_config.stroke_width
27
+ corners = layer_config.corner_radius(:power)
28
+
29
+ svg.g do
30
+ # P/T box with ornate frame
31
+ svg.rect x: x_position, y: y, width: dynamic_width, height: height,
32
+ fill: "url(##{SvgGradientService.name_gradient_id(color_scheme)})",
33
+ stroke: ColorPalette::FRAME_STROKE_COLOR,
34
+ stroke_width: stroke_width, rx: corners[:x], ry: corners[:y]
35
+
36
+ # P/T text with bold styling
37
+ cost_attrs = {
38
+ x: x_position + (dynamic_width / 2),
39
+ y: y + (height / 2) + layer_config.positioning(:power_area)[:y_offset],
40
+ fill: DEFAULT_TEXT_COLOR,
41
+ font_size: layer_config.font_size(:power_area),
42
+ text_anchor: 'middle',
43
+ class: 'card-power-toughness'
44
+ }
45
+ svg.text "#{power}/#{toughness}", cost_attrs
46
+ end
47
+ end
48
+
49
+ private
50
+
51
+ def dynamic_width
52
+ # Calculate total character count: power + toughness + "/"
53
+ total_chars = power.to_s.length + toughness.to_s.length + 1
54
+
55
+ # Base width for 3 characters, with steady increase per additional character
56
+ base_width = 60
57
+ width_per_char = 13
58
+ base_width + ((total_chars - 3) * width_per_char)
59
+ end
60
+
61
+ def x_position
62
+ # Anchor right side to card width (630px) with 35px margin
63
+ # Right edge should be at 595px (630 - 35)
64
+ margin = 35
65
+ right_edge = CARD_WIDTH - margin
66
+ right_edge - dynamic_width
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,107 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MtgCardMaker
4
+ # TextBoxLayer is a specialized layer for the rules and flavor text
5
+ class TextBoxLayer < BaseLayer
6
+ include LayerInitializer
7
+ attr_reader :rules_text, :flavor_text, :color_scheme
8
+
9
+ def initialize(dimensions:, rules_text:, flavor_text: nil, color: nil, color_scheme: DEFAULT_COLOR_SCHEME)
10
+ frame_color = initialize_layer_color(color, color_scheme, :background_color)
11
+ super(dimensions: dimensions, color: frame_color)
12
+ @rules_text = rules_text
13
+ @flavor_text = flavor_text
14
+ @color_scheme = color_scheme
15
+ end
16
+
17
+ # Render the rules text and flavor text in a text box with a background and highlight
18
+ def render
19
+ # Ensure gradients are defined for this color scheme
20
+ SvgGradientService.define_all_gradients(svg, color_scheme)
21
+
22
+ render_text_box
23
+ end
24
+
25
+ private
26
+
27
+ def render_text_box
28
+ layer_config = LayerConfig.default
29
+ stroke_width = layer_config.stroke_width
30
+
31
+ svg.g do
32
+ # Text box background with mask for transparent window
33
+ svg.rect x: x, y: y, width: width, height: height,
34
+ fill: "url(##{SvgGradientService.description_gradient_id(color_scheme)})",
35
+ stroke: color_scheme.primary_color,
36
+ stroke_width: stroke_width
37
+
38
+ render_text_content
39
+ end
40
+ end
41
+
42
+ def render_text_content
43
+ render_rules_text
44
+ render_flavor_text if flavor_text
45
+ end
46
+
47
+ def render_rules_text
48
+ layer_config = LayerConfig.default
49
+ text_service = create_text_service(layer_config, rules_text, :description, :card_description)
50
+ render_text_lines(text_service)
51
+ end
52
+
53
+ def render_flavor_text
54
+ return unless flavor_text && !flavor_text.strip.empty?
55
+
56
+ render_flavor_separator
57
+ layer_config = LayerConfig.default
58
+ text_service = create_text_service(layer_config, flavor_text, :flavor_text, :flavor_text)
59
+ render_text_lines(text_service)
60
+ end
61
+
62
+ def create_text_service(layer_config, text, text_type, css_class)
63
+ TextRenderingService.new(
64
+ text: text,
65
+ layer_config: layer_config,
66
+ x: layer_config.text_x_position(x),
67
+ y: calculate_text_y_position(layer_config, text_type),
68
+ font_size: layer_config.font_size(text_type),
69
+ color: color_scheme.text_color,
70
+ available_width: layer_config.text_width(width, text_type),
71
+ css_class: layer_config.css_class(css_class)
72
+ )
73
+ end
74
+
75
+ def calculate_text_y_position(layer_config, text_type)
76
+ if text_type == :flavor_text
77
+ y + height - layer_config.positioning(:flavor_text)[:y_offset]
78
+ else
79
+ layer_config.text_y_position(y, :description)
80
+ end
81
+ end
82
+
83
+ def render_text_lines(text_service)
84
+ text_service.wrapped_text_lines.each do |line, attrs|
85
+ svg.text line, attrs
86
+ end
87
+ end
88
+
89
+ def render_flavor_separator
90
+ svg.line(**separator_config)
91
+ end
92
+
93
+ def separator_config
94
+ layer_config = LayerConfig.default
95
+ separator_offset = layer_config.positioning(:flavor_text)[:separator_offset]
96
+ separator_y = y + height - separator_offset
97
+ {
98
+ x1: layer_config.text_x_position(x),
99
+ y1: separator_y,
100
+ x2: x + width - layer_config.horizontal_padding,
101
+ y2: separator_y,
102
+ stroke: color_scheme.primary_color,
103
+ stroke_width: 1
104
+ }
105
+ end
106
+ end
107
+ end
@@ -0,0 +1,86 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MtgCardMaker
4
+ # TypeLineLayer is a specialized layer for the type of the card
5
+ # This is the small area in the center of the card below the artwork
6
+ class TypeLineLayer < BaseLayer
7
+ include LayerInitializer
8
+ attr_reader :type_line, :color_scheme
9
+
10
+ def initialize(dimensions:, type_line:, color: nil, color_scheme: DEFAULT_COLOR_SCHEME)
11
+ frame_color = initialize_layer_color(color, color_scheme, :background_color)
12
+ super(dimensions: dimensions, color: frame_color)
13
+ @type_line = type_line
14
+ @color_scheme = color_scheme
15
+ end
16
+
17
+ def render
18
+ # Ensure gradients are defined for this color scheme
19
+ SvgGradientService.define_all_gradients(svg, color_scheme)
20
+
21
+ svg.g do
22
+ render_type_background
23
+ render_type_line
24
+ render_type_icon
25
+ end
26
+ end
27
+
28
+ private
29
+
30
+ def render_type_background
31
+ layer_config = LayerConfig.default
32
+ stroke_width = layer_config.stroke_width
33
+ corners = layer_config.corner_radius(:type)
34
+
35
+ svg.rect x: x, y: y, width: width, height: height,
36
+ fill: "url(##{color_scheme.scheme_name}_name_gradient)",
37
+ stroke: ColorPalette::FRAME_STROKE_COLOR,
38
+ stroke_width: stroke_width,
39
+ rx: corners[:x], ry: corners[:y]
40
+ end
41
+
42
+ def render_type_icon
43
+ layer_config = LayerConfig.default
44
+ icon_config = layer_config.type_icon_config
45
+
46
+ svg.path stroke_width: 3,
47
+ d: jsharp_path_data,
48
+ fill: 'none',
49
+ stroke: ColorPalette::FRAME_STROKE_COLOR,
50
+ transform: build_icon_transform(icon_config),
51
+ 'aria-label': 'J#'
52
+ end
53
+
54
+ def jsharp_path_data
55
+ jsharp_svg = IconService.new.jsharp_svg
56
+ raise 'J# icon not found' unless jsharp_svg
57
+
58
+ path_match = jsharp_svg.match(/<path[^>]*d="([^"]*)"[^>]*>/)
59
+ path_match&.[](1)
60
+ end
61
+
62
+ def build_icon_transform(icon_config)
63
+ translate_part = "translate(#{width - icon_config[:x_offset]},#{y + icon_config[:y_offset]})"
64
+ scale_part = "scale(#{icon_config[:scale]})"
65
+ aspect_part = "scale(#{icon_config[:aspect_ratio][:x]},#{icon_config[:aspect_ratio][:y]})"
66
+
67
+ "#{translate_part} #{scale_part} #{aspect_part}"
68
+ end
69
+
70
+ def render_type_line
71
+ layer_config = LayerConfig.default
72
+ text_service = TextRenderingService.new(
73
+ text: type_line,
74
+ layer_config: layer_config,
75
+ x: layer_config.text_x_position(x),
76
+ y: layer_config.text_y_position(y, :type_area, height),
77
+ font_size: layer_config.font_size(:type),
78
+ available_width: layer_config.text_width(width, :type_area),
79
+ css_class: layer_config.css_class(:card_type)
80
+ )
81
+ text_service.wrapped_text_lines.each do |line, attrs|
82
+ svg.text line, attrs
83
+ end
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,220 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'icon_service'
4
+
5
+ module MtgCardMaker
6
+ # Mana cost class that generates SVG for the mana cost of a card.
7
+ # This class parses mana cost strings (e.g., "2UR", "XG") and generates
8
+ # SVG circles with appropriate colors and icons for each mana symbol.
9
+ #
10
+ # @example
11
+ # mana_cost = MtgCardMaker::ManaCost.new("2UR")
12
+ # svg = mana_cost.to_svg
13
+ #
14
+ # @example
15
+ # mana_cost = MtgCardMaker::ManaCost.new("XG", icon_set: :custom)
16
+ # svg = mana_cost.to_svg
17
+ #
18
+ # @since 0.1.0
19
+ # rubocop:disable Metrics/ClassLength
20
+ class ManaCost
21
+ # Mapping of letters to colors
22
+ # @return [Hash] the color mapping for mana symbols
23
+ COLOR_MAP = {
24
+ 'B' => :black, 'S' => :black,
25
+ 'U' => :blue, 'I' => :blue,
26
+ 'G' => :green, 'F' => :green,
27
+ 'W' => :white, 'P' => :white,
28
+ 'R' => :red, 'M' => :red,
29
+ 'C' => :colorless
30
+ }.freeze
31
+
32
+ # @return [Array<Symbol>] the parsed mana elements
33
+ attr_reader :elements
34
+
35
+ # @return [Integer, nil] the integer value for generic mana
36
+ attr_reader :int_val
37
+
38
+ # Initialize a new mana cost parser
39
+ #
40
+ # @param mana_string [String, nil] the mana cost string (e.g., "2UR", "XG")
41
+ # @param icon_set [Symbol] the icon set to use (default: :default)
42
+ def initialize(mana_string = nil, icon_set = :default) # rubocop:disable Metrics/MethodLength
43
+ @elements = []
44
+ @origins = [] # :numeric, :C, or color symbol
45
+ @int_val = nil
46
+ @original_string = mana_string
47
+ @icon_service = IconService.new(icon_set)
48
+
49
+ return if mana_string.nil? || mana_string.empty?
50
+
51
+ # Convert to uppercase for consistency
52
+ mana_string = mana_string.to_s.upcase
53
+
54
+ # Parse the mana string
55
+ parse_mana_string(mana_string)
56
+
57
+ # Limit to maximum circles
58
+ layer_config = LayerConfig.default
59
+ max_circles = layer_config.mana_cost_config[:max_circles]
60
+ @elements = @elements.first(max_circles)
61
+ @origins = @origins.first(max_circles)
62
+ end
63
+
64
+ # Returns SVG string for the mana cost circles with drop shadow
65
+ #
66
+ # @return [String] the SVG markup for the mana cost
67
+ def to_svg
68
+ layer_config = LayerConfig.default
69
+ circle_spacing = layer_config.mana_cost_config[:circle_spacing]
70
+
71
+ # Build mana cost circles SVG
72
+ mana_circles = @elements.each_with_index.map do |color, i|
73
+ x = i * circle_spacing
74
+ y = 0
75
+ mana_element_svg(x, y, color, @origins[i])
76
+ end.join
77
+
78
+ <<~SVG.delete("\n")
79
+ #{drop_shadow_filter}
80
+ <g filter="url(#mana-cost-drop-shadow)">
81
+ #{mana_circles}
82
+ </g>
83
+ SVG
84
+ end
85
+
86
+ private
87
+
88
+ def parse_mana_string(mana_string)
89
+ return if mana_string.nil?
90
+
91
+ if numeric_cost?(mana_string) || mana_string.start_with?('X')
92
+ parse_numeric_cost(mana_string)
93
+ else
94
+ parse_colored_mana(mana_string)
95
+ end
96
+ end
97
+
98
+ def numeric_cost?(mana_string)
99
+ mana_string.match?(/^\d+/)
100
+ end
101
+
102
+ def parse_numeric_cost(mana_string)
103
+ mana_string = mana_string.gsub(/^X/, '10')
104
+
105
+ int_match = mana_string.match(/^(\d+)/)
106
+ @int_val = int_match[1].to_i
107
+
108
+ # If it's a double-digit number, treat as X
109
+ @int_val = 10 if @int_val >= 10
110
+
111
+ @elements << :colorless
112
+ @origins << :numeric
113
+
114
+ # Remove the integer and parse remaining colored mana
115
+ remaining = mana_string[int_match[1].length..]
116
+ parse_colored_mana(remaining)
117
+ end
118
+
119
+ def parse_colored_mana(mana_string)
120
+ return if mana_string.nil? || mana_string.empty?
121
+
122
+ mana_string.chars.each do |char|
123
+ process_colored_character(char)
124
+ end
125
+ end
126
+
127
+ def process_colored_character(char)
128
+ color = COLOR_MAP[char]
129
+ if colorless_symbol?(color, char)
130
+ @elements << :colorless
131
+ @origins << :C
132
+ elsif color
133
+ @elements << color
134
+ @origins << color
135
+ end
136
+ end
137
+
138
+ def colorless_symbol?(color, char)
139
+ color == :colorless && char == 'C'
140
+ end
141
+
142
+ def drop_shadow_filter
143
+ layer_config = LayerConfig.default
144
+ drop_shadow = layer_config.drop_shadow_config
145
+ <<~SVG.delete("\n")
146
+ <defs>
147
+ <filter id="mana-cost-drop-shadow"
148
+ x="-50%"
149
+ y="-50%"
150
+ width="200%"
151
+ height="200%">
152
+ <feDropShadow dx="#{drop_shadow[:dx]}"
153
+ dy="#{drop_shadow[:dy]}"
154
+ stdDeviation="#{drop_shadow[:std_deviation]}"
155
+ flood-color="black"
156
+ flood-opacity="#{drop_shadow[:flood_opacity]}"/>
157
+ </filter>
158
+ </defs>
159
+ SVG
160
+ end
161
+
162
+ def mana_element_svg(x, y, color, origin) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
163
+ layer_config = LayerConfig.default
164
+ circle_radius = layer_config.mana_cost_config[:circle_radius]
165
+ icon_size = layer_config.mana_cost_config[:icon_size]
166
+
167
+ fill = svg_color(color)
168
+ svg = "<circle cx='#{x}' cy='#{y}' r='#{circle_radius}' fill='#{fill}' />"
169
+
170
+ if color == :colorless && origin == :numeric
171
+ # Add text for numeric colorless mana circles
172
+ svg << colorless_text(x, y, origin)
173
+ else
174
+ # Add icon inside colored mana circles, smaller and with opacity
175
+ icon_svg = @icon_service.icon_svg(color, size: icon_size)
176
+ if icon_svg
177
+ icon_x = x - (icon_size / 2)
178
+ icon_y = y - (icon_size / 2)
179
+ layer_config = LayerConfig.default
180
+ opacity = layer_config.mana_cost_icon_opacity
181
+ svg << "<g transform='translate(#{icon_x}, #{icon_y})' opacity='#{opacity}'>#{icon_svg}</g>"
182
+ end
183
+ end
184
+
185
+ svg
186
+ end
187
+
188
+ def colorless_text(x, y, origin)
189
+ if origin == :numeric && @int_val
190
+ text = @int_val == 10 ? 'X' : @int_val.to_s
191
+ text_svg(x, y, text)
192
+ else
193
+ ''
194
+ end
195
+ end
196
+
197
+ def text_svg(x, y, text)
198
+ # Position text in the center of the circle with appropriate styling
199
+ layer_config = LayerConfig.default
200
+ font_size = layer_config.mana_cost_font_size
201
+ font_weight = text == 'X' ? 'normal' : 'semibold'
202
+ y_offset = layer_config.mana_cost_text_y_offset
203
+
204
+ "<text x='#{x}' y='#{y + y_offset}' fill='#000' text-anchor='middle' " \
205
+ "font-weight='#{font_weight}' font-size='#{font_size}' font-family='serif'>#{text}</text>"
206
+ end
207
+
208
+ def svg_color(color)
209
+ case color
210
+ when :white then '#FFF9C4' # White primary color
211
+ when :blue then '#90CAF9' # Blue primary color (from fixture)
212
+ when :black then '#BDBDBD' # Black primary color (from fixture)
213
+ when :red then '#EF9A9A' # Red primary color (from fixture)
214
+ when :green then '#A5D6A7' # Green primary color (from fixture)
215
+ else '#DDD'
216
+ end
217
+ end
218
+ end
219
+ # rubocop:enable Metrics/ClassLength
220
+ end