We have an Angular 8 single page web app deployed on the customer server. They set one of the CSP directive to: default-src 'self'. We build the Angular app using ng build --prod like any other Angular applications. After deploying, we get this error:
main-es2015.47b2dcf92b39651610c0.js:1 Refused to apply inline style because it violates the following Content Security Policy directive: "default-src 'self'". Either the 'unsafe-inline' keyword, a hash ('sha256-47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU='), or a nonce ('nonce-...') is required to enable inline execution. Note also that 'style-src' was not explicitly set, so 'default-src' is used as a fallback.
Look into the html code on the browser, I see something like this:
As you can see, Angular actually use tag <style> to serve the css (please correct me if I'm wrong). This violates the CSP directive mentioned in the question.
After searching around, I think Angular/React is quite bad at handling this issue, those frameworks are not built with CSP in mind. You can check out Angular github page, there is an open issue for this. Now I'm searching for a solution to overcome this, of course changing CSP policy is not an option because the customers don't want to.
How can I tell Angular not to use tag <style> in production to serve css? I think to make it works we need to set Angular in a way that it will load the css files, and then use styles in those files instead of injecting <style> into html which causes CSP issue.
Edit 1: Our project is using scss.
Edit 2: After searching around, I have found out that Angular will inject your component's styles into the DOM by using <style> element. As shown here:
Now I have an idea, because for each compinent's style will be injeced into the DOM through <style> element, we can prevent this from happening by bundling all component's style .scss file into a single style.scss file. From the image above you can see that we always have an empty <style> element, so if this works, we will endup with only one <style> element and a <link> element that link to our global style scss file. We can have multiple way to remove that empty <style> element before the page got rendered by the browser.
Now I'm stuck at configuring custom webpack to make this happen. We cant use ng eject to get the webpack.config.js file since Angular CLI 6. I've been using Angular CLI 8 so the only way for me to add custom configuration into Webpack is to use custom-webpack npm. I cant find a good config file that has the same output as my desire, please help if you know how to config webpack to bundle all component's styles scss files in Angular into a global scss file.
I think this can be an acceptable answer for my question:
First of all, my recommendation is stay away from using styleUrls. Angular will inject styles of your component into the DOM using <style> element. Secondly, if it's possible, you should know / ask for the CSP policy on the deployment server/environment. What I have been doing to resolve the issue (my project is reletively small with just a couple dozen of components):
Copy (one by one) relative link of components, put them into angular.json, in styles attribute. This is because Angular will bundle all styles in this attribute as a single css/scss file. I copy one by one because the css/scss file was designed to work with Angular View Encapsulation in the first place. Gathering all of them into one place might introduct unexpected issue, this will break the UI. Whenever copy a component style and put into styles, I check if the UI breaks because of that. This will help me narrow down the root cause if such issue happens.
For each component, after copy its component style file's relative path into styles, I remove styleUrls in #Component. This prevents Angular from injecting <style> into the DOM.
Caveats:
Gathering all styles into one single file and load them at once might cause performance issue. Luckily my company project is relatively small.
Now you need to document the new way of making styling work in your project.
Related
I'm on Vue.js v2. I have a CSS stylesheet stored as a string in a variable.
import sitePackCss from '!!raw-loader!sass-loader!../../app/javascript/styles/site.sass';
I need to create a tag from my component.
<style v-html="sitePackCss" />
OR
<style>{sitePackCss}</style>
When I do either of these, I get the following error in the console:
Templates should only be responsible for mapping the state to the UI. Avoid placing tags with side-effects in your templates, such as <style>, as they will not be parsed.
How do I get this tag onto the page?
NOTE: I know this is a hacky, non-preferred way to include styles. This solution will only get used in the context of storybook, where I need to include specific CSS files for specific stories (without storybook/webpack adding them to every story). If I use normal webpack loaders, each tag is added to every story. Importing the styles as a string is the only way I've found to sidestep that behavior.
Try to add the style to the src tag of the style in your SFC :
<style lang="sass" src="../../app/javascript/styles/site.sass">
</style>
This seems to work!
import sitePackCss from '!!raw-loader!sass-loader!../../app/javascript/styles/site.sass';
In template:
<component is="style" type="text/css">${sitePackCss}</component>
Note: the sass files have references to fonts that were not working correctly using this technique. I had to update the staticDirs config to make those paths work. https://storybook.js.org/docs/react/configure/images-and-assets
I just committed and pushed a minor CSS tweak. On my server I git pull, npm run build, and forever restart __sapper__/build
Now there seems to be more than one version of the same CSS rule across different files, as per the below screenshot (this is after disabling browser cache):
The correct rule is the third one (vertical-align: top; margin-top: 1px;), which seems to be a combination of CSS files.
Any idea where the 'old' rules are coming from? Cached somewhere somehow?
/EDIT This is my rollup.config.js: https://gist.github.com/Bandit/bbcfd6c70ace5800765313dfe6021854
/EDIT2 The styles in question are in a /style/global.scss file, which is included using the following code in /routes/_layout.svelte:
<style lang="scss" global>
#import "./style/global.scss";
main {
background-color: white;
padding: 5rem 1rem 0 1rem;
}
</style>
Guessing this is somehow the issue? Where is the right place to 'inject' global stylesheet for colours/theme/typography etc?
/EDIT3 The styles being included via _layout.svelte are being included more than once in dev as well, here's a screenshot:
These selectors don't seem to come from a Svelte component, since they're not scoped (e.g. .split-button.svelte-a9ylb1)? Or are you using :global(.split-button) in a Svelte component?
Anyway... I failed to reproduce your issue, but my intuition is that your problem probably comes from the postcss plugin. It has an inject option that is enabled by default. What this option does is injecting a <style> tag in the <head> of your doc; the code that does this is appended to your modules' JS by the postcss plugin. This behaviour might very well clash with what svelte-preprocess or rollup-plugin-svelte is doing.
Try adding inject: false in the 3 places where you're using postcss in your Rollup config, and see if this helps.
Another possibility might be the service worker. I don't think an issue there could produce your result you get, but we never know... You should try options like "Update on reload" and "Bypass for network" (I don't know what are the equivalent options in your browser) to see if that makes a difference.
Otherwise, you may have to show more of your code. Where does this precise CSS rule come from (e.g. style tag in a Svelte component, SCSS file in node_modules, ...)? How is it imported into your project (e.g. import './app.css', #import './app.scss', etc.), and where? Also, I'm surprised that you have the postcss rollup plugin only in the server (the one that is not registered in sveltePreprocess)... What do you need this for, that you don't need on the client?
EDIT: Follow up
Wait, what? You've got some style files under your routes directory?? routes/style/global.scss?
Even with that, I don't appear to be able to reproduce your problem, but it's worth noting that Sapper will try to include every file it encounters under this directory. If you've got a plugin that lets you import *.scss files, then Sapper will actually see a global.scss.js, so it will think it's a server route. Without a plugin that can eat SCSS, it should... crash. If the plugin in question is postcss with its default inject option still to true, to me it looks like a star suspect...
Anyway, some further points of clarification...
svelte-preprocess enables lang="xxx", global attribute in <style global ...>, in .svelte files only.
rollup-plugin-postcss can additionally be added, directly in plugins array (i.e. not as an option of svelte plugin). It gives support for import './foo.scss', in .js files, as well as in the <script> part of .svelte files.
(Of course, SASS support by PostCSS, or PostCSS support by Svelte preprocess are depending on the config you feed them.)
OK. So now there are multiple places where some CSS / SCSS can enter your build. That I can think of, there are the following ways:
<link rel='stylesheet' href='global.css'> in src/template.html: this one will copied as is without processing.
I suppose you can also have such a "custom" <link> tag in the markup (~HTML) part of a .svelte file, and it would be included as is in the resulting HTML (you'd still have the responsibility that the reference CSS file be accessible at the given URL).
import 'something.css' or 'import 'something.scss'in a.jsor JS part of a.sveltefile: these will get processed by bundler & plugins, and converted to some JS code, with optionally additional assets that the JS can reference (typically, a proper CSS file is generated, and some JS code dynamically injects atag for it at runtime; another approach is to generate some JS that will inject every CSS rule in the doc). PostCSS withinject: true` uses the CSS + inject tag method.
the CSS / SCSS style that you write in the <style> part of a .svelte file will also be processed by the Svelte plugin in a similar way as described just before (preprocess option required to accept anything else than raw CSS); depending on the plugin configuration, it may also try to write a '.css' file for your application (see docs. With the emitCss option, that is apparently needed for Sapper, it should output one CSS file per component (or maybe entrypoint).
In your case, you say that you've removed rollup-plugin-postcss from your config, so the 3rd point (import css from js) should not be possible anymore.
Well... I just hope this can help you investigate further.
I've pushed a Sapper + PostCSS example on a branch on this repo. As far as I can tell, it doesn't have the issue you're describing here. So maybe you can find the problem by comparing with what you have. See this commit for the diff with the vanilla official template.
I tried to also add rollup-plugin-postcss, like you initially had in your config, in order to be able to import .scss from outside of Svelte components. But I failed to find a way to do this that don't conflict with Sapper.
EDIT 2
Oh, and just to be sure... Be sure to try a little rm -r __sapper__ && rm -r src/node_modules/#sapper (notice: node_modules under src, not the one in your project's root) before pursuing your investigation. I'm sure you've already done that, but better safe than sorry. Stale things can live in there.
I use nativescript with VueJs
My problem is that my page specified CSS files aren't used.
My start page is start.js and in the same folder I have a start.css
but the styles aren't applied.
Do I need to something else, or configure?
Because at the docs I said that it normally should work like this.
Always refer the appropriate docs for the flavour, since you are using Vue you must follow the docs here. What you were referring to was for core js one.
With NativeScript Vue you have to write scoped styles within your component, just the same way how you would do it for a Vue based web app.
An external file can be used as Page-Specific CSS as follows in NativeScript-Vue:
<style scoped src="./Home.css"></style>
Where, Home.css is located in your components folder.
Similarly, for SCSS:
<style lang="scss" scoped src="./Home.scss"></style>
Note: You'll need to rebuild your app by if its running on an emulator/device when you make this addition to your .Vue file.
This is one of those "what should we do about this"-questions. As you know, web components are supposed to be small, contained applications for websites. However, sometimes these needs to be styled depending on the site they're embedded on.
Example: "Sign up to our newsletter"-component. This component would have a few key items:
An input box
A button
Maybe recaptcha
A method that talks to your service once the button is pressed (passing in the email)
We're going to use Google and YouTube as examples. Google's color scheme is blue (let's imagine that) and YouTube's color scheme is red. The component would then be something like <newsletter-signup></newsletter-signup> on the page you're embedding it in. Both Google and YouTube have this.
The problem comes in, when the component needs to inherit the styles from Google and YouTube. A few deprecated CSS selectors would be great for this, because Google and YouTube's style sheets could simply enable colors for the Shadow DOM, so we wouldn't have to copy/paste the styles. The component should theoretically not know anything about the styles from the host, because we want it to inherit from the host (Google and YouTube).
At the moment, I'm creating a web component using Angular 6, which has a lot of styles, because it has a lot of elements. I'm copy/pasting styles, Bootstrap, icons, and so on from the host site, then styling them based on <newsletter-signup brand="google"></newsletter-signup>. So if the brand is Google, the colors should be red, for example.
This is really bad, because of a few reasons:
Styles have to be updated on both the web component and on the host
Duplicated code is never a good idea
If all the styles are copied 1:1, the amount of bytes required for styles is doubled
How would I, as a developer, take this into account? How do I make styles on the host, then apply them on my web component (call it inheritance)? I'm sure someone has had the exact same problem with Shadow DOM as I am experiencing. Thanks for reading.
I realize you do not want to write same kind of rules for your common component(selector)
i.e. you want to do styling as where your common selector is placed.
Things you can do to handle this:
1. Create your own logical css framework
Write most commonly used CSS rules in global css.For example if you have integrated bootstrap and you want to override bootstrap, you will write most common overrides in app.css which overrides bootstrap.
"styles": [
"node_modules/bootstrap/dist/css/bootstrap.min.css",
"src/styles/app.scss"
],
This app.scss should be written in way to which you can override.
Send Rules as input
send custom rules Obj and use in elements you want to override.
<newsletter [input]="customRulesObj"></newsletter>
component.ts
customRulesObj = new CustomRulesClass();
customRulesObj.color = 'red';
You can send rules in input in various component by creating a common class
as you know where you are embedding this component.
Extend this component from a common component
If you are too concerned for css you can extend your component from a common component which provides you with css logic as per need.
export class NewsLetterComponent extends CSSComponent implements OnInit
{
}
css-component.ts
In this component can logically define css as per host, current routerlink and
other multiple if else condition.
You can define rules by switch case conditions and bind those rules to component you have extended.
One of the biggest must-do's of web components is: My host (page where I'm embedding my web component) should not depend on the web component nor know about the web component.
What this basically means: Styles of my web component should not be shared with the host.
If my host decides to update the styles, it should affect my web component. Not the other way around. To solve this, I imported the external styles from my host directly inside the CSS file using #import. Here's an example:
import url("https://my-host.com/styles/core.css");
my-component {
//all styles goes here
}
I did this using SASS but can be done using regular CSS.
This is not a great solution at all, but it does what I want: Inherit the styles from the host. Although I would have to import every stylesheet there is, it still works.
A downside to my solution: When I load the page, it will send a request to the style from the <link> element inside the <head>-tag my host, but also to the style inside my import. So the styles are loaded twice. For our application, which is internal use only, it doesn't matter if we request additional ~200 KB data.
This question is a few years old and the situation has changed. The way to share styles with web components is now to use link tags to a shared stylesheet.
Inside each component:
<link rel="stylesheet" href="https://my-host.com/styles/core.css">
Reference:
https://github.com/WICG/webcomponents/issues/628
Because I'm using Webpack to also bundle my css and that my script tag is at the bottom of my HTML, on initial page load I get the content of the page without any of the styling.
Then all of a sudden the styling comes in when the script kicks in.
Webpack is very useful to help bundle the CSS but this behavior is quite unsettling and not really acceptable.
What are common ways to remedy this problem?
You can try using extract-text-webpack-plugin to break out the css in to their own files. That way you can add <link> tags yourself to those pages you wish to have their styles loaded before the JS is loaded. See stylesheets as separate bundle.
For webpack v4, mini-css-extract-plugin should be used instead of extract-text-webpack-plugin (source). There are usage examples on their README.