chrome.tabs.insertCSS(tabId, { code : '#import url("custom.css");' });
OR
chrome.tabs.insertCSS(tabId, { file : 'importer.css' });
importer.css:
#import url("custom.css");
a { color:red!important; } /* this rule applied successfully though. */
Doesn't seem to work as expected.
Why doesn't it work and how to make it work?
Edit:
Maybe I am looking at the wrong source code location, but the source code indicates that it uses the regular style sheet parser to parse the injected CSS. In other words, if the #import directive works in regular CSS documents, it should also work in injected CSS.
https://code.google.com/p/chromium/codesearch#chromium/src/extensions/renderer/script_injection.cc&q=script_injection.cc&sq=package:chromium&type=cs&l=312-320
void ScriptInjection::InjectCss(blink::WebLocalFrame* frame) {
std::vector<std::string> css_sources =
injector_->GetCssSources(run_location_);
for (std::vector<std::string>::const_iterator iter = css_sources.begin();
iter != css_sources.end();
++iter) {
frame->document().insertStyleSheet(blink::WebString::fromUTF8(*iter));
}
}
Edit:
Sample code that's not working:
Directory structure:
ext.root
|-- custom.css
|-- custom.css.js
|-- importer.css
|-- manifest.json
manifest.json:
{
"background": {
"scripts": [ "custom.css.js" ],
"persistent": true
},
"manifest_version": 2,
"name": "custom.css",
"version": "1.0",
"web_accessible_resources" : [ "*" ],
"permissions" : [ "webNavigation", "http://*/", "https://*/" ]
}
custom.css.js:
chrome.webNavigation.onCommitted.addListener(function(details) {
console.log('inserting css');
console.log(chrome.runtime.getURL("custom.css"));
chrome.tabs.insertCSS(details.tabId, { file : 'importer.css' });
chrome.tabs.insertCSS(details.tabId, { code : '#import url("custom.css");' });
chrome.tabs.insertCSS(details.tabId, { code : '#import url(custom.css);' });
chrome.tabs.insertCSS(details.tabId, { code : '#import url("' + chrome.runtime.getURL("custom.css") + '");' });
chrome.tabs.insertCSS(details.tabId, { code : '#import url(' + chrome.runtime.getURL("custom.css") + ');' });
chrome.tabs.insertCSS(details.tabId, { code : '#import "custom.css";' });
chrome.tabs.insertCSS(details.tabId, { code : '#import custom.css;' });
chrome.tabs.insertCSS(details.tabId, { code : '#import "' + chrome.runtime.getURL("custom.css") + '";' });
chrome.tabs.insertCSS(details.tabId, { code : '#import ' + chrome.runtime.getURL("custom.css") + ';' });
});
importer.css:
#import "custom.css";
#import "chrome-extension://__MSG_##extension_id__/custom.css";
#import custom.css;
#import chrome-extension://__MSG_##extension_id__/custom.css;
#import url("custom.css");
#import url("chrome-extension://__MSG_##extension_id__/custom.css");
#import url(custom.css);
#import url(chrome-extension://__MSG_##extension_id__/custom.css);
body { background-color: red!important; } /* change page background color to red */
custom.css (Rules in this file are supposed to be applied but not):
body { border: 20px solid red!important; } /* add a 20px border around the body. */
a { background-color: blue!important; } /* change all link's background color to blue. */
If you try to inject such a file into a tab, relative URLs take its origin.
I.e. if you try to inject this into http://example.com/test/index.html, then the above CSS will try to load http://example.com/test/custom.css.
If custom.css is packed in the extension, you should use absolute path obtained from chrome.runtime.getURL():
chrome.tabs.insertCSS(
tabId,
{ code : '#import url("' + chrome.runtime.getURL(custom.css) + '");' }
);
You may try to use the special constant ##extension_id:
#import url("chrome-extension://__MSG_##extension_id__/custom.css");
but I'm not 100% sure it'll work.
Lastly, make sure the imported CSS file is in web_accessible_resources.
#important doesn't work with tabs.insertCSS(). insertCSS seem to be interpreted before injection in the page. How they are interpreted is not documented. For example !important do not works neither.
Related
Scenario
We followed this tutorial to provide our users a dark and a light theme.
Problem
The browser loads the expected css-File (dark or light). Nevertheless, the styles are not applied to our components.
This happens in <head></head>:
<link rel="stylesheet" type="text/css" href="theme-dark.css">
or
<link rel="stylesheet" type="text/css" href="theme-light.css">
As you can see, the browser correctly switches the two themes.
Code
angular.json
"styles": [
"projects/menu/src/styles.scss",
{
"input": "projects/menu/src/theming/theme-dark.scss",
"bundleName": "theme-dark",
"inject": false
},
{
"input": "projects/menu/src/theming/theme-light.scss",
"bundleName": "theme-light",
"inject": false
}
],
theme-service.ts
private _mainTheme$: BehaviorSubject<string> = new BehaviorSubject(
'theme-light'
);
private _darkMode$: BehaviorSubject<boolean> = new BehaviorSubject(false);
darkMode$: Observable<boolean> = this._darkMode$.asObservable();
private _renderer: Renderer2;
private head: HTMLElement;
private themeLinks: HTMLElement[] = [];
theme$: Observable<[string, boolean]>;
constructor(
rendererFactory: RendererFactory2,
#Inject(DOCUMENT) document: Document
) {
this.head = document.head;
this._renderer = rendererFactory.createRenderer(null, null);
this.theme$ = combineLatest([this._mainTheme$, this._darkMode$]);
this.theme$.subscribe(async ([mainTheme, darkMode]) => {
const cssExt = '.css';
const cssFilename = darkMode
? 'theme-dark' + cssExt
: mainTheme + cssExt;
await this.loadCss(cssFilename);
if (this.themeLinks.length == 2)
this._renderer.removeChild(this.head, this.themeLinks.shift());
});
}
setMainTheme(name: string) {
this._mainTheme$.next(name);
}
setDarkMode(value: boolean) {
this._darkMode$.next(value);
}
private async loadCss(filename: string) {
return new Promise((resolve) => {
const linkEl: HTMLElement = this._renderer.createElement('link');
this._renderer.setAttribute(linkEl, 'rel', 'stylesheet');
this._renderer.setAttribute(linkEl, 'type', 'text/css');
this._renderer.setAttribute(linkEl, 'href', filename);
this._renderer.setProperty(linkEl, 'onload', resolve);
this._renderer.appendChild(this.head, linkEl);
this.themeLinks = [...this.themeLinks, linkEl];
});
}
theme-base.scss
#import '~#angular/material/theming';
#include mat-core();
#mixin theming($theme) {
#include angular-material-theme($theme);
}
theme-dark.scss
#import '~#angular/material/theming';
#import './theme-base';
#include mat-core();
// color palette definitions
$primary: mat-palette($dark-md-mcgpaletteblack);
$accent: mat-palette($md-mcgpalettewhite);
$warn: mat-palette($dark-md-mcgpalettered);
$background:mat-palette($dark-md-mcgpalettebackground);
$confirm: mat-palette($dark-md-mcgpaletteorange);
$theme-dark: mat-dark-theme((color: (primary: $primary,
accent: $accent,
warn: $warn,
background: $background,
confirm: $confirm)));
theme-dark.scss - looks the same
In style.scss we import both themes:
styles.scss
#import 'projects/menu/src/theming/theme-dark.scss';
#import 'projects/menu/src/theming/theme-light.scss';
.theme-dark {
#include angular-material-theme($theme-dark)
}
.theme-light {
#include angular-material-theme($theme-light)
}
and then we use those variables:
styles.scss
body {
background: mat-color($background, default);
}
h1 {
color: mat-color($accent, default);
}
But currently we also import them in our components. I think this is one thing, that is wrong.
A dialog would look like this:
#import 'projects/menu/src/theming/theme-dark.scss';
#import 'projects/menu/src/theming/theme-light.scss';
.theme-dark {
#include angular-material-theme($theme-dark)
}
.theme-light{
#include angular-material-theme($theme-light)
}
.mat-button {
color: mat-color($accent, default);
background-color: mat-color($confirm, default);
}
What I tried:
Removing the import of both themes in every stylesheet. Colors are still influenced by the order of the theme import in style.scss. Dark theme last -> dark theme will be used and the other way round.
Using the same variables for the themes as in the tutorial. But then we had to change of all of the background-color: mat-color($confirm, default);
And so on. Spent lots of time...
We can't figure out how to put the missing parts together. Do we need to use mixins? Or are we missing setting the class dark-theme on the root component?
Thank you very much in advance.
We finally got it working.
Our mistakes:
#include mat-core(); must only be imported once (in theme-base.scss)
#import 'projects/menu/src/theming/theme-dark.scss'; and #import 'projects/menu/src/theming/theme-light.scss'; must only be imported once (in styles.scss). Importing them in all of the components lets bundle size explode.
Set theme-dark class directly in theme-dark.scss
Do not set classes theme-dark and theme-light in styles.scss or any other component theme
Create two wrapper elements for whole application: <div [ngClass]="{ 'theme-dark': darkMode$ | async }"> <div class="mat-app-background"> <router-outlet></router-outlet></div></div>
If components need to access the theme, use mixins (example shown below)
In theme-service.ts, use OverlayContainer to apply styles to material dialogs as well (as described here)
Mixin example:
settings-dialog.component.scss
#import '~#angular/material/theming';
#mixin settings-dialog-theme($theme) {
.settings-dialog {
#include mat-elevation(2);
.mat-button {
background-color: mat-color($confirm);
}
}
}
settings-dialog.component.html
<div mat-dialog-content class="settings-dialog"> Put your content here </div>
Create a component theme as described here
component-themes.scss
#import 'your-path/settings-dialog.component';
#mixin component-themes($theme) {
#include settings-dialog-theme($theme);
}
Import it into your base theme file
theme-base.scss
#import '~#angular/material/theming';
#import './component-themes.scss';
#include mat-core();
#mixin theming($theme) {
#include angular-material-theme($theme);
#include component-themes($theme);
}
I'm trying to allow my users to have different themes to choose from however I can't seem to get it working. I've tried multiple different ways, one way is having two different files and i import them based on if the theme is light/dark however once it's imported it stays on the page. Another is within SCSS like this:
$enable-rounded: true;
[theme="dark"] {
$blue: #232c3b;
$body-bg: #262626;
$body-color: white;
}
[theme="light"] {
$body-bg: #ffffff;
$body-color: #000000;
}
#import 'bootstrap/scss/bootstrap.scss';
#import 'bootstrap-vue/src/index.scss';
then in my layouts/default.vue:
import(`~/assets/scss/main.scss`)
export default {
data() {
return {
darkMode: false,
}
},
mounted() {
let bodyElement = document.body;
bodyElement.classList.add("app-background");
let htmlElement = document.documentElement;
let theme = localStorage.getItem("theme");
if (theme === 'dark') {
bodyElement.setAttribute('theme', 'dark')
this.darkMode = true
} else {
bodyElement.setAttribute('theme', 'light');
this.darkMode = false
}
},
watch: {
darkMode: function () {
let htmlElement = document.documentElement;
if (this.darkMode) {
localStorage.setItem("theme", 'dark');
htmlElement.setAttribute('theme', 'dark');
} else {
localStorage.setItem("theme", 'light');
htmlElement.setAttribute('theme', 'light');
}
}
}
}
however nothing happens when I do it this way. I've been trying to figure it out for days and can't seem to get it. It makes me want to just use CSS and avoid SCSS even though I really want to use scss
I think you are importing your scss file in a wrong way. Please try importing it like below out of the script part:
<style lang="scss">
#import '~/assets/scss/main.scss';
</style>
I think you can use this for your project, you can switch into themes or custom on your own
https://color-mode.nuxtjs.org/
I have the following my-file.css:
.a [data-smth] { ... }
.b .c.d { ... }
.e { ... }
#f { ... }
and I want to do something like the following (pseudocode) in Node.js:
let css = readFile('my-file.css')
let prefixedCss = prefixClasses(css, 'prfx-')
writeFile(prefixedCss, 'my-prefixed-file.css')
to end up with my-prefixed-file.css:
.prfx-a [data-smth] { ... }
.prfx-b .prfx-c.prfx-d { ... }
.prfx-e { ... }
#f { ... }
I have found these npm modules:
https://github.com/vic/prefix-css (hasn't been updated in years and has issues)
https://pegjs.org/ (requires lots of low-level AST configuration)
But I was wondering whether there are better/safer solutions that have already been tested/become standard practice ?
NOTE: The above file contents was just an example. I'm looking for a way to achieve this for files whose content is completely unknown to me. So I'm looking for a "universal" solution.
Any help would be most welcome & thank you in advance! :-)
You might want to check https://github.com/marceloucker/postcss-prefixer#postcss-prefixer.
postcss-prefixer
PostCSS plugin to add a prefix to all css selectors classes and ids.
Usage
With PostCSS cli:
Install postcss-cli and postcss-prefixer on your project directory:
npm install postcss-cli postcss-prefixer --save-dev
and at your package.json
"scripts": {
"postcss": "postcss input.css -u postcss-prefixer -o output.css"
}
Others
postcss([ require('postcss-prefixer')({ /* options */ }) ])
Options
prefix
Type: `string`<br>
Default: none
String to be used as prefix
ignore
Type: `array`<br>
Default: `[]`
Array of selectors to be ignored by the plugin, accepts string and regex.
Example
Example of usage with results generated by the plugin.
Code
const postcss = require('postcss');
const prefixer = require('postcss-prefixer');
const input = fs.readFileSync('path/to/file.css', 'utf-8');
const output = postcss([
prefixer({
prefix: 'prefix-'
ignore: [ /selector-/, '.ignore', '#ignore' ]
})
]).process(input);
Input:
#selector-one .example {
/* content */
}
.selector-two .example2 {
/* content */
}
#ignore .ignore {
/* content */
}
#ignore .other {
/* content */
}
Output:
#selector-one .prefix-example {
/* content */
}
.selector-two .prefix-example2 {
/* content */
}
#ignore .ignore {
/* content */
}
#ignore .prefix-other {
/* content */
}
Credits
Plugin based on postcss-class-prefix create by thompsongl.
I'm making a library containing some generic UI components, some kind of Bootstrap. Suppose I have the following LESS files structure:
button.less
checkbox.less
index.less
variables.less
Each component style file has #import "variables.less";. Index file imports all components files. Now I would like to distribute library.less and variables.less files into a package.
How do I bundle the index file? I used to concatenate all the files and remove repeated #import "variables"; line using regexps, maybe there is a Less API for doing this.
You can use less-bundle https://www.npmjs.com/package/less-bundle
// variables.less
#red: #ff0000;
#grey: #cccccc;
// button.less
#import 'variables.less';
.btn-danger {
background: #color;
}
// checkbox.less
#import 'variables.less';
input[type='checkbox'] {
background: #grey;
}
// index.less
#import 'button.less';
#import 'checkbox.less';
Here is main code to do that.
// bundler.js
const path = require('path')
const bundle = require('less-bundle');
const srcFile = path.join(__dirname, './index.less');
bundle({
src: srcFile,
dest: path.join(__dirname, './bundle.less')
}, function (err, data) {
console.log('Error Bundle', err, data);
});
Run bundler.js using command node bundler.js and it will create a bundle.less file containing all styles.
// bundle.less
#red: #ff0000;
#grey: #cccccc;
.btn-danger {
background: #color;
}
input[type='checkbox'] {
background: #grey;
}
I'm trying to use Pure.css in my application. I've installed it using npm and have my brunch-config.js configured like so:
stylesheets: {
joinTo: {
'app.css': /^app/,
'vendor.css': /^(?!app)/
}
}
I expect vendor.css to be generated at this point, but it's not. However, if in my JavaScript, I say require('purecss');, then I get vendor.css generated...but I also get the JavaScript error Cannot find module 'purecss' from '<filename>'. I've also tried various permutations of #import 'purecss' in my CSS without success.
How does one consume vendor CSS from npm modules?
You should use npm config section, for example when I wanted to include node_modules/bootstrap/dist/css/bootstrap.css I had to put the following in my brunch-config.js:
npm: {
styles: {
bootstrap: ['dist/css/bootstrap.css']
}
}
That, combined with your section in files makes it write bootstrap's css into vendor.css.
Full brunch-config.js that works for me:
module.exports = {
files: {
javascripts: {
joinTo: {
'app.js': /^app/,
'vendor.js': /^(?!app)/
}
},
stylesheets: {
joinTo: {
'app.css': /^app/,
'vendor.css': /^(?!app)/
}
}
},
plugins: {
babel: { presets: ['es2015'] }
},
npm: {
styles: {
bootstrap: ['dist/css/bootstrap.css']
}
}
};