view_component-scoped_styles 0.1.0

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 43108a25040819bdeee257cd43228632e8de7822b24e96d675625208ff5f1544
4
+ data.tar.gz: 1023d48e94469ea2db73b971eb0c09440bdfb18167aea8abb461cb45e5bc6ed5
5
+ SHA512:
6
+ metadata.gz: e40669a1884ae189bc18edcf04cad923e210875078095d6fd7238b3dd9ff061172253f8c6dd86632ee207cd3e1ae108bf04d1dac1866daedd948c078e0a95e50
7
+ data.tar.gz: 6f67241201f8e8452e8eec7e316ff4a1259496347bc62f58dc6b2a7739557dc3df7f4e69ce75cc1041843dcd5d95d9a3cc40d590d7108c917f3850ae630e89e0
data/README.md ADDED
@@ -0,0 +1,226 @@
1
+ # ViewComponent::ScopedStyles
2
+
3
+ Scoped, colocated CSS for [ViewComponent](https://viewcomponent.org/).
4
+
5
+ Avoids collisions by rewriting class selectors to stable, content-derived names.
6
+
7
+ E.g. `.button` becomes `.c-a1b2c3d4`
8
+
9
+ ## Table of Contents
10
+
11
+ - [ViewComponent::ScopedStyles](#viewcomponentscopedstyles)
12
+ - [Table of Contents](#table-of-contents)
13
+ - [Installation](#installation)
14
+ - [Usage](#usage)
15
+ - [1. Using a sidecar stylesheet](#1-using-a-sidecar-stylesheet)
16
+ - [2. Using a styles block in the component](#2-using-a-styles-block-in-the-component)
17
+ - [Referencing classes](#referencing-classes)
18
+ - [Using the scoped CSS](#using-the-scoped-css)
19
+ - [Configuration](#configuration)
20
+ - [Related projects](#related-projects)
21
+ - [Development](#development)
22
+ - [Contributing](#contributing)
23
+ - [License](#license)
24
+ - [Code of Conduct](#code-of-conduct)
25
+
26
+ ## Installation
27
+
28
+ Install the gem and add to the application's Gemfile by executing:
29
+
30
+ ```bash
31
+ bundle add view_component-scoped_styles
32
+ ```
33
+
34
+ Or add it to the Gemfile manually:
35
+
36
+ ```ruby
37
+ gem "view_component-scoped_styles"
38
+ ```
39
+
40
+ If bundler is not being used to manage dependencies, install the gem by executing:
41
+
42
+ ```bash
43
+ gem install view_component-scoped_styles
44
+ ```
45
+
46
+ ## Usage
47
+
48
+ Include the module in any component class you would like to use with scoped CSS.
49
+
50
+ ```ruby
51
+ class ExampleComponent < ViewComponent::Base
52
+ include ViewComponent::ScopedStyles
53
+ end
54
+ ```
55
+
56
+ CSS can be written in two ways:
57
+
58
+ ### 1. Using a sidecar stylesheet
59
+
60
+ Learn more about sidecar [here](https://viewcomponent.org/guide/generators.html#place-the-view-in-a-sidecar-directory).
61
+
62
+ ```bash
63
+ bin/rails generate view_component:component Example title --sidecar
64
+
65
+ create app/components/example_component.rb
66
+ invoke test_unit
67
+ create test/components/example_component_test.rb
68
+ invoke erb
69
+ create app/components/example_component/example_component.html.erb
70
+
71
+ ```
72
+
73
+ Then add a matching stylesheet in the sidecar directory:
74
+
75
+ ```css
76
+ /* app/components/example_component/example_component.css */
77
+
78
+ .component {
79
+ position: relative;
80
+ }
81
+ ```
82
+
83
+ ### 2. Using a styles block in the component
84
+
85
+ ```ruby
86
+ # app/components/example_component.rb
87
+
88
+ class ExampleComponent < ViewComponent::Base
89
+ include ViewComponent::ScopedStyles
90
+
91
+ styles do
92
+ <<~CSS
93
+ .component {
94
+ position: relative;
95
+ }
96
+ CSS
97
+ end
98
+ end
99
+ ```
100
+
101
+ **NB:** Using a styles block will take precedence over a sidecar stylesheet.
102
+
103
+ ### Referencing classes
104
+
105
+ Use the `component_class` helper inside component templates to refer to the scoped CSS classes:
106
+
107
+ ```erb
108
+ <div class="<%= component_class %>">
109
+ My component content
110
+ </div>
111
+ ```
112
+
113
+ The default selector is `.component` but you can change this by defining `component_css_class` in your component:
114
+
115
+ ```ruby
116
+ class ExampleComponent < ViewComponent::Base
117
+ include ViewComponent::ScopedStyles
118
+
119
+ component_css_class "example"
120
+
121
+ styles do
122
+ <<~CSS
123
+ .example {
124
+ position: relative;
125
+ }
126
+ CSS
127
+ end
128
+ end
129
+ ```
130
+
131
+ `component_class` takes an optional string argument to reference other classes in the CSS:
132
+
133
+ ```ruby
134
+ class ExampleComponent < ViewComponent::Base
135
+ include ViewComponent::ScopedStyles
136
+
137
+ component_css_class "example"
138
+
139
+ styles do
140
+ <<~CSS
141
+ .example {
142
+ position: relative;
143
+ }
144
+
145
+ .inner {
146
+ position: absolute;
147
+ }
148
+ CSS
149
+ end
150
+ end
151
+ ```
152
+
153
+ ```erb
154
+ <div class="<%= component_class %>">
155
+ My component content
156
+
157
+ <div class="<%= component_class("inner") %>">
158
+ Inner content
159
+ </div>
160
+ </div>
161
+ ```
162
+
163
+ ### Using the scoped CSS
164
+
165
+ All scoped CSS will be compiled into `app/assets/stylesheets/components.scoped.css`.
166
+
167
+ You should import this stylesheet within your app:
168
+
169
+ ```css
170
+ /* app/assets/stylesheets/application.css */
171
+
172
+ @import url("./components.scoped.css");
173
+ ```
174
+
175
+ ## Configuration
176
+
177
+ Run the install generator in your Rails app:
178
+
179
+ ```bash
180
+ bin/rails generate view_component:scoped_styles:install
181
+ ```
182
+
183
+ That creates `config/initializers/view_component_scoped_styles.rb` with the same defaults as `ViewComponent::ScopedStyles::Configuration`.
184
+
185
+ Or create the initializer manually:
186
+
187
+ ```ruby
188
+ ViewComponent::ScopedStyles.configure do |config|
189
+ # Where ViewComponent classes live (relative to Rails.root). Default: "app/components"
190
+ config.components_path = File.join("app", "components")
191
+
192
+ # Optional @layer name for components.scoped.css (e.g. "components"). Default: nil.
193
+ config.components_layer = nil
194
+ end
195
+ ```
196
+
197
+ | Option | Default | Description |
198
+ | --- | --- | --- |
199
+ | `components_path` | `"app/components"` | Where ViewComponent classes live, relative to `Rails.root`. |
200
+ | `components_layer` | `nil` | When set, wraps generated CSS in `@layer <name> { ... }` for cascade control. |
201
+
202
+ ## Related projects
203
+
204
+ This gem was heavily inspired by Partials Fx, and indeed takes its foundations from it, modified to work with ViewComponent instead.
205
+
206
+ - https://github.com/Rails-Designer/partials_fx
207
+ - https://github.com/aileron-inc/view_component_scoped_css
208
+ - https://github.com/amkisko/style_capsule.rb
209
+
210
+ ## Development
211
+
212
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests.
213
+
214
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
215
+
216
+ ## Contributing
217
+
218
+ Bug reports and pull requests are welcome on GitHub at https://github.com/chrise86/view_component-scoped_styles. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/chrise86/view_component-scoped_styles/blob/master/CODE_OF_CONDUCT.md).
219
+
220
+ ## License
221
+
222
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
223
+
224
+ ## Code of Conduct
225
+
226
+ Everyone interacting in the ViewComponent::ScopedStyles project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/chrise86/view_component-scoped_styles/blob/master/CODE_OF_CONDUCT.md).
data/Rakefile ADDED
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rspec/core/rake_task"
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ require "rubocop/rake_task"
9
+
10
+ RuboCop::RakeTask.new
11
+
12
+ task default: %i[spec rubocop]
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "view_component/scoped_styles/configuration"
4
+ require "rails/generators"
5
+
6
+ module ViewComponent
7
+ module ScopedStyles
8
+ module Generators
9
+ # Installs +config/initializers/view_component_scoped_styles.rb+ with defaults
10
+ # from {ViewComponent::ScopedStyles::Configuration}.
11
+ #
12
+ # bin/rails generate view_component:scoped_styles:install
13
+ class InstallGenerator < Rails::Generators::Base
14
+ source_root File.expand_path("templates", __dir__)
15
+
16
+ desc "Creates a ViewComponent::ScopedStyles initializer with default configuration"
17
+
18
+ def copy_initializer
19
+ template "view_component_scoped_styles.rb.tt",
20
+ "config/initializers/view_component_scoped_styles.rb"
21
+ end
22
+
23
+ private
24
+
25
+ def configuration_defaults
26
+ @configuration_defaults ||= Configuration.new
27
+ end
28
+
29
+ def components_path_expression
30
+ segments = Pathname(configuration_defaults.components_path).each_filename.to_a
31
+ "File.join(#{segments.map { |segment| %("#{segment}") }.join(", ")})"
32
+ end
33
+
34
+ def components_layer_value
35
+ configuration_defaults.components_layer.inspect
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ ViewComponent::ScopedStyles.configure do |config|
4
+ # Where ViewComponent classes live (relative to Rails.root). Default: "app/components"
5
+ config.components_path = <%= components_path_expression %>
6
+
7
+ # Optional @layer name for components.scoped.css (e.g. "components"). Default: nil.
8
+ config.components_layer = <%= components_layer_value %>
9
+ end
@@ -0,0 +1,163 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ViewComponent
4
+ module ScopedStyles
5
+ extend ActiveSupport::Concern
6
+
7
+ CACHED_VARIABLES = %i[@component_styles @component_id @component_class_map].freeze
8
+ CLASS_SELECTOR_PATTERN = /\.([a-zA-Z_][\w-]*)\b/
9
+
10
+ # Default root class for +component_class+ when it matches a selector in the CSS.
11
+ COMPONENT_CSS_CLASS = "component".freeze
12
+
13
+ class_methods do
14
+ # Sets which CSS class is the root for +component_class+ (no argument).
15
+ # Also triggers style registration when Rails is loaded (registration also
16
+ # runs via the Railtie for all styled components).
17
+ #
18
+ # All class selectors are rewritten to scoped names in +components.scoped.css+.
19
+ # The primary class uses an id from the full stylesheet (e.g. +.icon+ → +.c-99d08d5a+);
20
+ # other classes get per-class ids (e.g. +.input-box+ → +.c-a1b2c3d4+).
21
+ #
22
+ # Call with a name when the root is not +.component+ (the default).
23
+ #
24
+ # Clears cached generated styles when +name+ changes …
25
+ #
26
+ # @param name [String, nil] selector name without a leading dot (e.g. +"icon"+)
27
+ def component_css_class(name = nil)
28
+ if name
29
+ const_set(:COMPONENT_CSS_CLASS, name)
30
+ clear_component_style_cache
31
+ end
32
+ register_styles_if_rails_loaded
33
+ end
34
+
35
+ # Returns processed CSS with scoped class selectors, or +nil+ if none.
36
+ def component_styles
37
+ return @component_styles if defined?(@component_styles)
38
+ return nil unless @styles_block || has_stylesheet?
39
+
40
+ generate_component_styles
41
+ end
42
+
43
+ # Writes this component's processed styles to the bundled or host stylesheet.
44
+ def register_styles
45
+ return unless @styles_block || has_stylesheet?
46
+
47
+ Stylist.register(self)
48
+ end
49
+
50
+ # +true+ when a sidecar +.css+ file exists for this component.
51
+ def has_stylesheet?
52
+ stylesheet_path && File.exist?(stylesheet_path)
53
+ end
54
+
55
+ def styles(&block)
56
+ @styles_block = block
57
+ register_styles_if_rails_loaded
58
+ end
59
+
60
+ private
61
+
62
+ def stylesheet_path
63
+ return @stylesheet_path if defined?(@stylesheet_path)
64
+
65
+ @stylesheet_path = sidecar_files(["css"]).first
66
+ end
67
+
68
+ def register_styles_if_rails_loaded
69
+ return unless defined?(Rails) && Rails.root
70
+ return unless defined?(Rails::Server) # only web server boot path
71
+
72
+ register_styles
73
+ end
74
+
75
+ def generate_component_styles
76
+ styles_content = generate_styles_content
77
+ css_classes = extract_css_classes(styles_content)
78
+ primary_class = primary_css_class(css_classes)
79
+
80
+ @component_id = generate_component_id(styles_content)
81
+ @component_class_map = build_component_class_map(styles_content, css_classes, primary_class)
82
+ @component_styles = replace_css_classes(styles_content, @component_class_map)
83
+ end
84
+
85
+ def generate_styles_content
86
+ @styles_block ? @styles_block.call : File.read(stylesheet_path)
87
+ end
88
+
89
+ def extract_css_classes(styles_content)
90
+ styles_content.scan(CLASS_SELECTOR_PATTERN).flatten.uniq
91
+ end
92
+
93
+ def primary_css_class(css_classes)
94
+ configured = self::COMPONENT_CSS_CLASS.delete_prefix(".")
95
+ css_classes.include?(configured) ? configured : css_classes.first
96
+ end
97
+
98
+ def build_component_class_map(styles_content, css_classes, primary_class)
99
+ css_classes.index_with do |css_class|
100
+ if css_class == primary_class
101
+ @component_id
102
+ else
103
+ generate_scoped_class_id(styles_content, css_class)
104
+ end
105
+ end
106
+ end
107
+
108
+ def replace_css_classes(styles_content, class_map)
109
+ class_map.keys.sort_by(&:length).reverse.reduce(styles_content) do |content, css_class|
110
+ content.gsub(/\.#{Regexp.escape(css_class)}\b/, ".#{class_map[css_class]}")
111
+ end
112
+ end
113
+
114
+ def generate_component_id(styles_content)
115
+ hash = Digest::MD5.hexdigest(styles_content)[0..7]
116
+
117
+ "c-#{hash}"
118
+ end
119
+
120
+ def generate_scoped_class_id(styles_content, css_class)
121
+ hash = Digest::MD5.hexdigest("#{styles_content}:#{css_class}")[0..7]
122
+
123
+ "c-#{hash}"
124
+ end
125
+
126
+ def clear_component_style_cache
127
+ CACHED_VARIABLES.each do |ivar|
128
+ remove_instance_variable(ivar) if instance_variable_defined?(ivar)
129
+ end
130
+ end
131
+ end
132
+
133
+ # Scoped CSS class for a selector (e.g. +"c-99d08d5a"+).
134
+ #
135
+ # With no argument, returns the scoped root class ({COMPONENT_CSS_CLASS} when it
136
+ # appears in the CSS, otherwise the first class in the stylesheet).
137
+ #
138
+ # @param name [String, Symbol] CSS class without a leading dot (e.g. +"input-box"+)
139
+ def component_class(name = nil)
140
+ return nil unless component_has_styles? || component_has_stylesheet?
141
+
142
+ self.class.component_styles
143
+
144
+ if name
145
+ class_map = self.class.instance_variable_get(:@component_class_map)
146
+ class_map[name.to_s.delete_prefix(".")]
147
+ else
148
+ self.class.instance_variable_get(:@component_id)
149
+ end
150
+ end
151
+
152
+ private
153
+
154
+ def component_has_stylesheet?
155
+ self.class.has_stylesheet?
156
+ end
157
+
158
+ def component_has_styles?
159
+ self.class.instance_variable_defined?(:@styles_block) &&
160
+ self.class.instance_variable_get(:@styles_block)
161
+ end
162
+ end
163
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ViewComponent
4
+ module ScopedStyles
5
+ # Global settings for ViewComponent::ScopedStyles.
6
+ #
7
+ # Configure in an initializer:
8
+ #
9
+ # ViewComponent::ScopedStyles.configure do |config|
10
+ # config.components_path = File.join("app", "view_components")
11
+ # config.components_layer = "components"
12
+ # end
13
+ class Configuration
14
+ # Directory where ViewComponent classes live, relative to {Rails.root}.
15
+ #
16
+ # @return [String] default: +"app/components"+
17
+ attr_accessor :components_path
18
+
19
+ # Optional CSS cascade layer name for generated styles in
20
+ # +app/assets/stylesheets/components.scoped.css+.
21
+ #
22
+ # When set, the bundled stylesheet is wrapped in +@layer <name> { ... }+ so
23
+ # you can control specificity relative to other layers in your app.
24
+ #
25
+ # @return [String, nil] default: +nil+ (no layer wrapper)
26
+ attr_accessor :components_layer
27
+
28
+ def initialize
29
+ @components_path = File.join("app", "components")
30
+ @components_layer = nil
31
+ end
32
+ end
33
+
34
+ class << self
35
+ # Returns the global configuration object, creating it on first access.
36
+ #
37
+ # @return [Configuration]
38
+ def configuration
39
+ @configuration ||= Configuration.new
40
+ end
41
+
42
+ # Yields the global configuration for block-style setup.
43
+ #
44
+ # @yieldparam config [Configuration]
45
+ # @return [void]
46
+ def configure = yield(configuration)
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails/railtie"
4
+
5
+ module ViewComponent
6
+ module ScopedStyles
7
+ class Railtie < Rails::Railtie
8
+ config.after_initialize do
9
+ ViewComponent::ScopedStyles::Railtie.load_and_register_components
10
+ end
11
+
12
+ if Rails.env.development?
13
+ config.to_prepare do
14
+ ViewComponent::ScopedStyles::Railtie.register_components
15
+ end
16
+ end
17
+
18
+ class << self
19
+ def component_path
20
+ Rails.root.join("app/components/**/*.rb")
21
+ end
22
+
23
+ def load_and_register_components
24
+ Dir[component_path].each { require_dependency _1 }
25
+
26
+ register_components
27
+ end
28
+
29
+ def register_components
30
+ ObjectSpace
31
+ .each_object(Class)
32
+ .select { _1.ancestors.include?(ViewComponent::ScopedStyles) }
33
+ .each(&:register_styles)
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,100 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ViewComponent
4
+ module ScopedStyles
5
+ class Stylist
6
+ class Writer
7
+ def self.print(component_class)
8
+ new.print!(component_class)
9
+ end
10
+
11
+ def print!(component_class)
12
+ component_name = component_class.name
13
+ component_class.instance_variable_get(:@component_id)
14
+ styles = component_class.component_styles
15
+
16
+ ensure_stylesheet_exists!
17
+
18
+ update_stylesheet_with(component_name, styles)
19
+ end
20
+
21
+ private
22
+
23
+ def ensure_stylesheet_exists!
24
+ return if File.exist?(stylesheet_path)
25
+
26
+ FileUtils.mkdir_p(File.dirname(stylesheet_path))
27
+ File.write(stylesheet_path, initial_content)
28
+ end
29
+
30
+ def update_stylesheet_with(component_name, styles)
31
+ content = File.read(stylesheet_path)
32
+ updated_content = merge_styles_into(content, component_name, styles)
33
+
34
+ write_atomically(updated_content)
35
+
36
+ notify_asset_pipeline!
37
+ end
38
+
39
+ def merge_styles_into(content, component_name, styles)
40
+ wrapped_styles = wrap_with(component_name, styles)
41
+ pattern = style_pattern_for(component_name)
42
+
43
+ if content.match?(pattern)
44
+ content.gsub(pattern, wrapped_styles.strip)
45
+ else
46
+ append_styles_to(content, wrapped_styles)
47
+ end
48
+ end
49
+
50
+ def write_atomically(content)
51
+ if ViewComponent::ScopedStyles.configuration.components_layer
52
+ content = content.rstrip + "\n}\n"
53
+ end
54
+
55
+ temp_file = generate_temp_file_name
56
+
57
+ File.write(temp_file, content)
58
+ FileUtils.mv(temp_file, stylesheet_path)
59
+ end
60
+
61
+ # Multiple Rails processes can boot concurrently (e.g. web + jobs),
62
+ # so each writer needs its own temp file to avoid rename races.
63
+ def generate_temp_file_name
64
+ "#{stylesheet_path}.#{Process.pid}.#{Thread.current.object_id}.#{Process.clock_gettime(Process::CLOCK_MONOTONIC, :nanosecond)}.tmp"
65
+ end
66
+
67
+ def notify_asset_pipeline! = FileUtils.touch(stylesheet_path)
68
+
69
+ def wrap_with(component_name, styles)
70
+ start_marker = "/* #{component_name} */\n"
71
+ end_marker = "/* /#{component_name} */"
72
+
73
+ "#{start_marker}#{styles}#{end_marker}"
74
+ end
75
+
76
+ def style_pattern_for(component_name)
77
+ /\/\* #{Regexp.escape(component_name)} \*\/\n.*?\/\* \/#{Regexp.escape(component_name)} \*\//m
78
+ end
79
+
80
+ def append_styles_to(content, wrapped_styles)
81
+ [content.chomp, wrapped_styles, "\n"].join("\n")
82
+ end
83
+
84
+ def initial_content
85
+ content = "/* Generated by ViewComponent::ScopedStyles - Do not edit manually */\n"
86
+
87
+ if (layer = ViewComponent::ScopedStyles.configuration.components_layer)
88
+ content += "@layer #{layer} {\n"
89
+ end
90
+
91
+ content
92
+ end
93
+
94
+ def stylesheet_path
95
+ @stylesheet_path ||= Rails.root.join("app/assets/stylesheets/components.scoped.css")
96
+ end
97
+ end
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "view_component/scoped_styles/stylist/writer"
4
+
5
+ module ViewComponent
6
+ module ScopedStyles
7
+ class Stylist
8
+ def self.register(component_class)
9
+ return if unstyled?(component_class)
10
+
11
+ Writer.print(component_class)
12
+ end
13
+
14
+ private_class_method def self.unstyled?(component_class)
15
+ !component_class.component_styles &&
16
+ !component_class.instance_variable_get(:@component_id)
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ViewComponent
4
+ module ScopedStyles
5
+ VERSION = "0.1.0"
6
+ end
7
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "scoped_styles/version"
4
+ require_relative "scoped_styles/configuration"
5
+ require "active_support/concern"
6
+ require_relative "scoped_styles/concern"
7
+ require_relative "scoped_styles/stylist"
8
+ require_relative "scoped_styles/railtie"
9
+
10
+ module ViewComponent
11
+ module ScopedStyles
12
+ end
13
+ end
metadata ADDED
@@ -0,0 +1,147 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: view_component-scoped_styles
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Chris Edwards
8
+ bindir: bin
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: rails
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - ">="
17
+ - !ruby/object:Gem::Version
18
+ version: '7.0'
19
+ - - "<"
20
+ - !ruby/object:Gem::Version
21
+ version: '9'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ version: '7.0'
29
+ - - "<"
30
+ - !ruby/object:Gem::Version
31
+ version: '9'
32
+ - !ruby/object:Gem::Dependency
33
+ name: view_component
34
+ requirement: !ruby/object:Gem::Requirement
35
+ requirements:
36
+ - - ">="
37
+ - !ruby/object:Gem::Version
38
+ version: '3.0'
39
+ type: :runtime
40
+ prerelease: false
41
+ version_requirements: !ruby/object:Gem::Requirement
42
+ requirements:
43
+ - - ">="
44
+ - !ruby/object:Gem::Version
45
+ version: '3.0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: rubocop
48
+ requirement: !ruby/object:Gem::Requirement
49
+ requirements:
50
+ - - ">="
51
+ - !ruby/object:Gem::Version
52
+ version: '0'
53
+ type: :development
54
+ prerelease: false
55
+ version_requirements: !ruby/object:Gem::Requirement
56
+ requirements:
57
+ - - ">="
58
+ - !ruby/object:Gem::Version
59
+ version: '0'
60
+ - !ruby/object:Gem::Dependency
61
+ name: rubocop-rails-omakase
62
+ requirement: !ruby/object:Gem::Requirement
63
+ requirements:
64
+ - - ">="
65
+ - !ruby/object:Gem::Version
66
+ version: '0'
67
+ type: :development
68
+ prerelease: false
69
+ version_requirements: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - ">="
72
+ - !ruby/object:Gem::Version
73
+ version: '0'
74
+ - !ruby/object:Gem::Dependency
75
+ name: rspec
76
+ requirement: !ruby/object:Gem::Requirement
77
+ requirements:
78
+ - - ">="
79
+ - !ruby/object:Gem::Version
80
+ version: '0'
81
+ type: :development
82
+ prerelease: false
83
+ version_requirements: !ruby/object:Gem::Requirement
84
+ requirements:
85
+ - - ">="
86
+ - !ruby/object:Gem::Version
87
+ version: '0'
88
+ - !ruby/object:Gem::Dependency
89
+ name: rake
90
+ requirement: !ruby/object:Gem::Requirement
91
+ requirements:
92
+ - - "~>"
93
+ - !ruby/object:Gem::Version
94
+ version: '13.0'
95
+ type: :development
96
+ prerelease: false
97
+ version_requirements: !ruby/object:Gem::Requirement
98
+ requirements:
99
+ - - "~>"
100
+ - !ruby/object:Gem::Version
101
+ version: '13.0'
102
+ description: Rewrites ViewComponent component styles to content-derived class names,
103
+ bundles them into a single stylesheet, and provides helpers to use those classes
104
+ in templates.
105
+ email:
106
+ - chris@chrise.net
107
+ executables: []
108
+ extensions: []
109
+ extra_rdoc_files: []
110
+ files:
111
+ - README.md
112
+ - Rakefile
113
+ - lib/generators/view_component/scoped_styles/install_generator.rb
114
+ - lib/generators/view_component/scoped_styles/templates/view_component_scoped_styles.rb.tt
115
+ - lib/view_component/scoped_styles.rb
116
+ - lib/view_component/scoped_styles/concern.rb
117
+ - lib/view_component/scoped_styles/configuration.rb
118
+ - lib/view_component/scoped_styles/railtie.rb
119
+ - lib/view_component/scoped_styles/stylist.rb
120
+ - lib/view_component/scoped_styles/stylist/writer.rb
121
+ - lib/view_component/scoped_styles/version.rb
122
+ homepage: https://github.com/chrise86/view_component-scoped_styles
123
+ licenses:
124
+ - MIT
125
+ metadata:
126
+ homepage_uri: https://github.com/chrise86/view_component-scoped_styles
127
+ source_code_uri: https://github.com/chrise86/view_component-scoped_styles
128
+ changelog_uri: https://github.com/chrise86/view_component-scoped_styles/blob/main/CHANGELOG.md
129
+ rubygems_mfa_required: 'true'
130
+ rdoc_options: []
131
+ require_paths:
132
+ - lib
133
+ required_ruby_version: !ruby/object:Gem::Requirement
134
+ requirements:
135
+ - - ">="
136
+ - !ruby/object:Gem::Version
137
+ version: 3.2.0
138
+ required_rubygems_version: !ruby/object:Gem::Requirement
139
+ requirements:
140
+ - - ">="
141
+ - !ruby/object:Gem::Version
142
+ version: '0'
143
+ requirements: []
144
+ rubygems_version: 4.0.3
145
+ specification_version: 4
146
+ summary: Scoped, colocated CSS for ViewComponent components.
147
+ test_files: []