# Inline SVG
[![CircleCI](https://circleci.com/gh/jamesmartin/inline_svg.svg?style=svg)](https://circleci.com/gh/jamesmartin/inline_svg)
Styling a SVG document with CSS for use on the web is most reliably achieved by
[adding classes to the document and
embedding](http://css-tricks.com/using-svg/) it inline in the HTML.
This gem adds a Rails helper method (`inline_svg`) that reads an SVG document (via Sprockets, so works with the Rails Asset Pipeline), applies a CSS class attribute to the root of the document and
then embeds it into a view.
Inline SVG supports [Rails 3](http://weblog.rubyonrails.org/2010/8/29/rails-3-0-it-s-done/) (from [v0.12.0](https://github.com/jamesmartin/inline_svg/releases/tag/v0.12.0)), [Rails 4](http://weblog.rubyonrails.org/2013/6/25/Rails-4-0-final/) and [Rails 5](http://weblog.rubyonrails.org/2016/6/30/Rails-5-0-final/) (from [v0.10.0](https://github.com/jamesmartin/inline_svg/releases/tag/v0.10.0)).
## Changelog
This project adheres to [Semantic Versioning](http://semver.org). All notable changes are documented in the
[CHANGELOG](https://github.com/jamesmartin/inline_svg/blob/master/CHANGELOG.md).
## Installation
Add this line to your application's Gemfile:
gem 'inline_svg'
And then execute:
$ bundle
Or install it yourself as:
$ gem install inline_svg
## Usage
```
inline_svg(file_name, options={})
```
The `file_name` can be a full path to a file, the file's basename or an `IO`
object. The
actual path of the file on disk is resolved using
[Sprockets](://github.com/sstephenson/sprockets) (when available), a naive file finder (`/public/assets/...`) or in the case of `IO` objects the SVG data is read from the object.
This means you can pre-process and fingerprint your SVG files like other Rails assets, or choose to find SVG data yourself.
Here's an example of embedding an SVG document and applying a 'class' attribute in
HAML:
```erb
```
Here's some CSS to target the SVG, resize it and turn it an attractive shade of
blue:
```css
.some-class {
display: block;
margin: 0 auto;
fill: #3498db;
width: 5em;
height: 5em;
}
```
## Options
key | description
:---------------------- | :----------
`id` | set a ID attribute on the SVG
`class` | set a CSS class attribute on the SVG
`data` | add data attributes to the SVG (supply as a hash)
`size` | set width and height attributes on the SVG Can also be set using `height` and/or `width` attributes, which take precedence over `size` Supplied as "{Width} * {Height}" or "{Number}", so "30px\*45px" becomes `width="30px"` and `height="45px"`, and "50%" becomes `width="50%"` and `height="50%"`
`title` | add a \ node inside the top level of the SVG document
`desc` | add a \ node inside the top level of the SVG document
`nocomment` | remove comment tags from the SVG document
`preserve_aspect_ratio` | adds a `preserveAspectRatio` attribute to the SVG
`aria` | adds common accessibility attributes to the SVG (see [PR #34](https://github.com/jamesmartin/inline_svg/pull/34#issue-152062674) for details)
Example:
```ruby
inline_svg("some-document.svg", id: 'some-id', class: 'some-class', data: {some: "value"}, size: '30% * 20%', title: 'Some Title', desc:
'Some description', nocomment: true, preserve_aspect_ratio: 'xMaxYMax meet', aria: true)
```
## Accessibility
Use the `aria: true` option to make `inline_svg` add the following
accessibility (a11y) attributes to your embedded SVG:
* Adds a `role="img"` attribute to the root SVG element
* Adds a `aria-labelled-by="title-id desc-id"` attribute to the root SVG
element, if the document contains `` or `` elements
Here's an example:
```erb
<%=
inline_svg('iconmonstr-glasses-12-icon.svg',
aria: true, title: 'An SVG',
desc: 'This is my SVG. There are many like it. You get the picture')
%>
```
```xml
```
***Note:*** The title and desc `id` attributes generated for, and referenced by, `aria-labelled-by` are one-way digests based on the value of the title and desc elements and an optional "salt" value using the SHA1 algorithm. This reduces the chance of `inline_svg` embedding elements inside the SVG with `id` attributes that clash with other elements elsewhere on the page.
## Custom Transformations
The transformation behavior of `inline_svg` can be customized by creating custom transformation classes.
For example, inherit from `InlineSvg::CustomTransformation` and implement the `#transform` method:
```ruby
# Sets the `custom` attribute on the root SVG element to supplied value
# Remember to return a document, as this will be passed along the transformation chain
class MyCustomTransform < InlineSvg::CustomTransformation
def transform(doc)
doc = Nokogiri::XML::Document.parse(doc.to_html)
svg = doc.at_css 'svg'
svg['custom'] = value
doc
end
end
```
Add the custom configuration in an initializer (E.g. `./config/initializers/inline_svg.rb`):
```ruby
# Note that the named `attribute` will be used to pass a value to your custom transform
InlineSvg.configure do |config|
config.add_custom_transformation(attribute: :my_custom_attribute, transform: MyCustomTransform)
end
```
The custom transformation can then be called like so:
```haml
%div
= inline_svg "some-document.svg", my_custom_attribute: 'some value'
```
In this example, the following transformation would be applied to a SVG document:
```xml
```
You can also provide a default_value to the custom transformation, so even if you don't pass a value it will be triggered
```ruby
# Note that the named `attribute` will be used to pass a value to your custom transform
InlineSvg.configure do |config|
config.add_custom_transformation(attribute: :my_custom_attribute, transform: MyCustomTransform, default_value: 'default value')
end
```
The custom transformation will be triggered even if you don't pass any attribute value
```haml
%div
= inline_svg "some-document.svg"
= inline_svg "some-document.svg", my_custom_attribute: 'some value'
```
In this example, the following transformation would be applied to a SVG document:
```xml
```
And
```xml
```
Passing a `priority` option with your custom transformation allows you to
control the order that transformations are applied to the SVG document:
```ruby
InlineSvg.configure do |config|
config.add_custom_transformation(attribute: :custom_one, transform: MyCustomTransform, priority: 1)
config.add_custom_transformation(attribute: :custom_two, transform: MyOtherCustomTransform, priority: 2)
end
```
Transforms are applied in ascending order (lowest number first).
***Note***: Custom transformations are always applied *after* all built-in
transformations, regardless of priority.
## Custom asset file loader
An asset file loader returns a `String` representing a SVG document given a
filename. Custom asset loaders should be a Ruby object that responds to a
method called `named`, that takes one argument (a string representing the
filename of the SVG document).
A simple example might look like this:
```ruby
class MyAssetFileLoader
def self.named(filename)
# ... load SVG document however you like
return ""
end
end
```
Configure your custom asset file loader in an initializer like so:
```ruby
InlineSvg.configure do |config|
config.asset_file = MyAssetFileLoader
end
```
## Contributing
1. Fork it ( [http://github.com/jamesmartin/inline_svg/fork](http://github.com/jamesmartin/inline_svg/fork) )
2. Create your feature branch (`git checkout -b my-new-feature`)
3. Commit your changes (`git commit -am 'Add some feature'`)
4. Push to the branch (`git push origin my-new-feature`)
5. Create new Pull Request
Please write tests for anything you change, add or fix.
There is a [basic Rails
app](http://github.com/jamesmartin/inline_svg_test_app) that demonstrates the
gem's functionality in use.