Last active
July 10, 2021 13:37
-
-
Save raybrownco/0047ef8d7eaec0bf31bb to your computer and use it in GitHub Desktop.
Inline SVG in Middleman
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Middleman - Inline SVG Helper | |
# ------------------------------------------------------------------------------ | |
# | |
# Installation | |
# ------------ | |
# 1. Save this file at `[project_root]/helpers/image_helpers.rb` | |
# 2. Open the project's Gemfile and add this line: `gem "oga"` | |
# 3. Run `bundle install` from the command line | |
# | |
# Note: Restart your local Middleman server (if it's running) before continuing | |
# | |
# Usage | |
# ----- | |
# Embed SVG files into your template files like so: | |
# | |
# ``` | |
# <%= inline_svg "name/of/file.svg" %> | |
# ``` | |
# | |
# The helper also allows you to pass attributes to add to the SVG tag, like so: | |
# | |
# Input: | |
# ``` | |
# <%= inline_svg "name/of/file.svg", class: "foo", data: {bar: "baz"} %> | |
# ``` | |
# | |
# Output: | |
# ``` | |
# <svg <!-- existing attributes --> class="foo" data-bar="baz"> | |
# <!-- SVG contents --> | |
# </svg> | |
# ``` | |
# | |
# Acknowledgements and Contributors | |
# -------------------------- | |
# This was initally adapted from the work of James Martin | |
# and Steven Harley, which you can read more about here: | |
# https://robots.thoughtbot.com/organized-workflow-for-svg | |
# | |
# Further improvements were made based on contributions by: | |
# | |
# * Cecile Veneziani (@cveneziani) | |
# * Allan White (@allanwhite) | |
# | |
# Thanks for improving this helper! Have questions or concerns? | |
# Feel free to fork the Gist or comment on it here: | |
# https://gist.github.com/bitmanic/0047ef8d7eaec0bf31bb | |
module ImageHelpers | |
def inline_svg(relative_image_path, optional_attributes = {}) | |
image_path = File.join(config[:source], config[:images_dir], relative_image_path) | |
# If the image was found... | |
if File.exists?(image_path) | |
# Open the image | |
image = File.open(image_path, 'r') { |f| f.read } | |
# Return the image if no optional attributes were passed in | |
return image if optional_attributes.empty? | |
# Otherwise, parse the image | |
document = Oga.parse_xml(image) | |
svg = document.css('svg').first | |
# Then, add the attributes | |
# NOTE: This allows for hash-based values, but we're only going one level | |
# deep right now. If you know a great way to dig `N` levels deeper, | |
# feel free to post about it on the Gist. | |
optional_attributes.each do |attribute, value| | |
case value | |
when Hash | |
value.each do |subattribute, subvalue| | |
unless subvalue.class == Hash | |
svg.set( | |
"#{attribute} #{subattribute}".parameterize, | |
subvalue.html_safe | |
) | |
end | |
end | |
else | |
svg.set(attribute.to_sym, value.html_safe) | |
end | |
end | |
# Finally, return the image | |
document.to_xml | |
# If the file wasn't found... | |
else | |
# Embed an inline SVG image with an error message | |
%( | |
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 400 30" | |
width="400px" height="30px" | |
> | |
<text font-size="16" x="8" y="20" fill="#cc0000"> | |
Error: '#{relative_image_path}' could not be found. | |
</text> | |
<rect | |
x="1" y="1" width="398" height="28" fill="none" | |
stroke-width="1" stroke="#cc0000" | |
/> | |
</svg> | |
) | |
end | |
end | |
end |
I found this gist and approach really very helpful, and am using it now in our middleman-driven site. I made a few tweaks someone might find useful:
# /helpers/image_helpers.rb
module ImageHelpers
# usage <%= inline_svg("logo"); %> assuming logo.svg is stored at source/assets/icons/logo.svg
def inline_svg(filename, options = {})
asset = "source/assets/icons/#{filename}.svg"
if File.exists?(asset)
file = File.open(asset, 'r') { |f| f.read }
# we pass svg-targeting css classes through here. The class targets fill, stroke, poly, circle, etc.
css_class = options[:class]
aspect_ratio = options[:preserveAspectRatio]
# added some aspect-ratio settings; default covers most of our svg use cases. We want all our SVG icon artwork to scale to fit. This could be an argument if needed.
radio_default = "xMidYMid meet"
return file if css_class.nil?
document = Oga.parse_xml(file)
svg = document.css('svg').first
svg.set(:class, css_class)
svg.set(:preserveAspectRatio, radio_default)
document.to_xml
else
# added a little more helpful info here so it's easier to track down a problem.
puts "inline_svg '#{asset}' at #{current_page.url} could not be found!"
%(
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 400 60" width="400px" height="60px">
<text font-size="12" x="8" y="20" fill="#cc0000">
Error: '#{asset}' could not be found.
</text>
<rect x="1" y="1" width="398" height="38" fill="none" stroke-width="1" stroke="#cc0000" />
</svg>
)
end
end
end
I've updated this Gist to perform better and support more attributes. This work relies on the improvements made by @cveneziani and @allanwhite. Thanks so much for your contributions!
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Is there a way to modify this helper so I can use it with HAML-SVGs? (I have to set the
xlink:href
of an image with theimage_path
helper.)Example SVG: