class Inkcite::Renderer::Slant
@helper Slanted Edge @summary Bulletproof, responsive, sloped edges for modern email designs
@description The {slant} Helper provides consistent, reliable sloped edges between sections of your email. You can customize the size, color and dimensions of the slant. It renders using CSS in modern email clients and has a VML fallback for Outlook.
@warning Slants are not compatible with Outlook 2016 which renders a thin, white border around the slant. @warning Older email clients render angled CSS borders without anti-aliasing
@credit Slanted edges based on @M_J_Robbins sloped edges for email concept. codepen.io/M_J_Robbins/pen/rpzLNx
@usage {div width=600 padding=15 bgcolor=#009}
...
{/div} {slant bgcolor=#009 color=#909 bottom right width=600 height=50} {div width=600 padding=15 bgcolor=#909}
...
{/div}
Constants
- COORDINATE_SCALE
Arbitrary multiplier present in the original codepen.
- IF_OUTLOOK_LT_2016
This is the MSO conditional used to prevent the slant from appearing in Outlook 2016 where it has thin lines around the VML according to Litmus results.
- NO_FALLBACK
Flags allowing the user to disable VML fallbacks.
- NO_VML
- NO_WRAP
Tag allowing the user to disable the wrap element if they don't want it
- TRANSPARENT
Static constant for the color used where the border is transparent to create the slanted edge.
Public Instance Methods
# File lib/inkcite/renderer/slant.rb, line 26 def render tag, opt, ctx html = '' # @attribute no-fallback If present, disables VML fallback so the slant doesn't appear in Outlook 2007-2013.; alias no-vml no_vml = opt[NO_FALLBACK] || opt[NO_VML] # Check to see if VML is enabled for this email include_fallback = ctx.vml_enabled? && !no_vml # The first time the slant is used, we need to initialize the VML shapes # and the responsive class that will be used to scale this. if include_fallback if ctx.once?(:slant) # Notify the context that VML has been used in this email. ctx.vml_used! html << IF_OUTLOOK_LT_2016 # These need to be wrapped in a div with zero height otherwise unwanted whitespace will # appear in the email where these shapes are initialized. # https://litmus.com/community/discussions/538-vml-outlook-07-10-13-unwanted-20px-padding-at-the-bottom html << '{div font-size=0 line-height=0}' html << render_vml_triangle('stl', [ [0, 1], [1, 0], [0, 0] ]) html << render_vml_triangle('str', [ [0, 0], [1, 0], [1, 1] ]) html << render_vml_triangle('sbl', [ [0, 0], [1, 1], [0, 1] ]) html << render_vml_triangle('sbr', [ [0, 1], [1, 1], [1, 0] ]) html << '{/div}' html << '{/if}' end end # @attribute height The height of the slope in pixels; required height = opt[:height].to_i # @attribute width The width of the slope in pixels; required width = opt[:width].to_i if height <= 0 || width <= 0 ctx.error('Missing slant dimensions', opt) return nil end # Pre-calculate half the width and height of the slant. half_height = (height / 2.0).round(0) half_width = (width / 2.0).round(0) # @attribute color The foreground color of the slanted edge.; default #234 color = hex(opt[:color] || '#234') directions = [ (opt[:bottom] ? :bottom : :top), (opt[:left] ? :left : :right) ] border_colors = [] DIRECTIONS.each do |d| next if d.nil? border_colors << (directions.include?(d) ? color : TRANSPARENT) end # @attribute no-wrap If present, disables the table that wraps the slant. Use this if the slant is already in a container (e.g. table or div) that provides width and background color. no_wrap = opt[NO_WRAP] unless no_wrap wrap_div = Element.new('table') wrap_div[:width] = width # @attribute bgcolor The color that appears behind the slanted edge; default transparent; alias background-color bgcolor = detect_bgcolor(opt) wrap_div[:bgcolor] = bgcolor unless none?(bgcolor) wrap_div[:mobile] = :fill html << wrap_div.to_helper html << '{td}' end # This zero-sized div will have beefed up borders that create the # slant effect. slant_div = Element.new('div') slant_div.style[BORDER_COLOR] = border_colors.join(' ') slant_div.style[BORDER_STYLE] = :solid slant_div.style[BORDER_WIDTH] = "#{px(half_height)} #{px(half_width)}" slant_div.style[MSO_HIDE] = :all # Fix to trigger antialiasing in the slanted border on webkit clients # like Outlook for iOS # https://stackoverflow.com/a/27506977 slant_div.style[:'-webkit-transform'] = 'rotate(0.0005deg)' # Create a custom mobile class name for this slant. Then check to # see if that responsive rule has already been registered - otherwise # add it. klass = "slant#{half_height}" slant_rule = ctx.media_query.find_by_klass(klass) || Rule.new('div', klass, "border-width: #{px(half_height)} 50vw !important;") ctx.media_query << slant_div.add_rule(slant_rule) html << slant_div.to_s html << '</div>' if include_fallback backward = false; vshape = Element.new('v:shape') # Generate the unique VML shape ID from the direction of the # 90-degree corner. type_id = "s" # Slant type_id << (opt[:bottom] ? 'b' : 't') type_id << (opt[:left] ? 'l' : 'r') vshape[:type] = quote(type_id) vshape.style[:width] = px(width) vshape.style[:height] = px(height) vshape.style[:'mso-position-horizontal'] = :center vshape[:fillcolor] = color vshape[:stroked] = :f # Render the outlook-only VML-based fallback. Once again, it needs # to be wrapped in a zero-height div to prevent unwanted space from # being injected on certain version of outlook. html << IF_OUTLOOK_LT_2016 html << '{div font-size=0 line-height=0}' html << vshape.to_s html << '<o:lock selection="t"/>' html << '</v:shape>' html << '{/div}' html << '{/if}' end # Close the wrap element. html << '{/td}{/table}' unless no_wrap html end
Private Instance Methods
# File lib/inkcite/renderer/slant.rb, line 166 def render_vml_coordinate coord x, y = coord "#{x * COORDINATE_SCALE},#{y * COORDINATE_SCALE}" end
# File lib/inkcite/renderer/slant.rb, line 171 def render_vml_triangle id, coords path = '' path << 'm' # moveto path << render_vml_coordinate(coords[0]) path << 'l' # lineto path << render_vml_coordinate(coords[1]) path << ',' path << render_vml_coordinate(coords[2]) path << 'x' # close path << 'e' # end %Q(<v:shapetype id="#{id}" path="#{path}" xmlns:v="urn:schemas-microsoft-com:vml"/>) end