How to set SCSS variable value programatically in ReactJs - reactjs

I am working on a project in ReactJS. I have to create multiple themes feature in this website. Color code of all themes are coming from an API.
I have used variables in SCSS and imported them throughout the project to set color. For example
$primary-color: #fec00b;
but now I need to set value of that primary-color dynamically using color code that I am receiving in API response.
So how I can set it programatically?

If you want to change values dynamically then SASS it is not suitable for this task, because of it should be compiled into CSS before it can be used by browser.
It is much more better to use native css variables for such tasks - they are dynamic, you could change them any time both from css and from js. And browser support is also very good (unless you must support IE).
Here's a little example of how to change such vars:
let $color = document.querySelector('#color');
$color.addEventListener('change', (e) => {
let color = e.target.value;
document.querySelector("html").style.setProperty('--box-bg', color);
})
html {
--box-bg: gold;
}
.box {
background: var(--box-bg);
width: 100px;
height: 100px;
border-radius: 4px;
margin-bottom: 16px;
transition: background .3s ease-in-out;
}
<div class='box'></div>
<label>
Choose color
<input type='color' id='color' />
</label>

Related

Extension styling breaks after being published [duplicate]

I wrote a Google Chrome extension, which popups a dialog with an autocomplete field and it's own style, but there are some sites where my CSS gets totally broken, which doesn't look very nice.
I know about isolating styles with iFrames, but in Google Chrome extension there is no way to isolate my HTML and CSS in this way. Another method is to wrap all my stuff into a separated div with it's own id and relative styles for that id, and I do so, but it seems that it doesn't work on some sites with "hard" tags style overloading or "!important" directives in the CSS code.
So, I want to know is there any way to really isolate my styles in z convenient way or it's my bad carma to overload every little CSS property to fix one or another style issue for each site?
By the way: I set up my manifest to load all the things at the "document_end", but I see it's not being applied to the stylesheets which is every time loaded whenever the DOM is ready.
At the time of asking the question, your only option was to either use iframes, or stylesheets with a very high specificity and explicitly set all properties that might affect styles. The last method is very cumbersome, because there will always be some property that is overlooked by you. Consequently, the only usable method for isolating stylesheets was to use iframes.
The solution to this problem -isolation of styles without iframes- is Shadow DOM (since Chrome 25). You can find a tutorial at HTML5 Rocks. For a real-world Chrome extension that uses Shadow DOM to isolate styles, see Display #Anchors (source code here).
As I've recently gone through the gauntlet of this issue, I want to share some information I think is valuable.
First, Rob W's answer is correct. Shadow DOM is the correct solution to this problem. However, in my case not only did I need CSS isolation, I also needed JavaScript events. For example, what happens if the user clicks a button that lives within the isolated HTML? This gets really ugly with just Shadow DOM, but we have another Web Components technology, Custom Elements, to the rescue. Except that as of this writing there is a bug in chrome that prevents custom element in chrome extensions. See my questions here and here and the bug here.
So where does that leave us? I believe the best solution today is IFrames, which is what I went with. The article shahalpk linked is great but it only describes part of the process. Here's how I did it:
First, create an html file and js file for your isolated widget. Everything inside these files will run in an isolated environment in an iframe. Be sure to source your js file from the html file.
//iframe.js
var button = document.querySelector('.my-button');
button.addEventListener('click', function() {
// do useful things
});
//iframe.html
<style>
/* css */
</style>
<button class='my-button'>Hi there</button>
<script src='iframe.js'></script>
Next, inside your content script create an iframe element in javascript. You need to do it in javascript because you have to use chrome.extension.getURL in order to grab your iframe html file:
var iframe = document.createElement('iframe');
iframe.src = chrome.extension.getURL("iframe.html");
document.body.appendChild(iframe);
And that's it.
One thing to keep in mind: If you need to communicated between the iframe and the rest of the content script, you need to chrome.runtime.sendMessage() to the background page, and then chrome.tabs.sendMessage from the background page back to the tab. They can't communicate directly.
EDIT: I wrote a blog post detailing everything I learned through my process, including a complete example chrome extension and lots of links to different information:
https://apitman.com/3/#chrome-extension-content-script-stylesheet-isolation
In case my blog goes down, here's the sources to the original post:
Blog post
Example source
Either use all
.some-selector {
all: initial;
}
.some-selector * {
all: unset;
}
or use Shadow DOM
Library
function Widget(nodeName, appendTo){
this.outer = document.createElement(nodeName || 'DIV');
this.outer.className = 'extension-widget-' + chrome.runtime.id;
this.inner = this.outer.createShadowRoot();
(appendTo || document.body).appendChild(this.outer);
}
Widget.prototype.show = function(){
this.outer.style.display = 'block';
return this;
};
Widget.prototype.hide = function(){
this.outer.style.display = 'none';
return this;
};
Usage
var myWidget = new Widget();
myWidget.inner.innerHTML = '<h1>myWidget</h1>';
You can access the widget contents via myWidget.inner and the outer via myWidget.outer.
Styles
/*
* Reset Widget Wrapper Element
*/
.extension-widget-__MSG_##extension_id__ {
background: none;
border: none;
bottom: auto;
box-shadow: none;
color: black;
cursor: auto;
display: inline;
float: none;
font-family : "Helvetica Neue", "Helvetica", "Arial", sans-serif;
font-size: inherit;
font-style: normal;
font-variant: normal;
font-weight: normal;
height: auto;
left: auto;
letter-spacing: 0;
line-height: 100%;
margin: 0;
max-height: none;
max-width: none;
min-height: 0;
min-width: 0;
opacity: 1;
padding: 0;
position: static;
right: auto;
text-align: left;
text-decoration: none;
text-indent: 0;
text-shadow: none;
text-transform: none;
top: auto;
vertical-align: baseline;
white-space: normal;
width: auto;
z-index: 2147483648;
}
/*
* Add your own styles here
* but always prefix them with:
*
* .extension-widget-__MSG_##extension_id__
*
*/
.extension-widget-__MSG_##extension_id__{
position: fixed;
top: 100px;
margin: 0 auto;
left: 0;
right: 0;
width: 500px;
}
.extension-widget-__MSG_##extension_id__::shadow h1 {
display: block;
margin: 0 auto;
padding: 20px;
background-color: yellow;
border: 10px solid green;
font-size: 20px;
text-align: center;
}
I recently created Boundary, a CSS+JS library to solve problems just like this. Boundary creates elements that are completely separate from the existing webpage's CSS.
Take creating a dialog for example. After installing Boundary, you can do this in your content script
var dialog = Boundary.createBox("yourDialogID", "yourDialogClassName");
Boundary.loadBoxCSS("#yourDialogID", "style-for-elems-in-dialog.css");
Boundary.appendToBox(
"#yourDialogID",
"<button id='submit_button'>submit</button>"
);
Boundary.find("#submit_button").click(function() {
// some js after button is clicked.
});
Elements within #yourDialogID will not be affected by the existing webpage. And find() function returns a regular jQuery DOM element so you can do whatever you want with it.
Hope this helps. Please let me know if you have any question.
https://github.com/liviavinci/Boundary
Use iframes. It's a workaround, but works fine.
Maxime has written an article on it.

How Do I style the draft-js-plugins/image using css?

I am using the #draft-js-plugins/image to display image in my react application. It is working but I can't seem to understand how to style the plugin so as the style my images that are displayed. I read through the documentation and I read that:
The plugin ships with a default styling available at this location in the installed package: node_modules/#draft-js-plugins/image/lib/plugin.css
When I checked this location, the plugin.css is empty. How do I apply style to the images that are displayed by the plugin? My images are covering the entire page.
You can target the images inside the draft.js editor on your component stylesheet. Here is an example screenshot using scss:
Screenshot: targeting draft.js editor using scss
Here is the code block
.DraftEditor-root {
figure {
margin-block-start: .5em;
margin-block-end: .5em;
margin-inline-start: 0;
margin-inline-end: 0;
}
img {
width: 100%;
// height: 200px;
object-fit: cover;
}
}

How to prevent webpage styling getting affected by inject chrome extension's styling [duplicate]

I wrote a Google Chrome extension, which popups a dialog with an autocomplete field and it's own style, but there are some sites where my CSS gets totally broken, which doesn't look very nice.
I know about isolating styles with iFrames, but in Google Chrome extension there is no way to isolate my HTML and CSS in this way. Another method is to wrap all my stuff into a separated div with it's own id and relative styles for that id, and I do so, but it seems that it doesn't work on some sites with "hard" tags style overloading or "!important" directives in the CSS code.
So, I want to know is there any way to really isolate my styles in z convenient way or it's my bad carma to overload every little CSS property to fix one or another style issue for each site?
By the way: I set up my manifest to load all the things at the "document_end", but I see it's not being applied to the stylesheets which is every time loaded whenever the DOM is ready.
At the time of asking the question, your only option was to either use iframes, or stylesheets with a very high specificity and explicitly set all properties that might affect styles. The last method is very cumbersome, because there will always be some property that is overlooked by you. Consequently, the only usable method for isolating stylesheets was to use iframes.
The solution to this problem -isolation of styles without iframes- is Shadow DOM (since Chrome 25). You can find a tutorial at HTML5 Rocks. For a real-world Chrome extension that uses Shadow DOM to isolate styles, see Display #Anchors (source code here).
As I've recently gone through the gauntlet of this issue, I want to share some information I think is valuable.
First, Rob W's answer is correct. Shadow DOM is the correct solution to this problem. However, in my case not only did I need CSS isolation, I also needed JavaScript events. For example, what happens if the user clicks a button that lives within the isolated HTML? This gets really ugly with just Shadow DOM, but we have another Web Components technology, Custom Elements, to the rescue. Except that as of this writing there is a bug in chrome that prevents custom element in chrome extensions. See my questions here and here and the bug here.
So where does that leave us? I believe the best solution today is IFrames, which is what I went with. The article shahalpk linked is great but it only describes part of the process. Here's how I did it:
First, create an html file and js file for your isolated widget. Everything inside these files will run in an isolated environment in an iframe. Be sure to source your js file from the html file.
//iframe.js
var button = document.querySelector('.my-button');
button.addEventListener('click', function() {
// do useful things
});
//iframe.html
<style>
/* css */
</style>
<button class='my-button'>Hi there</button>
<script src='iframe.js'></script>
Next, inside your content script create an iframe element in javascript. You need to do it in javascript because you have to use chrome.extension.getURL in order to grab your iframe html file:
var iframe = document.createElement('iframe');
iframe.src = chrome.extension.getURL("iframe.html");
document.body.appendChild(iframe);
And that's it.
One thing to keep in mind: If you need to communicated between the iframe and the rest of the content script, you need to chrome.runtime.sendMessage() to the background page, and then chrome.tabs.sendMessage from the background page back to the tab. They can't communicate directly.
EDIT: I wrote a blog post detailing everything I learned through my process, including a complete example chrome extension and lots of links to different information:
https://apitman.com/3/#chrome-extension-content-script-stylesheet-isolation
In case my blog goes down, here's the sources to the original post:
Blog post
Example source
Either use all
.some-selector {
all: initial;
}
.some-selector * {
all: unset;
}
or use Shadow DOM
Library
function Widget(nodeName, appendTo){
this.outer = document.createElement(nodeName || 'DIV');
this.outer.className = 'extension-widget-' + chrome.runtime.id;
this.inner = this.outer.createShadowRoot();
(appendTo || document.body).appendChild(this.outer);
}
Widget.prototype.show = function(){
this.outer.style.display = 'block';
return this;
};
Widget.prototype.hide = function(){
this.outer.style.display = 'none';
return this;
};
Usage
var myWidget = new Widget();
myWidget.inner.innerHTML = '<h1>myWidget</h1>';
You can access the widget contents via myWidget.inner and the outer via myWidget.outer.
Styles
/*
* Reset Widget Wrapper Element
*/
.extension-widget-__MSG_##extension_id__ {
background: none;
border: none;
bottom: auto;
box-shadow: none;
color: black;
cursor: auto;
display: inline;
float: none;
font-family : "Helvetica Neue", "Helvetica", "Arial", sans-serif;
font-size: inherit;
font-style: normal;
font-variant: normal;
font-weight: normal;
height: auto;
left: auto;
letter-spacing: 0;
line-height: 100%;
margin: 0;
max-height: none;
max-width: none;
min-height: 0;
min-width: 0;
opacity: 1;
padding: 0;
position: static;
right: auto;
text-align: left;
text-decoration: none;
text-indent: 0;
text-shadow: none;
text-transform: none;
top: auto;
vertical-align: baseline;
white-space: normal;
width: auto;
z-index: 2147483648;
}
/*
* Add your own styles here
* but always prefix them with:
*
* .extension-widget-__MSG_##extension_id__
*
*/
.extension-widget-__MSG_##extension_id__{
position: fixed;
top: 100px;
margin: 0 auto;
left: 0;
right: 0;
width: 500px;
}
.extension-widget-__MSG_##extension_id__::shadow h1 {
display: block;
margin: 0 auto;
padding: 20px;
background-color: yellow;
border: 10px solid green;
font-size: 20px;
text-align: center;
}
I recently created Boundary, a CSS+JS library to solve problems just like this. Boundary creates elements that are completely separate from the existing webpage's CSS.
Take creating a dialog for example. After installing Boundary, you can do this in your content script
var dialog = Boundary.createBox("yourDialogID", "yourDialogClassName");
Boundary.loadBoxCSS("#yourDialogID", "style-for-elems-in-dialog.css");
Boundary.appendToBox(
"#yourDialogID",
"<button id='submit_button'>submit</button>"
);
Boundary.find("#submit_button").click(function() {
// some js after button is clicked.
});
Elements within #yourDialogID will not be affected by the existing webpage. And find() function returns a regular jQuery DOM element so you can do whatever you want with it.
Hope this helps. Please let me know if you have any question.
https://github.com/liviavinci/Boundary
Use iframes. It's a workaround, but works fine.
Maxime has written an article on it.

Google maps markers labelClass animation not working properly

I am working on this project. I have an angular-google-map directive. I am overwritting defaults markers with labelClass.
CSS is working fine but does not hover.
.marker {
color: white;
border: 2px white solid;
border-radius: 50px;
text-align: center;
font-size: 20px;
letter-spacing: 0.1em;
font-weight: bold;
}
.marker:hover {
background-color: #C52183;
animation: pulse 1s;
}
/* ANIMATIONS */
#keyframes pulse {
50% {
transform: scale(4);
}
100% {
transform: scale(1);
}
}
If you check the example you can see an animation but not with real color. I sometimes get the real animation.
The full project is here.
pd: The problem can't be the animation, if i just try to change some css properties i dont get effect, so i think that the problem is with google maps and css.
I finally fixed this "bug".
The problem was on the MarkerWithLabel library but really isn't a bug, its just an impossibility (with this library). Checking the library we see :
// Set up the DIV for handling mouse events in the label. This DIV forms a transparent veil
// in the "overlayMouseTarget" pane, a veil that covers just the label. This is done so that
// events can be captured even if the label is in the shadow of a google.maps.InfoWindow.
// Code is included here to ensure the veil is always exactly the same size as the label.
this.eventDiv_ = document.createElement("div");
this.eventDiv_.style.cssText = this.labelDiv_.style.cssText;
I just modified the library so i don't create invisible div anymore and also i intercept events with real label. Now it's working under my requirements.
more info about the problem
demo working

How to use polymer core icons inside of an angular.js app..?

I am using angular.js to build a SPA and angular-material to for the designing the layout. But turns out that angular material icons are not CSS styleable but polymer's icons are. So I want to use polymer icons inside my angular.js app.. I've included polymer.js in my scripts and import core-icons.html but there is an error..
Uncaught HierarchyRequestError: Failed to execute 'appendChild' on 'Node': Nodes of type 'HTML' may not be inserted inside nodes of type '#document'.
Uncaught TypeError: Cannot read property 'resolveDom' of undefined
How can I use polymer icons inside my angular.js app..??
Angular does not understand ShadowDOM that Polymer makes extensive use of. So when Angular parses and compiles the HTML it does not know what to do which custom elements so it throws an error.
But from Polymer 0.8 this might change and might be compatible with Angular.
One more thing I notices in your Question was that, you added polymer.js instead of webcomponents.js. polymer.js is the Polymer Library which is used in making new elements and interacting with them, where are webcomponents.js is the polyfill for Web components themselves.
I've used polymer icons in an angular project using material angular. In my case I wanted to use the polymer paper-icon-button element as well but I've also done it with just a core-icon element.
I did not need to include polymer.js, just:
polymer.html, core-icon.html, core-icons.html
I used a paper-icon-button so I also included
paper-icon-button.html
and I used am icon from the social set so I included core-icons/social-icons.html as well
for just a core-icon (i had it in a button but i don't think it's necessary)
<button ng-click="toggleLeft()" class="rp-menu-button">
<core-icon icon="menu></core-icon>
</button>
The CSS (LESS):
.rp-menu-button {
background: none;
border: none;
.rp-menu-icon;
}
.rp-menu-icon {
width: 48px;
height: 48px;
color: #toolbar-font-color;
}
Or for a paper-icon-button:
<paper-icon-button class="rp-card-paper-icon" id="share" icon="social:share">
The CSS (LESS):
.rp-card-paper-icon {
text-align: center;
color: #inactive-icon;
&:hover {
color: #active-icon;
}
}
.rp-card-paper-icon::shadow core-icon {
width: 18px;
height: 18px;
}
.rp-card-paper-icon::shadow #ripple {
width: 33px;
height: 33px;
}
.rp-card-paper-icon#share::shadow #ripple {
color: green;
}
So You set the color in the class and the size of the icon in .[class]::shadow core-icon and you can use
.rp-card-paper-icon::shadow #ripple
to style to ripple effect (if you are using a paper-icon-button)

Resources