-
Notifications
You must be signed in to change notification settings - Fork 89
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Referencing assets in CSS #22
Comments
Are you using PostCSS to process your CSS? If so, can https://github.com/postcss/postcss-url solve your use case? |
I try to add a postprocessor to replace asset path: class AssetUrlProcessor
def self.call(input)
# don't know why, copy from other processor
context = input[:environment].context_class.new(input)
data = input[:data].gsub(/url\((.+?)\)/) do |match|
path = context.asset_path($1)
"url(#{path})"
end
{ data: data }
end
end
Sprockets.register_postprocessor 'text/css', AssetUrlProcessor It works for me. Need an official solution. |
Yes, we need an official solution to this. That asset url processor looks in the ballpark. Here's the one from sassy-rails: https://github.com/sass/sassc-rails/blob/master/lib/sassc/rails/functions.rb |
I have a bit different "ask" for official solution. But I guess still related to this. How to reference and "import" assets from npm packages (in my case font-awesome). Here is how to do it in Phoenix/esbuild/sass https://gist.github.com/ks2211/75af6cddc051f5e261a29fc25eed5789 It would be wonderful some official solution for Rails 7/cssbuilding-rails |
This works great, just ran into this. |
Updating to better handle asset URLs that contain quotes or if they use a data URI / full URL so that we do not attempt to overwrite with a wrong value. Thanks for the starting point @chloerei config/initializers/assets.rb class AssetUrlProcessor
def self.call(input)
# don't know why, copy from other processor
context = input[:environment].context_class.new(input)
data = input[:data].gsub(/url\(["']?(.+?)["']?\)/i) do |match|
asset = $1
if asset && asset !~ /(data:|http)/i
path = context.asset_path(asset)
"url(#{path})"
else
match
end
end
{ data: data }
end
end
Sprockets.register_postprocessor 'text/css', AssetUrlProcessor |
Thanks for the feedback everyone. I ended up going with a post processor that uses # config/initializers/asset_url_processor.rb
class AssetUrlProcessor
def self.call(input)
context = input[:environment].context_class.new(input)
data = input[:data].gsub(/asset-url\(["']?(.+?)["']?\)/) do |_match|
"url(#{context.asset_path($1)})"
end
{data: data}
end
end
Sprockets.register_postprocessor "text/css", AssetUrlProcessor Keep in mind this only handles assets that are managed by sprockets. If we have other external assets that need to be included (such as within node modules), we could use postcss-url as @rmacklin mentioned. I'd probably configure it to output directly to |
Looks like Propshaft might be the official solution to this, although it seems to use an This would make drop in updates of themes, libraries, or icon fonts much simpler by just copying files into folders under |
@stevestmartin thanks for pointing out Propshaft. I replied to your issue there: rails/propshaft#1 |
I think I'm running into this problem with FontAwesome. I've got a Rails 6.x app, and I switched from Webpacker to cssbundling-rails and jsbundling-rails. Everything is working great locally as far as I can tell. Compilation is SUPER fast, and there's a LOT less JavaScript involved in the entire process. I pushed to Heroku, and it seems the font URLs aren't being rewritten. Not a problem, this application isn't live yet, so I get to play with the new and shiny with limited consequences. Will Propshaft be the right solution for Rails 6.x apps? I see it's locked to Rails 7 right now. |
@Petercopter if the fonts are working in development but not production, they might not be included in the precompile. Try running If they are included, you might need a post processor to remap the url, similar to the ones mentioned earlier. |
There is a flaw in the regex in @ryanb's solution. It should be |
@jcoyne thanks, I'll update it. |
You can also use propshaft's regex, which I've tested against all mdn uses cases: It will ignore urls, anchors and data images, and will handle spaces or no |
Anyone want to take on bringing this to sprockets-rails proper? This should work out of the box with Rails 7 on both Sprockets and Propshaft (already works there). |
@dhh something like this: rails/sprockets-rails#476 ? |
Solved via rails/sprockets-rails#476. Thanks @jcoyne 🙏 |
@Petercopter were you able to get font awesome deploying correctly to Heroku? If yes, what did you use in your |
@MrHubble Yes! I added this to my
|
@Petercopter Not directly related to the topic at hand, but what did you do to get the webfonts in dev? |
@itsyoshio - I added the following to For context, this was in addition to
My testing is with development and checking puiblic/assets after FYI, my package.json looks like:
and my Procfile.dev has the following (I also have Procfile.watch that excludes web for running seperately whilst debugging under rubymine):
|
I'm still unclear as to how this works in practice. I have a file in
But my image is 404ing. Is there a step I'm missing here? |
@ianheggie Thanks for your set up help. Works great for me in development, but once pushed to Heroku I get errors in my logs like: Any ideas on why this might be? My set up is the same as yours, other than my css script: |
You might be in the same situation as I was. I see you're trying to use font-awesome. In my case I use Propshaft, but the following wouldn't be different from Sprocket config/initializers/assets.rb Rails.application.config.assets.excluded_paths += [Rails.root.join('app/assets/stylesheets'), Rails.root.join('app/javascript')]
# Add additional assets to the asset load path.
Rails.application.config.assets.paths << Rails.root.join('node_modules/@fortawesome/fontawesome-free/webfonts') application.css.scss $fa-font-path: '';
@import '@fortawesome/fontawesome-free/scss/fontawesome';
@import '@fortawesome/fontawesome-free/scss/regular';
@import '@fortawesome/fontawesome-free/scss/solid'; |
Using I don't understand why!! In both environments the assets are in the right place:
|
Incase someone stumbles upon this, I removed all fontawesome bootstrap refrences from application.scss, manifest.js and config/assets.rb and instead added |
To fix fontawesome problem: config/initializers/assets.rb Rails.application.config.assets.paths << Rails.root.join('node_modules/@fortawesome/fontawesome-free/webfonts') application.css.scss $fa-font-path: ".";
@import "@fortawesome/fontawesome-free/scss/fontawesome.scss";
@import "@fortawesome/fontawesome-free/scss/solid.scss"; |
Using esbuild to compile SCSS files works for the SCSS part, but not for assets that are referenced inside the SCSS files. Images are processed by the asset pipeline. The pipeline turns images like `foo.png` into `foo-138b348edbc780482d40f0abc5b57e487c0ecf24cfbde42d54007cfd8db0d7a4.pn g`. SCSS files still refer to them as `url("foo.png")`, but the final path to the image must include the hash in production. To make sure the final CSS file can find these images, sprockets-rails [scans] all CSS files using `AssetUrlProcessor`, which replaces `url("foo.png")` with the hashed version. Another complication was in a previous version of my asset pipeline esbuild located assets relative to the CSS file the refernced them. Sprockets doesn't look there. It [searches] any directory in `app/assets`. All the asset urls in SCSS files needed to be changed to be relative to those search paths instead, e.g. `../../images/foo.png` to `foo.png`. I have three base css files: application, blog, and admin. To support them all I changed the `sass` command to look in `./app/assets/stylesheets` and build to `./app/assets/builds` instead of looking at each individual file ([sass reference]). It built the `admin.css` file inside of an `admin/` directory, so the `stylesheet_link_tag` had to change to reflect that. Building asseets in development now either requires running the css, js, _and_ rails server. You can use `bin/dev` to start all three, or start them manually: ``` yarn build --watch yarn build:css --watch rails s ``` ## Other cleanup I wasn't able to get normalize.css imported from the node modules, but I also removed it and it didn't seem to break anything. So I'm going to keep it removed, as well as remove the `normalize-rails` gem. Finally, I was able to remove the `esbuild-sass-plugin` node module since I'm not using esbuild to bundle sass anymore. [scans]: rails/cssbundling-rails#22 [searches]: https://guides.rubyonrails.org/asset_pipeline.html#search-paths [sass reference]: https://sass-lang.com/documentation/cli/dart-sass#many-to-many-mode
Using esbuild to compile SCSS files works for the SCSS part, but not for assets that are referenced inside the SCSS files. Images are processed by the asset pipeline. The pipeline turns images like `foo.png` into `foo-138b348edbc780482d40f0abc5b57e487c0ecf24cfbde42d54007cfd8db0d7a4.pn g`. SCSS files still refer to them as `url("foo.png")`, but the final path to the image must include the hash in production. To make sure the final CSS file can find these images, sprockets-rails [scans] all CSS files using `AssetUrlProcessor`, which replaces `url("foo.png")` with the hashed version. Another complication was in a previous version of my asset pipeline esbuild located assets relative to the CSS file the refernced them. Sprockets doesn't look there. It [searches] any directory in `app/assets`. All the asset urls in SCSS files needed to be changed to be relative to those search paths instead, e.g. `../../images/foo.png` to `foo.png`. I have three base css files: application, blog, and admin. To support them all I changed the `sass` command to look in `./app/assets/stylesheets` and build to `./app/assets/builds` instead of looking at each individual file ([sass reference]). It built the `admin.css` file inside of an `admin/` directory, so the `stylesheet_link_tag` had to change to reflect that. Building asseets in development now either requires running the css, js, _and_ rails server. You can use `bin/dev` to start all three, or start them manually: ``` yarn build --watch yarn build:css --watch rails s ``` ## Other cleanup I wasn't able to get normalize.css imported from the node modules, but I also removed it and it didn't seem to break anything. So I'm going to keep it removed, as well as remove the `normalize-rails` gem. Finally, I was able to remove the `esbuild-sass-plugin` node module since I'm not using esbuild to bundle sass anymore. [scans]: rails/cssbundling-rails#22 [searches]: https://guides.rubyonrails.org/asset_pipeline.html#search-paths [sass reference]: https://sass-lang.com/documentation/cli/dart-sass#many-to-many-mode
Using esbuild to compile SCSS files works for the SCSS part, but not for assets that are referenced inside the SCSS files. Images are processed by the asset pipeline. The pipeline turns images like `foo.png` into `foo-138b348edbc780482d40f0abc5b57e487c0ecf24cfbde42d54007cfd8db0d7a4.pn g`. SCSS files still refer to them as `url("foo.png")`, but the final path to the image must include the hash in production. To make sure the final CSS file can find these images, sprockets-rails [scans] all CSS files using `AssetUrlProcessor`, which replaces `url("foo.png")` with the hashed version. Another complication was in a previous version of my asset pipeline esbuild located assets relative to the CSS file the refernced them. Sprockets doesn't look there. It [searches] any directory in `app/assets`. All the asset urls in SCSS files needed to be changed to be relative to those search paths instead, e.g. `../../images/foo.png` to `foo.png`. I have three base css files: application, blog, and admin. To support them all I changed the `sass` command to look in `./app/assets/stylesheets` and build to `./app/assets/builds` instead of looking at each individual file ([sass reference]). It built the `admin.css` file inside of an `admin/` directory, so the `stylesheet_link_tag` had to change to reflect that. Building asseets in development now either requires running the css, js, _and_ rails server. You can use `bin/dev` to start all three, or start them manually: ``` yarn build --watch yarn build:css --watch rails s ``` ## Other cleanup I wasn't able to get normalize.css imported from the node modules, but I also removed it and it didn't seem to break anything. So I'm going to keep it removed, as well as remove the `normalize-rails` gem. Finally, I was able to remove the `esbuild-sass-plugin` node module since I'm not using esbuild to bundle sass anymore. [scans]: rails/cssbundling-rails#22 [searches]: https://guides.rubyonrails.org/asset_pipeline.html#search-paths [sass reference]: https://sass-lang.com/documentation/cli/dart-sass#many-to-many-mode
Using esbuild to compile SCSS files works for the SCSS part, but not for assets that are referenced inside the SCSS files. Images are processed by the asset pipeline. The pipeline turns images like `foo.png` into `foo-138b348edbc780482d40f0abc5b57e487c0ecf24cfbde42d54007cfd8db0d7a4.pn g`. SCSS files still refer to them as `url("foo.png")`, but the final path to the image must include the hash in production. To make sure the final CSS file can find these images, sprockets-rails [scans] all CSS files using `AssetUrlProcessor`, which replaces `url("foo.png")` with the hashed version. Another complication was in a previous version of my asset pipeline esbuild located assets relative to the CSS file the refernced them. Sprockets doesn't look there. It [searches] any directory in `app/assets`. All the asset urls in SCSS files needed to be changed to be relative to those search paths instead, e.g. `../../images/foo.png` to `foo.png`. I have three base css files: application, blog, and admin. To support them all I changed the `sass` command to look in `./app/assets/stylesheets` and build to `./app/assets/builds` instead of looking at each individual file ([sass reference]). It built the `admin.css` file inside of an `admin/` directory, so the `stylesheet_link_tag` had to change to reflect that. Building asseets in development now either requires running the css, js, _and_ rails server. You can use `bin/dev` to start all three, or start them manually: ``` yarn build --watch yarn build:css --watch rails s ``` [scans]: rails/cssbundling-rails#22 [searches]: https://guides.rubyonrails.org/asset_pipeline.html#search-paths [sass reference]: https://sass-lang.com/documentation/cli/dart-sass#many-to-many-mode
note: The solution that was brought into rails was to automatically pick up note that if you seem to not getting your |
Thanks for the helpful hints above! I tried various combinations before finding something that works, so I'm sharing my setup when using FontAwesome as scss using cssbundling+propshaft:
$fa-font-path: ""; // or "."
@import "@fortawesome/fontawesome-free/scss/fontawesome";
@import "@fortawesome/fontawesome-free/scss/brands";
@import "@fortawesome/fontawesome-free/scss/solid";
@import "@fortawesome/fontawesome-free/scss/regular";
Rails.application.config.assets.version = "1.0"
Rails.application.config.assets.paths << Rails.root.join("node_modules/@fortawesome/fontawesome-free/webfonts") I have no
"scripts": {
"build": "esbuild app/javascript/*.* --bundle --sourcemap --format=esm --outdir=app/assets/builds --public-path=/assets",
"build:css": "sass ./app/assets/stylesheets/:./app/assets/builds/ --no-source-map --load-path=node_modules"
} I'm telling sass to compile the Gem versions: rails: 7.1.3.2
css-bundling: 1.4.0
js-bundling: 1.3.0
propshaft: 0.8.0 If you want to see what is generated, run |
Is it possible to reference other assets such as images and fonts in CSS? In the asset pipeline you'd normally do
image-url("image.png")
, but from my understanding that is handled bysass-rails
and requires anscss
file.Is there a recommended way for doing this through
cssbundling-rails
and the asset pipeline?Thanks for your work on this gem!
The text was updated successfully, but these errors were encountered: