stylicon 0.2.0 → 0.2.1
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.
- checksums.yaml +4 -4
- data/README.md +76 -29
- data/lib/stylicon/batch_transformer.rb +28 -7
- data/lib/stylicon/generator.rb +23 -1
- data/lib/stylicon/transformer.rb +31 -2
- data/lib/stylicon/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 38690808a8ba07f5bfb35310eee321d2f2eaec78799384847d756ba6661800cc
|
|
4
|
+
data.tar.gz: c438a87c7cc767734786e4694b50d0384e938447f0fa53c5c039f6febcad4e2f
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: dba8bcf1152535c48c72c3bb4cd81ccb0f603cad3314e04fe0284ecf2547082d42884e07572a9b4c34b71b6a1da2433d730ae5e640dd0c4ca6d2a3fe303851c4
|
|
7
|
+
data.tar.gz: bea0b94eaa5ea0b1f9f29d898154ad31620f00b94bb7e3d900380141db0df79b97cb30f4f987aa5c3607fb6a1c5a9dd5a05e7757b41ce7b39ff4f04bdfa1980a
|
data/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
⚡ **High-Performance Icon System** - Generate lightning-fast CSS icon classes from SVG files. Dramatically improve your application's performance by replacing inline SVGs with cacheable, optimized CSS classes.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Performance First.
|
|
6
6
|
|
|
7
7
|
Traditional icon approaches hurt performance:
|
|
8
8
|
|
|
@@ -20,6 +20,42 @@ Stylicon generates **cacheable CSS classes**:
|
|
|
20
20
|
<i class="icon-edit"></i>
|
|
21
21
|
```
|
|
22
22
|
|
|
23
|
+
## Performance Benchmarks
|
|
24
|
+
|
|
25
|
+
I tested Stylicon against traditional SVG approaches to see if the performance claims hold up.
|
|
26
|
+
|
|
27
|
+
### Test Setup
|
|
28
|
+
- Users table with 100 rows, 3 action icons per row (300 icons total)
|
|
29
|
+
- Tested with `oha` HTTP load tester: 100 requests, 10 concurrent connections
|
|
30
|
+
- Rails 7 development server, local environment
|
|
31
|
+
- Real-world scenario: Edit, Show, Delete icons
|
|
32
|
+
|
|
33
|
+
### Results
|
|
34
|
+
|
|
35
|
+
| Approach | Requests/sec | Avg Response Time | Total Payload | HTTP Requests |
|
|
36
|
+
|----------|-------------|------------------|---------------|---------------|
|
|
37
|
+
| Stylicon CSS | 99.7 | 96.5ms | 49.79 KiB | 2 |
|
|
38
|
+
| Inline SVG | 98.0 | 98.4ms | 256.54 KiB | 1 |
|
|
39
|
+
| Rails image_tag | 32.9 | 294.1ms | 71.47 KiB | 301 |
|
|
40
|
+
|
|
41
|
+
### Analysis
|
|
42
|
+
|
|
43
|
+
**Server Performance**
|
|
44
|
+
Stylicon handles 3x more requests per second than the Rails image_tag approach. The difference comes from HTTP
|
|
45
|
+
request overhead - serving 300 individual SVG files per page puts significant load on the Rails server.
|
|
46
|
+
|
|
47
|
+
**Payload Size**
|
|
48
|
+
Stylicon generates the smallest payload at under 50 KiB. Inline SVG creates massive HTML files (256 KiB) because the
|
|
49
|
+
same SVG markup gets repeated 100 times. This affects loading speed, especially on slower connections.
|
|
50
|
+
|
|
51
|
+
**HTTP Requests**
|
|
52
|
+
The Rails image_tag approach creates 301 HTTP requests per page load (1 for HTML + 300 for individual SVGs).
|
|
53
|
+
Stylicon uses just 2 requests - the HTML and one cacheable CSS file.
|
|
54
|
+
|
|
55
|
+
**Caching Benefits**
|
|
56
|
+
The CSS file generated by Stylicon gets cached by browsers, so subsequent page loads and navigation only require the
|
|
57
|
+
HTML request. This compounds the performance advantage over time
|
|
58
|
+
|
|
23
59
|
## Features
|
|
24
60
|
|
|
25
61
|
- 🎨 **CSS Generation**: Convert SVG files to optimized CSS classes with base64 encoding
|
|
@@ -60,14 +96,49 @@ stylicon [config.yml] [output.css]
|
|
|
60
96
|
stylicon icons.yml stylicon.css
|
|
61
97
|
```
|
|
62
98
|
|
|
99
|
+
Create an `icons.yml` configuration file:
|
|
100
|
+
```yaml
|
|
101
|
+
# icons.yml - Configuration for CSS generation
|
|
102
|
+
edit:
|
|
103
|
+
path: icons/edit.svg
|
|
104
|
+
class: ".icon-edit"
|
|
105
|
+
background: "#333"
|
|
106
|
+
|
|
107
|
+
user:
|
|
108
|
+
path: icons/user.svg
|
|
109
|
+
class: [".icon-user", ".user-icon"]
|
|
110
|
+
background: "currentColor"
|
|
111
|
+
|
|
112
|
+
home:
|
|
113
|
+
path: icons/home.svg
|
|
114
|
+
class: ".icon-home"
|
|
115
|
+
# background optional - will use mask mode
|
|
116
|
+
|
|
117
|
+
delete:
|
|
118
|
+
path: icons/delete.svg
|
|
119
|
+
class: ".icon-delete"
|
|
120
|
+
background: "#e74c3c"
|
|
121
|
+
```
|
|
122
|
+
|
|
63
123
|
This creates a **single cacheable CSS file** with all your icons as base64-encoded background images:
|
|
64
124
|
|
|
65
125
|
```css
|
|
66
|
-
.icon-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
126
|
+
.icon-edit {
|
|
127
|
+
-webkit-mask-image: url("data:image/svg+xml;base64,PHN2Zy...");
|
|
128
|
+
-webkit-mask-repeat: no-repeat;
|
|
129
|
+
-webkit-mask-size: contain;
|
|
130
|
+
-webkit-mask-position: center;
|
|
131
|
+
display: inline-block;
|
|
132
|
+
background-color: #333;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
.icon-user, .user-icon {
|
|
136
|
+
-webkit-mask-image: url("data:image/svg+xml;base64,PHN2Zy...");
|
|
137
|
+
-webkit-mask-repeat: no-repeat;
|
|
138
|
+
-webkit-mask-size: contain;
|
|
139
|
+
-webkit-mask-position: center;
|
|
70
140
|
display: inline-block;
|
|
141
|
+
background-color: currentColor;
|
|
71
142
|
}
|
|
72
143
|
```
|
|
73
144
|
|
|
@@ -118,30 +189,6 @@ stylicon --transform-svg "assets/icons/user-*.svg" --out output/ --stroke red --
|
|
|
118
189
|
stylicon --transform-svg "**/*.svg" --out flattened/ --height 24 --width 24
|
|
119
190
|
```
|
|
120
191
|
|
|
121
|
-
## Performance Benchmarks
|
|
122
|
-
|
|
123
|
-
### Icon Loading Performance
|
|
124
|
-
|
|
125
|
-
**Test: 100 icons used 10 times each (1,000 total icon instances)**
|
|
126
|
-
|
|
127
|
-
| Method | HTML Size | HTTP Requests | Cache Efficiency | Load Time |
|
|
128
|
-
|--------|-----------|---------------|------------------|-----------|
|
|
129
|
-
| Inline SVG | 2.3MB | 1 | 0% | 850ms |
|
|
130
|
-
| Individual Icon Files | 45KB | 100 | 80% | 420ms |
|
|
131
|
-
| **Stylicon CSS** | **67KB** | **1** | **100%** | **95ms** |
|
|
132
|
-
|
|
133
|
-
### Real Application Examples
|
|
134
|
-
|
|
135
|
-
**E-commerce Site (500 product icons):**
|
|
136
|
-
- Before: 15MB HTML, 3.2s load time
|
|
137
|
-
- After: 890KB HTML + 45KB CSS, 0.8s load time
|
|
138
|
-
- **75% faster loading**
|
|
139
|
-
|
|
140
|
-
**Admin Dashboard (200 UI icons):**
|
|
141
|
-
- Before: 8MB repeated SVG code
|
|
142
|
-
- After: 380KB total, cached across all pages
|
|
143
|
-
- **95% reduction in icon-related bandwidth**
|
|
144
|
-
|
|
145
192
|
## Command Line Options
|
|
146
193
|
|
|
147
194
|
### Core Options
|
|
@@ -14,19 +14,40 @@ module Stylicon
|
|
|
14
14
|
|
|
15
15
|
def transform
|
|
16
16
|
files = find_svg_files
|
|
17
|
-
|
|
17
|
+
abort "Error: No SVG files found" if files.empty?
|
|
18
|
+
|
|
19
|
+
begin
|
|
20
|
+
FileUtils.mkdir_p(@output_folder)
|
|
21
|
+
rescue Errno::EACCES
|
|
22
|
+
abort "Error: Permission denied creating directory #{@output_folder}"
|
|
23
|
+
rescue => e
|
|
24
|
+
abort "Error: Failed to create directory #{@output_folder}: #{e.message}"
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
failures = []
|
|
18
28
|
|
|
19
29
|
files.each do |input_file|
|
|
20
30
|
output_file = File.join(@output_folder, File.basename(input_file))
|
|
31
|
+
begin
|
|
32
|
+
Stylicon::Transformer.new(
|
|
33
|
+
input_svg: input_file,
|
|
34
|
+
output_svg: output_file,
|
|
35
|
+
**@transform_options
|
|
36
|
+
).transform
|
|
37
|
+
rescue => e
|
|
38
|
+
failures << { file: input_file, error: e.message }
|
|
39
|
+
end
|
|
40
|
+
end
|
|
21
41
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
42
|
+
if failures.any?
|
|
43
|
+
puts "Failed to transform #{failures.length} files:"
|
|
44
|
+
failures.each do |failure|
|
|
45
|
+
puts " #{failure[:file]}: #{failure[:error]}"
|
|
46
|
+
end
|
|
47
|
+
abort "Error: Failed to transform some files"
|
|
27
48
|
end
|
|
28
49
|
|
|
29
|
-
puts "🎨 Transformed #{files.length} SVG files to #{@output_folder}"
|
|
50
|
+
puts "🎨 Transformed #{files.length} SVG files to #{@output_folder} successfully"
|
|
30
51
|
end
|
|
31
52
|
|
|
32
53
|
private
|
data/lib/stylicon/generator.rb
CHANGED
|
@@ -10,11 +10,22 @@ module Stylicon
|
|
|
10
10
|
end
|
|
11
11
|
|
|
12
12
|
def generate
|
|
13
|
-
|
|
13
|
+
begin
|
|
14
|
+
config = YAML.load_file(@config_path)
|
|
15
|
+
rescue Errno::ENOENT
|
|
16
|
+
abort "Error: Config file not found: #{@config_path}"
|
|
17
|
+
rescue Psych::SyntaxError => e
|
|
18
|
+
abort "Error: Config file is not valid YAML: #{@config_path}: #{e.message}"
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
abort "Error: Config file is empty: #{@config_path}" if config.nil? || config.empty?
|
|
22
|
+
|
|
14
23
|
css = ""
|
|
15
24
|
|
|
16
25
|
config.each do |name, opts|
|
|
17
26
|
path = opts["path"]
|
|
27
|
+
abort "Error: No path specified for icon #{name} in config" if path.nil? || path.empty?
|
|
28
|
+
abort "Error: SVG file not found: #{path}" unless File.exist?(path)
|
|
18
29
|
classes = Array(opts["class"] || ".icon-#{name}")
|
|
19
30
|
background = opts["background"]
|
|
20
31
|
|
|
@@ -22,6 +33,7 @@ module Stylicon
|
|
|
22
33
|
doc = Nokogiri::XML(svg)
|
|
23
34
|
|
|
24
35
|
svg_node = doc.at_css("svg")
|
|
36
|
+
abort "Error: no svg element found in #{path}" if svg_node.nil?
|
|
25
37
|
svg_node.remove_attribute("fill")
|
|
26
38
|
svg_node.remove_attribute("width")
|
|
27
39
|
svg_node.remove_attribute("height")
|
|
@@ -43,8 +55,18 @@ module Stylicon
|
|
|
43
55
|
RULE
|
|
44
56
|
end
|
|
45
57
|
|
|
58
|
+
begin
|
|
46
59
|
File.write(@output_path, css)
|
|
47
60
|
puts "✅ stylicon.css written to #{@output_path}"
|
|
61
|
+
rescue Errno::ENOENT
|
|
62
|
+
abort "Error: output directory does not exist: #{File.dirname(@output_path)}"
|
|
63
|
+
rescue Errno::EACCES
|
|
64
|
+
abort "Error: Permission denied writing to #{@output_path}"
|
|
65
|
+
rescue Errno::ENOSPC
|
|
66
|
+
abort "Error: Not enogh space on device when writing to #{@output_path}"
|
|
67
|
+
rescue => e
|
|
68
|
+
abort "Error: Failed to write output file: #{e.message}"
|
|
69
|
+
end
|
|
48
70
|
end
|
|
49
71
|
end
|
|
50
72
|
end
|
data/lib/stylicon/transformer.rb
CHANGED
|
@@ -14,8 +14,26 @@ module Stylicon
|
|
|
14
14
|
end
|
|
15
15
|
|
|
16
16
|
def transform
|
|
17
|
-
|
|
17
|
+
begin
|
|
18
|
+
svg_content = File.read(@input_svg)
|
|
19
|
+
rescue Errno::ENOENT
|
|
20
|
+
abort "Error: Input SVG file not found: #{@input_svg}"
|
|
21
|
+
rescue Errno::EACCES
|
|
22
|
+
abort "Error: Permission denied reading file #{@input_svg}"
|
|
23
|
+
rescue Errno::EISDIR
|
|
24
|
+
abort "Error: Path is a directory, not a file: #{@input_svg}"
|
|
25
|
+
rescue => e
|
|
26
|
+
abort "Error: Failed to read input file: #{e.message}"
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
doc = Nokogiri::XML(svg_content)
|
|
30
|
+
|
|
31
|
+
if doc.errors.any?
|
|
32
|
+
abort "Invalid XML in #{@input_svg}: #{doc.errors.first.message}"
|
|
33
|
+
end
|
|
34
|
+
|
|
18
35
|
svg = doc.at_css("svg")
|
|
36
|
+
abort "Error: No svg element found in #{@input_svg}" if svg.nil?
|
|
19
37
|
|
|
20
38
|
svg["style"] = [svg["style"], @style].compact.join("; ") if @style
|
|
21
39
|
svg["class"] = [svg["class"], @classes].compact.join(" ") if @classes
|
|
@@ -40,7 +58,18 @@ module Stylicon
|
|
|
40
58
|
end
|
|
41
59
|
end
|
|
42
60
|
|
|
43
|
-
|
|
61
|
+
begin
|
|
62
|
+
File.write(@output_svg, doc.to_xml)
|
|
63
|
+
rescue Errno::ENOENT
|
|
64
|
+
abort "Error: Output directory does not exist: #{File.dirname(@output_svg)}"
|
|
65
|
+
rescue Errno::EACCES
|
|
66
|
+
abort "Error: Permission denied writing to #{@output_svg}"
|
|
67
|
+
rescue Errno::ENOSPC
|
|
68
|
+
abort "Error: No space left on device when writing #{@output_svg}"
|
|
69
|
+
rescue => e
|
|
70
|
+
abort "Error: Failed to write output file: #{e.message}"
|
|
71
|
+
end
|
|
72
|
+
|
|
44
73
|
puts "🎨 Transformed SVG written to #{@output_svg}"
|
|
45
74
|
end
|
|
46
75
|
end
|
data/lib/stylicon/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: stylicon
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.2.
|
|
4
|
+
version: 0.2.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- JG
|
|
@@ -58,7 +58,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
58
58
|
- !ruby/object:Gem::Version
|
|
59
59
|
version: '0'
|
|
60
60
|
requirements: []
|
|
61
|
-
rubygems_version: 3.
|
|
61
|
+
rubygems_version: 3.6.9
|
|
62
62
|
specification_version: 4
|
|
63
63
|
summary: Generate performant CSS classes from SVGs using YAML configuration.
|
|
64
64
|
test_files: []
|