How can I define a helper to produce a CSSModules-style css class? #830
-
This Evil Martians article from 2022 is about ViewComponent, but it seems to have the key to making CSS modules work server-side with Phlex. It recommends the following setup:
// /postcss.config.js
module.exports = {
plugins: [
require('postcss-import'),
require('postcss-nesting'),
require('autoprefixer'),
require('postcss-modules')({
generateScopedName: (name, filename, _css) => {
const matches = filename.match(
/\/app\/views\/components\/?(.*)\/*.css$/);
// Don't transform CSS files from outside the components folder
if (!matches) return name;
// Transforms "way_down/we_go/example" into "way-down--we-go--example"
const identifier = matches[1].replaceAll('_', '-').
replaceAll('/', '--');
return `c--${ identifier }--${ name }`;
},
// Don't generate *.css.json files (we don't need them)
getJSON: () => {},
}),
],
};
# /app/helpers/css_helper.rb
module CssHelper
def class_for_component(class_name)
"c--#{component_name}--#{class_name}"
end
end
# /app/views/components/navbar/navbar.rb
class Components::Navbar < Components::Base
def view_template(&)
div(class: helpers.class_for_component("container"), &)
end
/* /app/views/components/navbar/navbar.css */
.container {
background-color: blue;
} And post: /* /app/assets/builds/application.css */
.c--Navbar--container {
background-color: blue;
} === As you can see, the part that is missing is that I'm not sure how to get the The snippet from the article linked above goes like this: class << self
def identifier
@identifier ||= component_name.gsub("_", "-").gsub("/", "--")
end
end
def class_for(name)
"c--#{self.class.identifier}--#{name}"
end But it's not clear to me where this code is supposed to live. I'm presuming it's specific to the ViewComponent implementation. In any case for me it throws the following error: |
Beta Was this translation helpful? Give feedback.
Replies: 3 comments 3 replies
-
You should be able to use |
Beta Was this translation helpful? Give feedback.
-
This will quickly fall apart as soon as you have a component class elsewhere with the same name. For example both I also use CSS modules, but instead I use the file system path of the component, using
|
Beta Was this translation helpful? Give feedback.
-
Here's what I ended up doing: # /app/helpers/css_helper.rb
module CssHelper
def modularize(class_name)
component = self.class.name.gsub("::", "--")
component + "--" + class_name
end
module_function :modularize
end # /app/views/components/base.rb
class Components::Base < Phlex::HTML
include Components
include Phlex::Rails::Helpers::Routes
include Phlex::Rails::Helpers::ImageTag
include Phlex::Translation
include CssHelper # <==
end # /app/views/components/navbar/navbar.rb
class Components::Navbar < Components::Base
def view_template(&)
div(class: modularize("nav"), &)
end
end Result: I decided to compromise on short prefix names and just keep the full class name to make it a bit more future-proof. And here's the CSS side of things: // /postcss.config.js
const path = require('path');
module.exports = {
plugins: [
require('postcss-import-ext-glob'),
require('postcss-import')({
plugins: [
require('postcss-modules')({
generateScopedName: (name, filename, _css) => {
const path = require('path');
return path.relative('./app/views/', filename).
split('/').
toSpliced(-1).
map((f) => String(f).charAt(0).toUpperCase() + String(f).slice(1)).
join('--') + "--" + name;
},
// Don't generate *.css.json files (we don't need them)
getJSON: () => {},
})],
}),
require('postcss-nesting'),
require('autoprefixer')
],
}; /* /app/assets/stylesheets/application.postcss.css */
/* Entry point for your PostCSS build */
/* Import stylesheets co-located with components */
@import-glob "../../views/**/*.css"; |
Beta Was this translation helpful? Give feedback.
You should be able to use
self.class.name
.