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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 524e3d4f79d939259c43efa7e1571db7e507f852d8655f17b4afd4698e384e74
4
- data.tar.gz: cf20bc89defef0a67d3fa767d53bab564a7ad1a3e36097bc55df9732a83682aa
3
+ metadata.gz: 38690808a8ba07f5bfb35310eee321d2f2eaec78799384847d756ba6661800cc
4
+ data.tar.gz: c438a87c7cc767734786e4694b50d0384e938447f0fa53c5c039f6febcad4e2f
5
5
  SHA512:
6
- metadata.gz: e37ae9efd64fe9709d70bddc2eb37c13636052986d2eb035b8ad8ed40ac86084eab9e896bbfbeb32c75e3cd93b79dab396f52101b3329f4bdcc70b729b188f41
7
- data.tar.gz: 1e2c82c275b670a5468856008cdf76f30acbc2f9c6d5a20900bade8187bd73f161614d493330f8f097eefb2b5e5b827523a73e787a16a6a2336c9a4d44e51227
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
- ## 🚀 Why Stylicon? Performance First.
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-user {
67
- background-image: url('data:image/svg+xml;base64,PHN2Zy...');
68
- width: 20px;
69
- height: 20px;
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
- FileUtils.mkdir_p(@output_folder)
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
- Stylicon::Transformer.new(
23
- input_svg: input_file,
24
- output_svg: output_file,
25
- **@transform_options
26
- ).transform
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
@@ -10,11 +10,22 @@ module Stylicon
10
10
  end
11
11
 
12
12
  def generate
13
- config = YAML.load_file(@config_path)
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
@@ -14,8 +14,26 @@ module Stylicon
14
14
  end
15
15
 
16
16
  def transform
17
- doc = Nokogiri::XML(File.read(@input_svg))
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
- File.write(@output_svg, doc.to_xml)
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Stylicon
4
- VERSION = "0.2.0"
4
+ VERSION = "0.2.1"
5
5
  end
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.0
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.7.1
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: []