Understanding styled components component selector and ampersand - javascript

In styled-components docs, they have this example:
https://www.styled-components.com/docs/advanced#referring-to-other-components
It shows an Icon that changes color when you hover its parent, which is a link, in this case.
const Link = styled.a`
display: flex;
align-items: center;
padding: 5px 10px;
background: papayawhip;
color: palevioletred;
`;
const Icon = styled.svg`
flex: none;
transition: fill 0.25s;
width: 48px;
height: 48px;
${Link}:hover & { // <---- This is what I'm not understanding
fill: rebeccapurple;
}
`;
From the Docs, we know that:
Doc Note #1: styled-components solves this use case cleanly via the "component
selector" pattern. Whenever a component is created or wrapped by the
styled() factory function, it is also assigned a stable CSS class for
use in targeting.
And also that:
Doc Note #2: Ampersands (&) get replaced by our generated, unique classname for
that styled component, making it easy to have complex logic.
Let's analyze ${Link}:hover &
I know it gets translated into the browser as:
and:
I understand that sc-kAzzGY is the "stable CSS class" (Doc Note #1) that is created whenever an element is wrapped by the styled function.
I also know that the Ampersand (&) gets replaced by their generated unique classname (Doc Note #2) for that styled components. Hence, kDmLky is that class.
QUESTION
But what does the resulting selector (picture below) is actually selecting? Can anybody explain that to me?

${Link} is pointing to const Link i.e.: "Hovering my parent changes my style" which gets a class of sc-kAzzGY.
& is kinda like saying "And add this to the current class(es)/id(s)/etc."
So,
.my-class {
some-css: awesomeness;
&:hover {
more-css: extra-cool;
}
}
is equivalent to:
.my-class {
some-css: awesomeness;
}
.my-class:hover {
more-css: extra-cool;
}
Therefore, & points to the containing element const Icon i.e. the speech bubble and gets a class of kDmLky.
When Link is hovered, cause Icon to have fill: rebeccapurple
EDIT:
Just to clarify things a bit more:
When you have a declaration block inside of another declaration block like the example below, that inner declaration block becomes an independent one.
const Icon = styled.svg`
flex: none;
transition: fill 0.25s;
width: 48px;
height: 48px;
${Link}:hover & { // This declaraition block becomes an independent one
fill: rebeccapurple;
}
`;
And the result, in this case, is a declaration block with a selection that says:
When you have a class & which is descendent of the class ${Link} which is in the hover state, apply these rules:
fill: rebeccapurple;
NOTE: ${Link} refers to the Link class and & refers to the Icon class (svg).

${Link}:hover &
Here the ampersand & is the short hand way for referring to the top level component being defined, so I would read it as
${Link}:hover ${Icon}
i.e. it is referring to an Icon component contained inside of a Link component being hovered over
I would also recommend this link to see the more general use case for component selectors with styled components, where it's used in a parent child configuration for selection, and applied to the child

Related

Global Descendant-Only Styles in Svelte

Is there a way in Svelte to add styles that only affect the current component and any descendant components?
Svelte supports a native :global() selector wrapper which will declare styles for that selector in the global scope, but I am looking for something similar which only matches selectors in the current or any descendant components.
For example (REPL):
App.svelte
<script>
import C1 from './C1.svelte';
let name = 'world';
</script>
<div><C1>Hello {name}!</C1></div>
C1.svelte
<script>
import C2 from './C2.svelte';
let name = 'world';
</script>
<style>
:global(div) {
padding: 10px;
background-color: blue;
}
div {
background-color: red;
}
</style>
<div><C2><slot /></C2></div>
C2.svelte
<div><slot /></div>
In the above example, all three components receive the global styling from the middle child component, C1.svelte. I am looking for a way to do a sort of hybrid styling (not passing down styles to child components) to add "global-down" styles that only affect components downward in the component tree.
When the :global() selector wrapper is not used, matched nodes are assigned a unique class which the selector then targets, added to the selector during compilation. What I am asking/suggesting would be something like this:
:find(div) {
background-color: blue;
}
…where :find() similarly assigns a unique class to any HTML elements matched in the same or descending components. Is this possible?
You can scope styles to only child components by combining :global() with a scoped selector. For instance, the following selector will apply to all divs in any component that are the descendant of a div in this component.
<style>
div :global(div) {
padding: 10px;
background-color: blue;
}
</style>
The selector is transformed to something like this:
div.svelte-hash div { /* etc */ }
If you also want to also target top-level divs in this component, you could write the rule like this (though this may have CSS specificity implications):
<style>
div, div :global(div) {
padding: 10px;
background-color: blue;
}
</style>

Cannot remove class from div with Javascript

I'm trying to create a class on a div and then delete it. First I thought just do like I did before with toggleClass, but that doesn't seem to work, because I'm adding a class to an ID instead of a Class. I want my header to have a black background top as well with the class: headerbg.
Also I have a small question about the color of my hamburger menu. I wanted to have a toggle for colors of the white lines (orange instead of white) on the class when pressed on the hamburger menu.
My live version where it is on, works only when 1024px or smaller
My Javascript
$(document).ready(function(){
$(".hamburger").click(function(){
$(".hamburger").toggleClass("closed");
$(".menu").toggleClass("show");
$("header").addClass('headerbg');
});
});
My CSS
.hamburger div{
height: 3px;
background-color: white;
margin: 5px 0;
border-radius: 25px;
transition: 0.3s;
}
.hamburger {
width: 30px;
display: none;
margin: 3em 3em 3em 0;
float: right;
transition: all 0.75s 0.25s;
}
.one {
width: 30px;
}
.two {
width: 20px;
}
.three {
width: 25px;
}
.hamburger:hover div {
width: 30px;
}
.hamburger.closed {
-webkit-transform: rotate(45deg);
-moz-transform: rotate(45deg);
}
#media only screen and (max-width: 1024px) {
.menu {
width: 100%;
background: #000;
margin: 0;
display: none;
}
.show {
width: 100%;
background: #000;
margin: 0;
display: block;
}
.headerbg {
background: #000;
}
.hamburger {
display: block;
}
}
If anyone maybe could lead me to a good example or even better help me out I would really appreciate it! Just came back after 2,5 years break from HTML/CSS as well.
Thanks for looking at the question!
Your understanding of DOM elements seems to be vague. Let's break it down.
I'm trying to create a class on a div and then delete it.
What is it here, what are you trying to delete? The class or the element?
..., because I'm adding a class to an ID instead of a Class.
That's not technically possible. You can't add a class to an id, nor can you add an id to a class. You can only add/remove/modify the id attribute of a DOM element and you can add/remove classes to the className property of a DOM element, referenced in markup by the class attribute.
To keep it short, using jQuery, you can select one or multiple elements by ID, by class, by attribute or by attribute value (in fact, by any valid CSS selector that matches the element), and you can apply the .toggleClass(), .addClass() or .removeClass() methods (or any other jQuery methods) to that element (or to each element in the collection, if they are more than one).
To clarify things for you here's what your current code does:
$(document).ready(function(){
/* when all the DOM has finished building... */
$(".hamburger").click(function(){
/* do the following when an element with class "hamburger" is clicked: */
$(".hamburger").toggleClass("closed");
/* toggle class `closed` on all elements with class "hamburger"
(not only on clicked one!) */
$(".menu").toggleClass("show");
// toggle class `show` on all elements with class "menu"
$("header").addClass('headerbg');
// add class "headerbg" to all <header> elements in page
});
});
Addition, as per OP comment:
First I want to add the class .headerbg on the <header> when I click on the .hamburger class, then when I click on the .hamburger class again I want to delete/remove the class .headerbg for the <header>
This will do it:
/*
* place the following inside an instance of
* $(document).ready(function(){...})
*/
$('.hamburger').on('click', function(){
$('header').toggleClass('headerbg');
})
Note:
$(selector).click(function(){...}) is a shortcut for
$(selector).on('click', [child-selector,] function(){...}). I personally recommend using the latter for all event binding functions to develop a consistent pattern of binding. It helps in the long run, when maintaining code. Also, it allows binding on elements that are not yet in DOM, by using the optional child selector argument. For example, if you wanted to do the binding before .hamburger was created in DOM, you could have, with the following syntax:
$(window).on('click', '.hamburger', function(){
$('header').toggleClass('headerbg');
})
The main difference is the first syntax binds an event listener on each and every instance of .hamburger it finds at the time the binding is done (document.ready in your case).
The second syntax binds only one event, on window object and evaluates at the moment of click if it was fired from inside an element with class .hamburger or not. This means that if you have 1k elements with class .hamburger, you don't bind an event on each of them (resulting in 1k listeners). Also, it has the great advantage that it will work on elements that are added to the page after the binding is done (because evaluation is done at the click event, not at the ready event.
To be even more precise and clear, there are two syntax choices here.
1. Choose between:
.click(function(){...})
.on('click', function(){...})
I always go for second, because it's consistent across all event listeners (it doesn't matter what I put as first argument, instead of click - also, it allows to bind on more than one event type at once: .on('click tap swipe', function(){...}))
2. Choose between
$(child-selector).on('click', function(){...})
$(parent-selector).on('click', child-selector, function(){...}).
If there is only one instance of child-selector and it's already in DOM at the time you do the binding, choose first. If there are more than one instances of child-selector and you want each one present inside parent-selector, use second.
Theoretically speaking, you want as few event listeners as possible, so instead of 2 listeners, one on each child is better to have a single listener on a parent.
Also, best practice is to use the smallest parent selector possible. For example, if you know all your child-selectors will always be contained in a div holding your content — say $('#main') — it's best to bind on that container rather than on $('<body>') or $(window). This will make your code not be evaluated against a click event triggered outside of $('#main'), which in both theory and practice makes your page faster and lighter, for a better user experience.
in your #header you should toggle the headerbg not just adding it :
then your jquery must be :
$(document).ready(function(){
$(".hamburger").click(function(){
$(".hamburger").toggleClass("closed");
$(".menu").toggleClass("show");
if($("#header").hasClass("headerbg")){
$("#header").removeClass("headerbg");
}
else
{
$("#header").addClass("headerbg");
}
});
});
if you need to add the styles of the ID you should pass it through the attr function . like this
$(document).ready(function(){
$(".hamburger").click(function(){
$(".hamburger").toggleClass("closed");
$(".menu").toggleClass("show");
$("header").addClass('headerbg');
$("header").attr('id','#header');
});
});
and you can delete it like this
$("header").attr('id','');
this way you can toggle it

Polymer 1.4.0 to 1.7.0, global :root styles no longer work

I have some styles that are meant to apply anywhere, including deep inside components. They're defined like this:
<style id="base-css" is="custom-style">
:root .primary {
color: red;
}
</style>
The result was that if I had, for example a <span class="primary"> anywhere, no matter how deep inside a Polymer component, it'd always apply. In fact, inspecting this span shows that the style was rewritten as:
:not([style-scope]):not(.style-scope):root .primary {
color: red;
}
Since I've updated my Polymer to 1.7.0, this no longer works. Styles defined this way no longer penetrate into components, and only work outside them. Adding a span with .primary to body and inspecting it shows that the style is now rewritten as:
html .primary:not([style-scope]):not(.style-scope) {
color: red;
}
Which, of course, wouldn't work inside a component, since all inside elements have .style-scope on them.
I read 1.7.0 release notes, and tried replacing :root with html, with exactly the same result.
Does anyone have any idea on how I can get this to work again?
Thank you.
Creating style hooks using CSS custom properties
You can tweak internal styles if you provide styling hooks using CSS custom properties. You create "style placeholders" inside the element that you can override from the main page.
Inside the main page:
<style>
base-css {
--primary: red;
}
</style>
Inside the element:
<style>
:host([background]) {
// Use --primary is not define, use #9E9E9E
background: var(--primary, #9E9E9E);
}
</style>
Documentation
Using CSS variables
Shadow DOM v1: Self-Contained Web Components

Defining custom css styles for html tags

I don't do CSS and I'm not even sure what this is called so excuse the ignorance :-)
.examples {
}
.examples b {
font-weight: bold;
}
.examples p {
margin-top: 0.9em;
margin-bottom: 0.9em;
}
I'm assuming the above means any b or p tags inside a <div class='examples'> will use the styling from .examples and anything custom defined for b or p?
Can I create my own style using that convention, like this?
.examples mystyle {
}
<div class='examples'>
<div class='mystyle'>
...
UPDATE:
I want mystyle to use examples styling, but override with a black bottom border. Using .examples .mystyle the bottom border appears outside examples div, but with .examples mystyle the enclosing div looks good, but the bottom black border is gone. My apologies, so it's not working either way.
http://jsfiddle.net/SAFX/5ft9W/
Since you are using a class on the tag it would need to be a class selector and the element must be a child of .examples:
/* Notice the `.` on mystyle */
.examples .mystyle {
}
<div class="examples">
<div class='mystyle'></div>
</div>
There are several parts to a CSS style:
.examples .mystyle { /* selector */
font-weight: bold; /* This entire line is a declaration consisting of a property & value*/
}
What you seem to be asking about is the terminology to describe child elements inheritance of style from an ancestor; this is the 'cascade' of 'Cascading Style Sheets.' Not all elements inherit from their parents/ancestors (a links, notably, do not inherit the color property by default, though specifying color: inherit; in their css declaration can make them do so).
If you're asking about how to refer to the list of selectors that determine which elements are targeted by a particular rule, that is the 'selector', or 'selector expression.'
References:
CSS (from the Mozilla Developer Network, 'MDN').
Introduction to CSS 2.1 (from the W3C).
Selectors, Level 3 (from the W3C).

remove / reset inherited css from an element [duplicate]

This question already has answers here:
How to reset/remove CSS styles for a specific element or selector only
(17 answers)
Closed last month.
I know this question was asked before, but before marking it as a duplicate, I want to tell you that my situation is a little different from what I found on the internet.
I'm building and embedded script that people can put it on their sites. This script creates a div with a certain width/height and some information in it.
My problem is that some websites declare styles for div that are inherited by my div as well.
for example:
div{
background-color:red;
}
so if I don't set any background color to my div, it will show red even if I don't want that.
The only solutions I come along is to overwrite as many css proprieties, this way my div will show exactly as I want.
The problem with this solution is that there are too many css proprieties to overwrite and I want my script to be as light as it can be.
So my question is if you know another solution to my problem.
It can be in css/javascript /jQuery.
Thanks
"Resetting" styles for a specific element isn't possible, you'll have to overwrite all styles you don't want/need. If you do this with CSS directly or using JQuery to apply the styles (depends on what's easier for you, but I wouldn't recommend using JavaScript/JQuery for this, as it's completely unnecessary).
If your div is some kind of "widget" that can be included into other sites, you could try to wrap it into an iframe. This will "reset" the styles, because its content is another document, but maybe this affects how your widget works (or maybe breaks it completely) so this might not be possible in your case.
Only set the relevant / important CSS properties.
Example (only change the attributes which may cause your div to look completely different):
background: #FFF;
border: none;
color: #000;
display: block;
font: initial;
height: auto;
letter-spacing: normal;
line-height: normal;
margin: 0;
padding: 0;
text-transform: none;
visibility: visible;
width: auto;
word-spacing: normal;
z-index: auto;
Choose a very specific selector, such as div#donttouchme, <div id="donttouchme"></div>. Additionally, you can add `!important before every semicolon in the declaration. Your customers are deliberately trying to mess up your lay-out when this option fails.
You could try overwriting the CSS and use auto
I don't think this will work with color specifically, but I ran into an issue where i had a parent property such as
.parent {
left: 0px;
}
and then I was able to just define my child with something like
.child {
left: auto;
}
and it effectively "reset" the property.
Technically what you are looking for is the unset value in combination with the shorthand property all:
The unset CSS keyword resets a property to its inherited value if it inherits from its parent, and to its initial value if not. In other words, it behaves like the inherit keyword in the first case, and like the initial keyword in the second case. It can be applied to any CSS property, including the CSS shorthand all.
.customClass {
/* specific attribute */
color: unset;
}
.otherClass{
/* unset all attributes */
all: unset;
/* then set own attributes */
color: red;
}
You can use the initial value as well, this will default to the initial browser value.
.otherClass{
/* unset all attributes */
all: initial;
/* then set own attributes */
color: red;
}
As an alternative:
If possible it is probably good practice to encapsulate the class or id in a kind of namespace:
.namespace .customClass{
color: red;
}
<div class="namespace">
<div class="customClass"></div>
</div>
because of the specificity of the selector this will only influence your own classes
It is easier to accomplish this in "preprocessor scripting languages" like SASS with nesting capabilities:
.namespace{
.customClass{
color: red
}
}
Try this: Create a plain div without any style or content outside of the red div. Now you can use a loop over all styles of the plain div and assign then to your inner div to reset all styles.
Of course this doesn't work if someone assigns styles to all divs (i.e. without using a class. CSS would be div { ... }).
The usual solution for problems like this is to give your div a distinct class. That way, web designers of the sites can adjust the styling of your div to fit into the rest of the design.
As long as they are attributes like classes and ids you can remove them by javascript/jQuery class modifiers.
document.getElementById("MyElement").className = "";
There is no way to remove specific tag CSS other than overriding them (or using another element).
you may use this below option.
<style>
div:not(.no_common_style){
background-color:red;
}
</style>
now , if their any place where you do not want to apply default style you can use 'no_common_style' class as class.
ex:
<div class="no_common_style">
It will not display in red
</div>
From what I understand you want to use a div that inherits from no class but yours. As mentioned in the previous reply you cannot completely reset a div inheritance. However, what worked for me with that issue was to use another element - one that is not frequent and certainly not used in the current html page. A good example, is to use instead of then customize it to look just like your ideal would.
area { background-color : red; }
One simple approach would be to use the !important modifier in css, but this can be overridden in the same way from users.
Maybe a solution can be achieved with jquery by traversing the entire DOM to find your (re)defined classes and removing / forcing css styles.

Categories