Use CSS variable in Vue component (scoped) - javascript

At the moment I need to have 3 card components, all with three different colors. The colors are applied to the :before pseudo-element (and multiple other areas), and therefore the easiest solution I believe would be to apply this with a CSS variable / property.
At the moment, I instantiate a new CSS property / variable in the mounted() of my component, and this works fine with 1 card, but breaks with multiple. When I have multiple components, only the first one gets the color and the second one does not even get the color. It seems to overwrite the previous color with the first component, and then ignores the second one completely (no color property is shown in dev tools in the second one).
My question is, what would be the best solution to this problem? Is there a way to easily add a scoped CSS variable that does not override the other variables? Or would be the best course of action be to add all these styles in JavaScript?
Vue component
<template>
<div :class="'card ' + clss">
<div class="card-top">
<h3>{{ title }}</h3>
</div>
<div class="card-center">
<p>{{ message }}</p>
</div>
<div class="card-bottom">
Learn more
</div>
</div>
</template>
<script>
export default {
name: "Card",
props: ['clss', 'color', 'title', 'message'],
data() {
return {
}
},
mounted() {
var style = document.querySelector('.card').style;
style.setProperty(`--color`, this.color);
},
}
</script>
<style lang="scss" scoped>
// Example, not all styles are shown
.card:before {
content: "";
position: absolute;
z-index: -1;
top: -16px;
right: -16px;
background: var(--color); // Example of where I need this color
}
</style>
Vue with all components
<template>
<div class="grid grid-cols-12">
<!-- Green -->
<Card
title="Lorem1"
message="Lorem"
color="#00838d"
clss="col-span-4"
>
</Card>
<!-- Purple -->
<Card
title="Lorem2"
message="--bg-color"
color="#0066b2"
clss="col-span-4"
>
</Card>
</div>
</template>

You have to use this.$refs here instead of document.querySelector('.card').
The document.querySelector('.card') is taking the one first element on the page. Using refs you're picking a reference to DOM element inside of your card item. Please add the ref attribute to your div in the template and replace document.querySelector('.card') with reference to your div using this.$refs .
Reference: https://v3.vuejs.org/guide/component-template-refs.html

CSS are cascading, the variable should be applied to a hierarchy of elements. It's a bad sign that DOM is directly accessed in Vue component. document.querySelector queries all elements, regardless of component instance they belong to, also accesses only one that can be unrelated to current component instance.
If DOM elements need to be acccessed in a component, this is commonly done through refs:
<div :class="'card ' + clss" ref="card">
and
mounted() {
this.$refs.card.style.setProperty(`--color`, this.color);
},
CSS variables (custom properties) are primarily needed to provide a value for nested styles. This isn't needed if a style is specified in the same component:
<div :class="'card ' + clss" :style="{ background: color }">

Related

Stripe elements style setting in css

I can set styles in the style options object for stripe
var elementStyles = {
base: {
textAlign: 'center',
fontWeight: 400,
fontSize: '12px',
color: '#333'
}
};
var stripe = stripeElements.elements();
cardNumberStripeElement = stripe.create('cardNumber', {
styles: elementStyles
});
But I want to transfer styles to css.
This example does not apply my styles, only those that apply to the container
var elementClasses = {
base: 'card-info-base'
};
var stripe = stripeElements.elements();
cardNumberStripeElement = stripe.create('cardNumber', {
classes: elementClasses
});
in css
.card-info-base {
text-align: center "does not work";
font-weight: 400 "does not work";
font-size: 12px "does not work";
height: 100px "works"
}
How do i transfer styles to css?
That's a good question. Unfortunately, it's not possible to apply those styles to the card element in the way you're thinking. The reason is that the card input is embedded within an iframe and styles don't cascade from a parent window down into iframes.
When you add a class to the classes option, the class gets added to the element that you mounting the card in. For example, this is roughly what the DOM would look like once the element has mounted in your case:
<div id="card-element" class="card-info-input StripeElement--empty">
<div class="__PrivateStripeElement">
<iframe src="https://js.stripe.com/v3/">
#document
<input class="InputElement">
</iframe>
</div>
</div>
As you can see, any styles that are applied by card-info-input wouldn't make their way down to the actual input because its in an iframe.
This is different when you use the style option [0]. When you add a custom style using the style option that style is automatically added to the InputElement class when the card mounts. In a way, styles added through the style option are injected into the iframe by Stripe.js - this is why they work the way you expect.
TLDR;
If you want to apply styles to the input inside the iframe use the style option [0]. If you want to apply styles to card's parent element you can use the classes option [1] - this is especially useful if you want to apply different styles depending on the state of the input (focused, invalid, etc.)
[0] https://stripe.com/docs/js/elements_object/create_element?type=card#elements_create-options-style
[1] https://stripe.com/docs/js/elements_object/create_element?type=card#elements_create-options-classes

Random classes getting displayed when we use styled components?

Am using styled components in React. Whenever i write the styles in styled component and if loads the application in the browser am getting some random classes name in the elements tab of developer tools. I just want to know whats happening behind the scene?
const Button = styled.a`
display: inline-block;
border-radius: 3px;
padding: 0.5rem 0;
margin: 0.5rem 1rem;
width: 11rem;
background: transparent;
color: white;
border: 2px solid white;
`
render(
<div>
<Button
href="https://github.com/styled-components/styled-components"
target="_blank"
rel="noopener"
primary
>
GitHub
</Button>
<Button as={Link} href="/docs">
Documentation
</Button>
)
if we inspect and check the element in the developer tools , i can able to see some random classes display like as follow;
<a
href="https://github.com/styled-components/styled-components"
target="_blank"
rel="noopener"
class = "sc-jDwBTQ "
>
GitHub
</a>
Currently styled-components uses MurmurHash algorithm to create a unique identifier and then converts the hash number to alphabetic name.
Each component instance with unique props has it’s own CSS class name which is generated by means of the MurmurHash algorithm, the componentId and the evaluatedStyles string:
const className = hash(componentId + evaluatedStyles);
Then this class name is stored in the component state as generatedClassName.
This was about all I could find in the styled-components FAQ
Each node actually has two classes connected to it: one is static per
component, meaning each element of a styled component has this class.
It hasn't any style attached to it. Instead, it's used to quickly
identify which styled component a DOM objects belongs to or to make
minor changes in the DevTools. It's also used for component selectors.
The static class probably will look something like: .sc-fVOeaW.
The other is dynamic, meaning it will be different for every element
of your styled component with different props, based on what the
interpolations result in. It will probably look like .fVOeaW (note the
lack of "sc" prefix.)
For example, the styled component <Button /> would render with the
same static class every time. If the styles are changed using
interpolations, like <Button secondary />, then the dynamic class will
be a different one, while the static class would remain the same.
Also, Motivation
No class name bugs: styled-components generates unique class names for your styles. You never have to worry about duplication, overlap or misspellings.
TL;DR They are automagically generated and maintained by styled-components.
Are you using Material UI for reactjs by any chance? If so,then just check in the package.json about the version details. If it's version has "rc" appended to it, then please update it to the previous/next stable version.

Need help appying color to the background using color picker

I'm using react-color as my color picker. console.log('Current color:', this.state.color); outputs color in rgba value. So what I want to achieve is changing component colors with a color picker value. Really amateurish I know, what I tried so far was adding classes to divs and with React Helmet:
export class Intro extends React.Component {
render() {
return (
<Style>
{
.intro:hover {
background-color: red;
}
}
</Style>
)
}
}
This wrote it perfectly fine inside </head> tags. And all elements with that class changed the color. When I tried to use this state inside the bracket, it rendered as a raw text color={ this.state.color } inside the style tags. This is the closest workaround I've got. I had no luck setting inline styles into an existing component.
Thanks in advance!
Check out the first option in this article. I think you're mixing terminology. export class is not a css class, that's a javascript ES6 class. CSS goes inside an object and can be passed inline to a DOM element. If you want to use the :hover selector, you'll have to assign a className to the element you are wanting to apply this to and import a css file. This is the example I have below.
file.css
.intro:hover {
background-color: red;
}
file.jsx
export class Intro extends React.Component {
render() {
return (
<div className="intro">some contents</div>
)
}
}

How to prevent styles from reaching into div

I have an interesting situation I'm trying to find a solution to.
There is a script that needs to be included that builds an HTML element on the page. The issue is that there are global defaults CSS styles that are interacting with this built element and preventing it from functioning.
So our ".core-design form" has a property that is being applied to this built element's form. None of our style needs to be used, as the script is calling in its own stylesheet.
Is there a way to prevent the div containing this element from getting any and all styles from our .core-design?
Edit: To add some clarity as to specifics.
1) The core-design class is attached to every page across many sites.
2) The sub-elements being targeted are the element names, not classes.
3) I am able to add styling to target the div around the generated content, but want to avoid modifying .core-design stylesheet due to its scope.
4) The generated content pulls in its own stylesheet.
5) I have no control over the content being pulled in, and it may change in the future.
6) Codepen of the situation I am currently in
body {
// the .core-design is applied here which contains the form styles.
}
form {
min-height:300px;
min-width:300px;
background-color:black;
}
<body>
<div class="controlled">
<div class="generated">
<form>
<!--
I need to prevent this form/generated div from inheriting any styles. Including any other sub-components inside of it. The only item I 'control' is the wrapping div.
-->
</form>
</div>
</div>
</body>
Yes, there's a way. You can use the all CSS property. More info here https://developer.mozilla.org/en-US/docs/Web/CSS/all.
There's no support for IE :(, but there are polyfills available.
This should reset all styles:
div.className * {
all: initial;
all: unset;
}
Solution 1:
Simple, it is all about CSS selector.
Add a class called ignore-this to the form you want to be ignored.
Then update the global css to have form:not(.ignore-this).
Example below, CSS applied to 1st form but skip the 2nd form.
.core-design form:not(.ignore-this) {
background: red;
}
<div class="core-design">
<form>
form 111111
</form>
<form class="ignore-this">
form 222222
</form>
</div>
Solution 2 (updated)
since you have no control over the core-design css, what you could do is to add a css rule like the following and add the class name to the form you want to reset CSS style:
form.ignore-this{
all: unset;
}
The CSS all shorthand property resets all properties, apart from unicode-bidi and direction, to their initial or inherited value.
REF: https://developer.mozilla.org/en/docs/Web/CSS/all
.core-design form {
background: red;
border: 2px solid green;
}
form.ignore-this{
all: unset;
}
<div class="core-design">
<form>
form 111111
</form>
<form class="ignore-this">
form 222222
</form>
</div>
Not so clear but i think you are having css override issues.
If you know the names of the classes and ids the div has you can change you ids and class names of the core-design so they wouldnt be the same.
Like:
if you have a class named .navi and the div contains .navi css will try to either override that of the div or that of the core depends.
Make sure your cores class and id names are unique from that of the div

How to prevent Vue.js from automatically merging CSS classes?

I recently started working with Vue.js and I really enjoy it but I have come across something that I just can't figure out how to do. I have searched and read the documentation but found no solution.
When you add a CSS class to any of your components Vue.js will automatically add or merge the CSS classes with the root element in your child component.
This is a nice feature but I need to deactivate this feature for a specific component since I want the classes to be added to a child element of the root element.
I made this fiddle to illustrate http://jsfiddle.net/rasmuswoelk/9m2j0a9s/3
<div id="demo">
<child-component class="some-class"></child-component>
</div>
(The "some-class" is being added automatically and adds a green background color)
How can I prevent Vue.js from automatically merging CSS classes?
Updated
I disagree that it is intuitive to have a class that is applied to a component be moved by that component to an inner element, so my recommendation is still: Pass it as a prop.
However, it is possible to do what you want to do, with some caveats. The component will have a list of classes that belong on the outer element. In mounted, it looks at $el.classList and finds the classes that are not among the known outer element classes. Those get applied to the inner element and removed from the outer element. Caveat: if one of the outer element classes is applied to the component, it will not be moved to the inner element. And any updates to the applied class will not be caught.
Vue.component('child-component', {
template: `<div :class="outerClasses"><h1 :class="childClasses">Hello</h1></div>`,
data() {
return {
outerClasses: ['child'],
childClasses: []
};
},
mounted() {
this.childClasses = Array.from(this.$el.classList).filter((c) => !this.outerClasses.includes(c));
for (const c of this.childClasses) this.$el.classList.remove(c);
}
});
var demo = new Vue({
el: '#demo'
});
.child {
border: 1px solid red;
padding: 20px;
text-align: center;
}
.some-class {
background-color: green;
}
<script src="//vuejs.org/js/vue.min.js"></script>
<div id="demo">
<child-component class="some-class a b c"></child-component>
</div>
I think that you will want to wrap the whole thing in another <div></div> as Vue will merge the classes on the top element. If you just want the style for <h1> then try doing this:
template: `<div><h1 class="child">Hello</h1></div>`
This should give you the expected behavior, let me know if it doesn't work.
You can't abstract away the wrapper div that you have in your template, and trying to make the component work in such as way as though that div doesn't exist will only lead to confusion and incorrect styling.
Let's say that you managed to get your component to apply the outer class to a nested element in its template (which is what you want). Now when people use that component and apply a class to it, the style is applied to the input element and it is as though the whole component consists solely of the input element and there is no such wrapper div (this is the behavior you want).
The problem with this is that some styles will work (like background-color, or any appearance style), but other styles that affect the layout won't work (like position: absolute, because it will cause the input element to be positioned relative to the wrapper div which is not what was expected).
People who use that component should know about how the template is structured so they can style it correctly, because in reality there is a wrapper div there which they need to take into account.
I should mention you might be able to abstract away the wrapper div by using web components or the shadow DOM, but that's out of context of Vue.
A more simpler way (imo) is to bind the incoming class attribute and dynamic class prop to the data and remove them from the vnode (virtual node) during the created() hook.
Vue.component('child-component', {
template: `
<div class="child">
<h1 :class="staticClass">Hello</h1>
</div>
`,
data() {
return {
staticClass: [],
}
},
created() {
// dynamic :class prop
this.staticClass.push(this.$vnode.data.class)
// delete if not applying on this.$el
delete this.$vnode.data.class
// static class attr
this.staticClass.push(this.$vnode.data.staticClass)
// delete if not applying on this.$el
delete this.$vnode.data.staticClass
},
});
var demo = new Vue({
el: '#demo',
data: {
dynamicClass: 'dynamic-class'
},
});
.child {
border: 1px solid red;
padding: 20px;
text-align: center;
}
.some-class {
background-color: green;
}
.dynamic-class {
border: 1px solid yellow;
}
<script src="https://cdn.jsdelivr.net/npm/vue#2.5.16/dist/vue.min.js"></script>
<div id="demo">
<child-component :class="dynamicClass" class="some-class"></child-component>
</div>

Categories