Skip to content

Can't use relative paths in url() in scss files #12797

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

Closed
yentheo opened this issue Oct 29, 2018 · 41 comments · Fixed by #17537
Closed

Can't use relative paths in url() in scss files #12797

yentheo opened this issue Oct 29, 2018 · 41 comments · Fixed by #17537
Labels
Milestone

Comments

@yentheo
Copy link

yentheo commented Oct 29, 2018

Bug Report or Feature Request (mark with an x)

- [ ] bug report -> please search issues before submitting
- [x] feature request

Command (mark with an x)

- [x] new
- [x] build
- [ ] serve
- [ ] test
- [ ] e2e
- [ ] generate
- [ ] add
- [ ] update
- [ ] lint
- [ ] xi18n
- [ ] run
- [ ] config
- [ ] help
- [ ] version
- [ ] doc

Versions

npm: 6.4.1
node: v10.7.0
ng cli: 6.2.6
macOS High Sierra

Repro steps

  1. Create new project

ng new ng-sass-test--style=scss

  1. Add following files:
  - src
    - sprite
      - _sprite.scss
      - sprite.svg

With the following content:

# _sprite.scss
.sprite-common {
    background-image: url('sprite.svg');
}

Add the following line to src/styles.scss.
@import '~src/sprite/sprite';
Add the same line to src/app/app.component.scss.

The log given by the failure

ERROR in ./src/styles.scss (./node_modules/raw-loader!./node_modules/postcss-loader/lib??embedded!./node_modules/sass-loader/lib/loader.js??ref--15-3!./src/styles.scss)
Module Error (from ./node_modules/postcss-loader/lib/index.js):
(Emitted value instead of an instance of Error) CssSyntaxError: /***/ng-sass-test/src/sprite/_sprite.scss:2:22: Can't resolve 'sprite.svg' in '/***/ng-sass-test/src'

  1 | .sprite-common {
> 2 |     background-image: url('sprite.svg');
    |                      ^
  3 | }

ERROR in ./src/app/app.component.scss
Module Error (from ./node_modules/postcss-loader/lib/index.js):
(Emitted value instead of an instance of Error) CssSyntaxError: /***/ng-sass-test/src/sprite/_sprite.scss:2:22: Can't resolve 'sprite.svg' in '/***/ng-sass-test/src/app'

  1 | .sprite-common {
> 2 |     background-image: url('sprite.svg');
    |                      ^
  3 | }

Desired functionality

I would like to be able to use the relative file path in the scss file. I know this is solved by using an absolute path for the image. But I'm using the package svg-sprite, I'm not an expert in the package, but it looks like it doesn't support generation of an absolute path. Maybe I'm missing something in the angular-cli setup, so I can use this way of path-handling.

Mention any other details that might be useful

@yentheo yentheo changed the title Can't use relative paths in scss files Can't use relative paths in url() in scss files Oct 29, 2018
@alan-agius4
Copy link
Collaborator

alan-agius4 commented Oct 29, 2018

Just had a quick look at this, at this is tricky one.

Since in ./src/app/app.component.scss if you want to use relative url, the image src, should be ../sprite/sprite.svg, while forstyle.scss it should be ./sprite/sprite.svg.

@alan-agius4 alan-agius4 added freq1: low Only reported by a handful of users who observe it rarely severity3: broken area: @angular-devkit/build-angular labels Oct 30, 2018
@ngbot ngbot bot added this to the needsTriage milestone Oct 30, 2018
@yentheo
Copy link
Author

yentheo commented Oct 30, 2018

Well yes, if I use a relative path in the one sass-file, it uses it as relative from every other file where I import it. Isn't there a way to just have it relative to the single file with the url()?

@alan-agius4
Copy link
Collaborator

alan-agius4 commented Oct 30, 2018

I think the recommended approach here is to put these sprite file in the assets folder, and use root level paths (starting with /) for urls in sass files that are imported by other sass files.

In scss files at an absolute path;

background-image: url('/sprite/sprite.svg');

in your angular.json add the following so that the sprite is copied, under architect -> build -> options

"assets": [
    "src/sprite/sprite.svg"
],

From svg-sprite package point of view, there will be no changes.

@yentheo
Copy link
Author

yentheo commented Oct 30, 2018

The problem with that is that I can't change the sprite.scss, it is generated by the svg-sprite package. For as far as I know, it does not have the option to generate with an absolute path.

@alan-agius4
Copy link
Collaborator

The simplest way to do it is to copy https://github.com/jkphl/svg-sprite/blob/6966592489dfcf2cd9ed2bba500cb8ee31c00b9c/tmpl/css/sprite.scss and add a lead slashes / and when using svg-sprite you provide this template via configuration example

config            = {
	mode: {
        css: { // Create a «css» sprite
            render: {
                scss : {
                    template: 'my-custom.scss',
                }
            },
        },
    },
},

@ngbot ngbot bot modified the milestones: needsTriage, Backlog Nov 1, 2018
@yentheo
Copy link
Author

yentheo commented Nov 5, 2018

Tried it out, works perfect. Thanks for the idea @alan-agius4 !

@agalazis
Copy link

agalazis commented Jan 11, 2019

Doesn't work same issue with absolute paths (at least behaviour is inconsistent with angular6 ). Happened after upgrade to 7. I think what changed is automatically adding resolved scss assets to assets option/public folder. Hence people reporting having to add each file to assets (At least in the past we didn't need to).

@agalazis
Copy link

agalazis commented Jan 11, 2019

btw I am not getting any errors for entirely wrong paths either as I used to.
seeing this: #12746 makes me think that updating angular +sass is a nightmare are there any instructions for updating? could this be caused by upstream projects as well?

@agalazis
Copy link

is sass loader what angular uses internally?
https://github.com/webpack-contrib/sass-loader#problems-with-url

@salilgaurav
Copy link

any update on this issue ?

@jussikinnula
Copy link

I’m also interested on this, since Stylus and LESS work fine with relative URLs and SASS doesn’t. For ejected projects resolve-url-loader does the trick.

@ruimarques
Copy link

I get the following, rocking Angular CLI: 7.3.0, Angular: 7.2.3.

// angular.json
"assets": [
    "src/assets"
]
background-image: url('/src/assets/images/icon.png');

// GET http://localhost:3000/src/assets/images/icon.png 404 (Not Found)
background-image: url('../../../../assets/images/icon.png');

// GET http://localhost:3000/icon.png 200 OK

@alan-agius4
Copy link
Collaborator

As @agalazis, correctly pointed out the issue. Sass itself doesn't provide Url rewiring. While indeed resolve-url-loader might work in some cases, this relies on a non standard behaviour as it uses sourcemaps to rewire the paths.

@alan-agius4
Copy link
Collaborator

@ruimarques, I suggest you open a separate issue with a reproduction for the issue you are encountering.

@agalazis
Copy link

@alan-agius4 another point to mention in the past svgs in my css but after giving up and setting assets path now they are loaded as links after build

@acohenOT
Copy link
Contributor

acohenOT commented Jun 4, 2019

Please consider not removing rebaseRootRelativeCssUrls build option (#14587) until this issue is fixed. Since this issue prevents using relative URLs, the rebaseRootRelativeCssUrls option is the only workaround when needing to specify a base-href when building.

@iwnow
Copy link

iwnow commented Jun 14, 2019

background-image: url('~/assets/images/logo.svg');

works for me (version 8.0.1)

@michael-letcher
Copy link

michael-letcher commented Jun 19, 2019

@iwnow This has been said to cause the app to be deployed with images copies to the dist folder (out of the assets folder) meaning you have multiple copies for no reason.

Angular Team:
Please fix this, it's very sad that it's now "The recommended method to transition is to use relative paths within the source stylesheet".

This ticket has been here for well over a year. What was the plan? Was there one? It seems the idea was to just break AngularCLI and say use this method that doesn't work and hope for the best.

@iwnow
Copy link

iwnow commented Jun 26, 2019

@michael-letcher yes I understand but I have no multiple copies, all works as expected
this is a simple workaround, but I still want this to be fixed

@Dada-Tech
Copy link

background-image: url('../../assets/img/bg-masthead.jpg'); worked for me.
Very annoying.

@safik222
Copy link

safik222 commented Oct 1, 2019

The solution is to write the path as if you were starting from "angular.json".

Instead of
background-image: url('../assets/images/pattern-dot-grid.svg');

Write it like this (witout the dots at the beggining)
background-image: url('/assets/images/pattern-dot-grid.svg');

-- this worked for me ☺

@intellix
Copy link
Contributor

A couple of the workarounds here are basically: "just use absolute paths". The issue is about the inability to use relative paths :D

In some cases you can't use absolute because you have a baseHrefUrl like /en/ so / is going out of that folder to a path that doesn't exist.

You can workaround this using Express I guess but

@pnrdk
Copy link

pnrdk commented Oct 23, 2019

I am having a similar issue. My production server is running the solution in a subfolder (mydomain.com/myapp/). I have setup the base href to "/myapp/" on localhost it is set to "/".

I have a css file placed in "assets/css" this file is referring to a file in "assets/images", I have done that writing "/assets/images/myimage.png" This is working fine localhost, but when publishing to the server it can't find the image. If I change url on the server in the css to "assets/images/myimage.png", it works on the server.

Then I have tried changing the url in the project to the one on the server ("assets/images/myimage.png"), but then it can't compile I am getting the error: Can't resolve 'assets/images/myimage.png".

I have tried the following combinations, but without any luck :-|

'./assets/images/myimage.png'
'~assets/images/myimage.png'
'../assets/images/myimage.png'

@drewberk
Copy link

@pnrdk did you try the following (note the / between the ~ and 'assets')?
~/assets/images/myimage.png

@xouqoa
Copy link

xouqoa commented Oct 28, 2019

@pnrdk did you try the following (note the / between the ~ and 'assets')?
~/assets/images/myimage.png

This worked for me with the latest version of the CLI.

@nisapaypay
Copy link

background-image: url('~/assets/images/logo.svg');

works for me (version 8.0.1)

thankk you

@incodedev
Copy link

You can use inline CSS style
<div style="background-image: url('/assets/images/logo.svg');" ></div>

@KingDarBoja
Copy link

@iwnow MVP, that really works! Works in latest Angular version (8.3.21)! 🚀

@cyrilfr
Copy link

cyrilfr commented Jan 14, 2020

background-image: url('~/assets/images/logo.svg');

works for me (version 8.0.1)

It's compiled to

background-image: url('/./assets/images/logo.svg');

in my case which is not what's expected.

Globally I'd recommand to never use absolute paths in url() as it is pure CSS and is always relative to the index.html file in production.

However, the compiler complains when using relative paths, especially if you import a variables.scss file containing assets in your components.

@bnohad
Copy link

bnohad commented Jan 21, 2020

background-image: url('~/assets/images/logo.svg');

works for me (version 8.0.1)

the problem this works in dev env, but not when building the app. (in SCSS - didn't try plain CSS)

the build output for '~/assets/images/logo.svg' is -> '/assets/images/logo.svg' which prevents from deploying the app in any other path than the root, even though in my index.html i set the baseHref to be relative './'

@cyrilfr
Copy link

cyrilfr commented Jan 21, 2020

There is still this fix if you can use custom-webpack.
It passes the webpack compilation and provides good relative paths in prod.

@bnohad
Copy link

bnohad commented Jan 21, 2020

the only way i found how to fix it in builds is to specify the base-href attribute to be the same as the path i plan to deploy to, for example:

ng build --base-href ui
deploy the app under www.example.com/ui/

and then in the SCSS file use: background-image: url('~/assets/icon.svg').

after building the app, the ~ char is replaced with whatever value you provide to the --base-href attribute.

@Yizhachok
Copy link

@bnohad unfortunately this didn't work with --localize flag that has been introduced in angular 9 to compile several language locales using one command.
I use ng build --prod --localize command and has configured 2 languages but there no added language directory to url path when I write like this background-image: url('~/assets/icon.svg')
But I see language directory in Html base tag. Probably this is bug of angular 9

The CLI also adjusts the HTML base HREF for each version of the application by adding the locale to the configured baseHref. https://angular.io/guide/i18n

@devniel
Copy link

devniel commented Mar 13, 2020

Just found that when using a caret ^ as the first character on the url provided in url(), the angular building process ignores it (it's actually a postcss-loader plugin), so with that you can use anything including relative urls expecting different base-hrefs. example:

.box {
  background: url("^myfiles/images")
}

Of course, there is an assets folder in angular where you will put the images (to have it working locally you can just url("^assets/images")) but the point is that when you want to deploy it in production, maybe you will copy your assets folder content to your server.com/myapp/myfiles folder, so in this case it's very useful, otherwise, the builder will try to resolve the path and it will throw an error because it can't found the files (actually, it has a problem when the url is relative),

In addition, as off-topic, when resolving the urls (not using ^), the files will be copied to the dist/project folder, not to dist/project/assets (that's the destiny of the assets folder configured in angular.json), so it will be quite a mess. A solution is to override the webpack scss loaders or (a clean way) is to use the next approach by including the target files in the assets directories, even of third party modules.

"assets": [
     "src/favicon.ico",
     "src/assets",
    { "glob": "**/*", "input": "./node_modules/mymodule/assets/", "output": "./assets/" }
 ],

For reference:
https://github.com/angular/angular-cli/blame/523a20d92bc516dbdf2ea17bc9b02d9402232c47/packages/angular_devkit/build_angular/src/angular-cli-files/plugins/postcss-cli-resources.ts#L72

@dannyskoog
Copy link

dannyskoog commented Mar 13, 2020

Just found that when using a caret ^ as the first character on the url provided in url(), the angular building process ignores it (it's actually a postcss-loader plugin), so with that you can use anything including relative urls expecting different base-hrefs. example:

.box {
  background: url("^myfiles/images")
}

FYI - the caret sign was previously discussed in the following issue angular/angular#32811

@devniel
Copy link

devniel commented Mar 13, 2020

Thanks @dannyskoog , not supported feature means that maybe this hidden gem could be removed without warning?

@dannyskoog
Copy link

Yes @devniel. And that was why I wasn't allowed to add it to the documentation (reference: angular/angular#32921 (comment))

clydin added a commit to clydin/angular-cli that referenced this issue Apr 27, 2020
…s when using preprocessors

Previously, when using a preprocessor, resources (e.g., `url(./my-image.jpg)`) referenced in a stylesheet that was imported into another stylesheet would retain the exact URL.  This would be problematic as the resource would not be at the relative location within the new combined stylesheet.  With this change the resource URLs will now be adjusted to reference the origin location of the resource.  This allows the resources to be found without any additional changes to the application or build process.
CSS and Less already functioned in this manner. This change brings Sass and Stylus to parity.

Fixes: angular#12797
mgechev pushed a commit that referenced this issue Apr 27, 2020
…s when using preprocessors

Previously, when using a preprocessor, resources (e.g., `url(./my-image.jpg)`) referenced in a stylesheet that was imported into another stylesheet would retain the exact URL.  This would be problematic as the resource would not be at the relative location within the new combined stylesheet.  With this change the resource URLs will now be adjusted to reference the origin location of the resource.  This allows the resources to be found without any additional changes to the application or build process.
CSS and Less already functioned in this manner. This change brings Sass and Stylus to parity.

Fixes: #12797
@tanvirstreame
Copy link

Thanks It worked 👍

@angular-automatic-lock-bot
Copy link

This issue has been automatically locked due to inactivity.
Please file a new issue if you are encountering a similar or related problem.

Read more about our automatic conversation locking policy.

This action has been performed automatically by a bot.

@angular-automatic-lock-bot angular-automatic-lock-bot bot locked and limited conversation to collaborators Jun 7, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

Successfully merging a pull request may close this issue.