Styled Components: Override Styles not working correctly with Server Side Rendering - styled-components

It seems like overriding styles does not work consistently. I have this two styled components:
const StreamContentContainer = styled.div`
display: flex;
vertical-align: middle;
flex-direction: column;
flex: 0 100%;
align-items: center;
flex-wrap: wrap;
padding: 1rem;
flex-flow: column wrap;
`;
// new Component based on StreamContentContainer
with additional styles and override stlye
const FullStreamContentContainer = styled(StreamContentContainer)`
height: 56.11vw;
overflow: hidden;
padding: 0;
`;
Know if I use my FullStreamContentContainer there should be no padding. On CSR that works fine, the element shows up in the markup correctly.
But if the element is requested initial with SSR the padding of the StreamContentContainer overrides the FullStreamContentContainer again.
It does not matter if the node is generated on SSR or CSR, it shows up the same way in the markup:
<div class="sc-4y67w2-1 fodYop sc-4y67w2-0 WzHos">...</div>.
But if I inspect the element with the DevTools, I can see that on SSR first the class WzHos shows up and then the class fodYop:
Compared to the rules rendered on CSR the rules occur the other way round - like expected:
Does anybody know what causes this weird behavior and how to avoid it?

Problem resolved: Increased the specificity of FullStreamContentContainer styles by using
const FullStreamContentContainer = styled(StreamContentContainer)`
&&& {
height: 56.11vw;
overflow: hidden;
padding: 0;
}
`;
Relating to the SC docs, the repeated class bumps the specificity high enough to override the source order.

Related

Adding content script will make pointer-events: none (chrome extension) [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 to make a header responsive and keep svg at center?

Hi all I already have it looking centered and responsive but I am not sure if there is anything else I can do to make it even better
I have this structure:
div
div
svg
the first div has
width 100%
second has
display flex
align items center
then for the svg I have it's size:
svg[Attributes Style] {
width: 85;
height: 21;
}
What else can I do? I am testing it in various screens but I haven't found issues yet.
Try this
div div { ## this is the second <div> tag
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
}
and
svg {text-align: center;}

Chrome Extension causing misbehavior with divs on active websites [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.

Styled components and scoping

I'm starting to work with styled-components and had a question about scoping.
This is just a dummy example but one that shows the point.
So I have a component. I setup a styled div called Wrapper then instead of creating another styled component to handle group, I thought be easier to just add a class to the inner div called .group and using nesting like in SCSS to style the inner div. This works but the problem with using className for the inner div is there could be a collision with global styles called .group
So, is there a way to avoid this with scoping somehow, or would I have to create another styled component called Group to handle that inner CSS ? Seems like a lot of boilerplate to have to add another styled component just to style the inner components.
const Wrapper = styled.div`
color: blue;
.group {
padding: 10px;
color: green;
}
`
const MyComponent = () => {
return (
<Wrapper>
<div className='group'>
<h1>heading text</h1>
<h2>subheading text</h2>
</div>
<div>This is my blue text</div>
</Wrapper>
);
}
Here is my globalStylesheet with group. Obviously this only has one style but it could have way more to handle grouped elements globally.
export default createGlobalStyle`
h1, h2, h3, h4, h5, h6 {
font-family: '.....';
}
.group {
background-color: red;
}
`;
I know I could also do
> div {
border: 1px solid red;
}
but I want to be able to be more explicit with a className
I think it's better to create another styled-component for group like
const Group = styled.div`
padding: 10px;
color: green;
`
So you can be sure that overwriting styles properly. And if there will be more styles in Wrapper, it stays less readable. Also you can easily replace Group component into children or make as parent(in this case you should rewrite .group style from Wrapper to another one).
In future to prevent boilerplate code you can rewrite existed styled-components like
const Timer = styled.div`
background: #ff5f36;
border-radius: 50%;
width: 48px;
height: 48px;
display: flex;
justify-content: center;
align-items: center;
font-family: GTWalsheim;
font-size: 32px;
color: #ffffff;
`
const TimeIsUp = styled(Timer)`
width: 172px;
border-radius: 8px;
`
EDIT
Also you can easily replace Group component into children or make as parent
I'll try to explain in code below
const MyComponent = () => {
return (
<Wrapper>
<div className='someClass'>
<Group> // so you can replace only your component, without rewriting any style
<h1>heading text</h1>
<h2>subheading text</h2>
</Group>
</div>
<div>This is my blue text</div>
</Wrapper>
);
}
I mean you can easily replace Group component to any place of code. While when you write style from parent as it was in Wrapper, you should replace this .group style from Wrapper to another element which is parent for .group

IE 10+ Transition From display:none Not Working

The problem I'm facing is that when I click on a button to show this element, on IE 10 it doesn't show up. I'm adding the display: block property via JavaScript, that's why it's not in the CSS. It works on all other browsers except IE.
If I remove the transition it shows up.
section {
display: none;
position: relative;
top: 50%;
margin: 0 auto;
width: 400px;
transition: all 0.5s linear;
transform: translate(0,-300%);
}
section.visible {
transform: translate(0,-50%);
}
I solved the issue by showing the section when adding display: block on the visible class rather than using jQuery show(). Guess IE10+ has issues with that.

Resources