Phlex::SVG
Class for building SVG (Scalable Vector Graphics) views in Phlex. Inherits from Phlex::SGML and provides SVG-specific functionality including all SVG elements and CDATA support.
Inheritance
class Phlex::SVG < Phlex::SGML
end
Inherits all methods from Phlex::SGML.
Usage
Create an SVG component by subclassing Phlex::SVG and defining a view_template method:
class MyIcon < Phlex::SVG
def view_template
svg(width: 24, height: 24, viewBox: "0 0 24 24") do
circle(cx: 12, cy: 12, r: 10, fill: "blue")
path(d: "M12 2L2 7l10 5 10-5-10-5z", fill: "white")
end
end
end
MyIcon.call # => "<svg width=\"24\" height=\"24\">..."
Instance Methods
#content_type
Returns the MIME type for SVG content.
MyIcon.new.content_type # => "image/svg+xml"
Returns: "image/svg+xml"
#filename
Override to provide a filename when serving the SVG file.
class Logo < Phlex::SVG
def filename
"logo.svg"
end
end
Default: nil
#cdata(content = nil, &block)
Output a CDATA section. CDATA sections are used to include text that should not be parsed as markup.
# With string content
style do
cdata("path { fill: red; }")
end
# => <style><![CDATA[path { fill: red; }]]></style>
# With block
style do
cdata do
<<~CSS
.icon { stroke: black; }
.icon:hover { stroke: blue; }
CSS
end
end
String content to wrap in CDATA (mutually exclusive with block)
Block that returns content to wrap in CDATA (mutually exclusive with content)
Raises Phlex::ArgumentError if:
- Both content and block are provided
- Neither content nor block is provided
- Content is not a String when provided
The method automatically escapes any ]]> sequences in the content by replacing them with ]]>]]<![CDATA[.
#tag(name, **attributes, &block)
Output an SVG element dynamically by name.
@shape = :rect
tag(@shape, x: 10, y: 10, width: 100, height: 50, fill: "red")
# => <rect x="10" y="10" width="100" height="50" fill="red"></rect>
# Works with custom elements (must contain hyphen)
tag(:my_custom_shape, points: "0,0 50,50")
# => <my-custom-shape points="0,0 50,50"></my-custom-shape>
Tag name as a symbol (underscores converted to hyphens)
SVG attributes for the element
Content to render inside the element
Raises Phlex::ArgumentError if:
- Tag name is not a Symbol
- Invalid SVG tag name
SVG Elements
Phlex::SVG includes all standard SVG elements as methods:
# Basic shapes
circle(cx: 50, cy: 50, r: 40)
rect(x: 10, y: 10, width: 100, height: 100)
ellipse(cx: 100, cy: 50, rx: 100, ry: 50)
line(x1: 0, y1: 0, x2: 100, y2: 100)
polygon(points: "0,0 100,0 50,100")
polyline(points: "0,0 100,0 100,100")
path(d: "M10 10 H 90 V 90 H 10 L 10 10")
# Container elements
g(transform: "translate(50, 50)") do
circle(r: 20)
end
defs do
linearGradient(id: "grad") do
stop(offset: "0%", style: "stop-color:rgb(255,255,0)")
stop(offset: "100%", style: "stop-color:rgb(255,0,0)")
end
end
# Text elements
text(x: 10, y: 20, fill: "black") { "Hello SVG" }
tspan(x: 10, dy: 20) { "Line 2" }
# Filter effects
filter(id: "blur") do
feGaussianBlur(in: "SourceGraphic", stdDeviation: 5)
end
Available SVG elements include:
svg, g, defs, symbol, use, marker, clipPath, mask, pattern, linearGradient, radialGradient, stop, circle, ellipse, line, polygon, polyline, rect, path, text, tspan, textPath, a, animate, animateMotion, animateTransform, set, feBlend, feColorMatrix, feComponentTransfer, feComposite, feConvolveMatrix, feDiffuseLighting, feDisplacementMap, feDistantLight, feDropShadow, feFlood, feFuncA, feFuncB, feFuncG, feFuncR, feGaussianBlur, feImage, feMerge, feMergeNode, feMorphology, feOffset, fePointLight, feSpecularLighting, feSpotLight, feTile, feTurbulence, filter, foreignObject, image, switch, style, script, title, desc, metadata
Common Patterns
Complete SVG Document
class Logo < Phlex::SVG
def view_template
svg(
xmlns: "http://www.w3.org/2000/svg",
viewBox: "0 0 200 200",
width: 200,
height: 200
) do
defs do
linearGradient(id: "gradient") do
stop(offset: "0%", stop_color: "#ff0000")
stop(offset: "100%", stop_color: "#0000ff")
end
end
circle(cx: 100, cy: 100, r: 80, fill: "url(#gradient)")
text(x: 100, y: 110, text_anchor: "middle", fill: "white", font_size: 24) do
plain "LOGO"
end
end
end
end
Icon Component
class ChevronIcon < Phlex::SVG
def initialize(direction: :down)
@direction = direction
end
def view_template
svg(width: 24, height: 24, viewBox: "0 0 24 24", fill: "none") do
path(
d: path_data,
stroke: "currentColor",
stroke_width: 2,
stroke_linecap: "round",
stroke_linejoin: "round"
)
end
end
private
def path_data
case @direction
when :up then "M18 15l-6-6-6 6"
when :down then "M6 9l6 6 6-6"
when :left then "M15 18l-6-6 6-6"
when :right then "M9 6l6 6-6 6"
end
end
end
Animated SVG
class SpinningLoader < Phlex::SVG
def view_template
svg(width: 50, height: 50, viewBox: "0 0 50 50") do
style do
cdata(<<~CSS)
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
.spinner { animation: spin 1s linear infinite; }
CSS
end
circle(
class: "spinner",
cx: 25,
cy: 25,
r: 20,
fill: "none",
stroke: "#3498db",
stroke_width: 4,
stroke_dasharray: "80, 200"
)
end
end
end
Reusable SVG Symbols
class IconSprite < Phlex::SVG
def view_template
svg(style: "display: none;") do
defs do
symbol(id: "icon-home", viewBox: "0 0 24 24") do
path(d: "M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z")
end
symbol(id: "icon-user", viewBox: "0 0 24 24") do
circle(cx: 12, cy: 8, r: 4)
path(d: "M6 21v-2a4 4 0 0 1 4-4h4a4 4 0 0 1 4 4v2")
end
end
end
end
end
# Usage in HTML
class Page < Phlex::HTML
def view_template
render IconSprite.new
svg(width: 24, height: 24) do
use(href: "#icon-home")
end
end
end
Embedding SVG in HTML
class Card < Phlex::HTML
def view_template
div(class: "card") do
# Use the svg helper to embed SVG
svg(width: 100, height: 100, viewBox: "0 0 100 100") do
circle(cx: 50, cy: 50, r: 40, fill: "blue")
end
h2 { "Card Title" }
p { "Card content" }
end
end
end
Dynamic SVG Generation
class BarChart < Phlex::SVG
def initialize(data)
@data = data
end
def view_template
svg(width: 400, height: 200, viewBox: "0 0 400 200") do
@data.each_with_index do |(label, value), i|
x = i * 50 + 10
height = value * 2
rect(
x: x,
y: 200 - height,
width: 40,
height: height,
fill: "steelblue"
)
text(x: x + 20, y: 195, text_anchor: "middle", font_size: 12) do
plain label
end
end
end
end
end
BarChart.call([("A", 45), ("B", 70), ("C", 35)])
See Also