:first-child styles not beeing applied in Vue Component - css

Problem:
Normally the first <User> should have margin-left: 0px and should align to the left with the other elements but this doesn't work as you can see in the screenshot below. Anyone got an idea how to fix this properly?
sidebar.scss Code:
.user {
margin-left: -8px;
&:first-child {
margin-left: 0;
}
}
Sidebar.vue Code
<template>
<section class="sidebar">
<slot></slot>
</section>
</template>
<script>
export default {
name: "Sidebar"
}
</script>
<style scoped lang="scss">
#import 'sidebar';
</style>
Profile.vue Code
<template>
<main>
<Sidebar>
<Header text="Members" :button="{text: 'Edit'}" hasBorder="true"></Header>
<User img="..."></User>
<User img="..."></User>
<User img="..."></User>
<User img="..."></User>
<Button color="grey" isRounded="true" isIconOnly="true"></Button>
</Sidebar>
</main>
</template>
User.vue code:
<template>
<div class="user">
<img v-if="this.img" :src="this.img" :alt="this.name">
<div v-if="!this.img"></div>
<h6 v-if="this.name">{{this.name}}</h6>
<slot></slot>
</div>
</template>

Not too sure if this is at all the problem here, but note that :first-child does not pick the first occurrence by the mere class name, but by the tag within a parent element. In other words: you cannot use a class name to apply :first-child to. Instead, use something more semantic like
<ul class="users">
<li class="user"></li> <!-- there is your component -->
<li class="user"></li>
<li class="user"></li>
</ul>

Use ::v-deep to change scoped css.
I answered this already but I have an old and more descriptive answer available which might be a help to you to understand the usage https://stackoverflow.com/a/56698470/7358308
https://vue-loader.vuejs.org/guide/scoped-css.html#deep-selectors
Note: Don not use /deep/ because it's deprecated and also >>> combinator will not work too as you are using sass preprocessor
::v-deep {
.user {
margin-left: -8px;
&:first-child {
margin-left: 0;
}
}
}

Scoped style is not shared between parent and child components. You have several options:
Make style global by removing scoped attribute from style section in parent.
Import style into the target component (User).
Use the deep combinator in scoped style.
>>>.user {
...
}
or
/deep/ .user {
...
}
or
::v-deep .user {
...
}

Related

CSS selectors not working as expected when using each and components in svelte

Why svelte declares unused CSS selector when there could be inner elements inside my Component?
<div class="users">
{#each [1,2,3,4,5] as id}
<User name={id} />
{/each}
</div>
<style>
.users > * {
margin-right: 5px;
}
</style>
Error:
Unused CSS selector ".users > *" (12:4)
Repl:
https://svelte.dev/repl/6c8b4a6a808c4aee80e51af58b4fcda4?version=3.44.0
User is a regular div. Not sure if I should use another pattern to achieve this.
Multiple things:
I don't see a users class in your code, the div has a class container, I guess you meant that one
Svelte scopes all styles to the current component, which means elements of children are not targeted. In your case, you want to target any direct child of your div, but the elements are inside User, so this doesn't work
Solution: Use :global which tells Svelte to not scope the selector to the current component:
<script>
import User from './User.svelte'
</script>
<div class="users">
{#each [1,2,3,4,5] as id}
<User name={id} />
{/each}
</div>
<style>
/* you could also write :global(.users > *) but that
would also target any "users" class somewhere else */
.users > :global(*) {
border: 3px solid teal;
}
</style>
In Svelte the css is scoped to the component by default, so styles defined inside a component will have no effect anywhere else.
(the styled elements inside a component get an additional class like 'svelte-1g7lik1')
The 'users' div is except for the User component - which is not affected by the styling - empty, so the declaration has no effect on anything and hence is redundant
You can solve the problem by wrapping your css statement in :global()
See this REPL
<script>
import User from './User.svelte'
</script>
<div class="users">
{#each [1,2,3,4,5] as id}
<User name={id} />
{/each}
</div>
<style>
.users > :global(*){
border: 3px solid teal;
}
/*>> .users.svelte-16kesww>* {...} in bundle.css */
>> affects 'childcomponent-children'
:global(.users > *) {
box-shadow: 2px 3px 0 0 pink;
}
/*>> .users > * {...} in bundle.css
>> affects globally all children of elements with class 'users' */
</style>

Vue 3 slot styles from child component

Summary: I need to style the contents of a <slot>, from the child component. I'm using scoped css and the styles don't apply:
I have the following two components:
<!-- Parent.vue -->
<template>
<h1>{{ msg }} from Parent</h1>
<Child>
<h1>{{ msg }} from Child</h1>
</Child>
</template>
...
<style scoped>
h1 {
color: green;
}
</style>
<!-- Child.vue -->
<template>
<slot></slot>
</template>
...
<style scoped>
h1 {
color: red;
}
</style>
I want the 2nd <h1> to be red, but it's green, since the component is rendered with something like:
<h1 data-v-452d6c4c data-v-2dcc19c8-s>Hello from Child</h1>
<style>
h1[data-v-452d6c4c] {
color: green;
}
h1[data-v-2dcc19c8] {
color: red;
}
</style>
data-v-452d6c4c comes from Parent, and data-v-2dcc19c8-s from Child
If the second attribute, in the <h1> tag, was just data-v-2dcc19c8 the style I wanted would be applied, but since it has that -s suffix (slot?), it doesn't.
I could probably find some other solution with a class or something, but I rarely use <slot> and I want to understand the inner workings. That -s tells me that what I'm trying to do can be dealt with the help of the framework, what am I missing?
A working sample:
https://codesandbox.io/s/condescending-brown-ilfwn
Use the new :slotted selector in Vue 3:
Child.vue
<template>
<slot></slot>
</template>
<script>
export default {
name: "Child",
};
</script>
<style scoped>
:slotted(h1) {
color: red !important;
}
</style>
In Vue 3, child scoped styles don't affect slotted content by default.
In your particular example, the !important modifier is also necessary because the parent also defined an h1 style which would take precedence otherwise

How styling works over components in angular?

Question is not clear but I'll break it down. In angular we can write isolated css for styling. It works pretty well for native html elements. But unlike react, angular wrap our html with custom elements like <app-card>...</app-card>. When I write css for those wrapper elements, it doesn't work .
If I have a post list like
<div class="post-list">
<app-card [post]="post" *ngFor="let post of posts"></app-card>
</div>
If I write css to apply some vertical gap between app-card components in PostListComponent. Well nothing happens.
.post-list app-card:not(:last-child) {
margin-bottom: 2rem;
}
How can I make it work? Or with angular logic, how can I apply vertical gap between angular components
Just add display: block; on your app-card component & it will work as expected.
.post-list app-card {
display: block;
}
.post-list app-card:not(:last-child) {
margin-bottom: 2rem;
}
<div class="post-list">
<app-card>Card 1</app-card>
<app-card>Card 2</app-card>
<app-card>Card 3</app-card>
</div>
You can define encapsulation: ViewEncapsulation.None in your Component like this:
#Component({
selector: 'foo',
template: './foo.component.html',
styleUrls: ['./foo.component.scss'],
encapsulation: ViewEncapsulation.None
})
export class FooComponent { }
Which will treat your .css as the same if you were putting it in the global scope.
To be more accurate, it won't append .fooComponent to each css rule in foo.component.scss.
You can make the iteration in div tag then add your class
<div class="post-list">
<div class="post" *ngFor="let post of posts">
<app-card [post]="post"></app-card>
</div>
</div>
And in your css
.post-list .post:not(:last-child) {
margin-bottom: 2rem;
}
There is no reason it shouldn't work. Just tried to put in some of your code here. https://stackblitz.com/edit/angular-scss-demo-icqrye
app.component.html
<div class="post-list">
<app-new *ngFor="let item of [1,2,3,4]"></app-new>
</div>
styles.scss
.post-list app-new:not(:last-child) p {
margin-top: 2rem;
color: green;
}
And it works perfectly. Are you looking for something else?
And if you want to add the style (margins) to the component directly, you will first need to set the display of the component to block/flex as per requirement.
.post-list app-new:not(:last-child) {
display: flex;
}

Non-generated classname selector in css module

Let's say we have a third-party CardComponent like this
<Card title="This is a title in the header">This is text in the body</Card>
And this renders in plain HTML
<div class="Card">
<div class="CardHeader"></div>
<div class="CardBody"></div>
</div>
And I'm using css modules for styling (scss in this case).
.customCard {
.CardBody {
color: red;
}
}
And then add the class to Card
import styles from ...
...
<Card className={styles.customCard} ...>...</Card>
Above will not work because it creates a generated class for CardBody like CardBody_sjkdh43. Is there a way to select non generated classnames in modules? Or is this only possible in the old fashion way to use a stylesheet?
Demo SandBox
It's an old question but the answer is :global() selector in css modules.
For more info: Exceptions - CSS Modules
Example the code:
<div class={styles.myClass}>
<SomeComponentWithOwnCss />
</div>
Output:
<div class="myClass_s4jkdh3">
<div class="SomeComponentWithOwnCss"></div>
</div>
And CSS:
.myClass div:global(.SomeComponentWithOwnCss) {
background-color: red;
}
Working example overhere: codesandbox
a small addition to #Closery's answer, you saved my ass man❤️.
you could try this for a more convenient approach with SCSS:
.parent :global {
.non-cssmodule-class {
color: red;
}
}
<div className={styles.parent}>
<div className="non-cssmodule-class" />
</div>
output:
<style>
.filename-parent__7MR41 .non-cssmodule-class { color: red; }
</style>
<div class="filename-parent__7MR41">
<div class="non-cssmodule-class"></div>
</div>

Vue styling local components

I have Single-File Components and I want to use it in different places like subscribe page and footer.
But I want to style to be different.
Is it possible to style local components?
Component file: Newsletter.vue
<template>
<form>
<label>Enter your email address here</label>
<input type="text" name="email" required/>
</form>
<button #click="subscribe" >
SUBSCRIBE
</button>
</template>
Subscribe page: Subscribe.vue
let say style Newsletter background-color green and align to left
<template>
<h2>
Subscribe to receive Newsletter
</h2>
<Newsletter></Newsletter>
</template>
<script>
import Newsletter from "#/components/Newsletter.vue";
export default {
components: {
Newsletter
}
}
</script>
<style scoped>
Newsletter{
background-color: green;
text-align:left;
}
</style>
Subscribe page: Footer.vue
but in Footer, I want to style Newsletter background-color red and align center
<template>
<h2>
JOIN OUR NEWSLETTER
</h2>
<Newsletter></Newsletter>
</template>
<script scoped>
import Newsletter from "#/components/Newsletter.vue";
export default {
components: {
Newsletter
}
}
</script>
<style>
Newsletter{
background-color: red;
text-align:center;
}
</style>
This is possible! All you have to do is add the scoped attribute to the style tag.
Here is the snippet from the docs just for reference here:
<template>
<button class="btn btn-close">X</button>
</template>
<style scoped>
.btn-close {
background-color: red;
}
</style>
This will only apply the styles locally, to the component which contains the style tag. Keep in mind though, you really should avoid using tag selectors because they are the most generic. It's a better practice to use a class selector because it has a higher specificity, meaning it is less likely to be over-written (id selectors or inline styles take precedence). I would suggest doing something like this:
<template>
<h2>
Subscribe to receive Newsletter
</h2>
<Newsletter class="newsletter"></Newsletter>
</template>
<script>
import Newsletter from "#/components/Newsletter.vue";
export default {
components: {
Newsletter
}
}
</script>
<style scoped>
.newsletter {
background-color: green;
text-align:left;
}
</style>
In the example above, the newsletter class will make the background-color green only in this single-file component.
There are many options. You may benefit from using the ::v-deep combinator which allows leaking the style to the child components.
Using your example with a small change:
<template>
<h2>
Subscribe to receive Newsletter
</h2>
<Newsletter class="newsletter"></Newsletter>
</template>
<script>
import Newsletter from "#/components/Newsletter.vue";
export default {
components: {
Newsletter
}
}
</script>
<!-- this is the change. Updated to have `::v-deep` to allow style to "leak" to the child component -->
<style scoped>
::v-deep .newsletter {
background-color: green;
text-align:left;
}
</style>
See more at:
https://vue-loader.vuejs.org/guide/scoped-css.html#deep-selectors
Another option to consider is using global style in place of the local style and be sure that each shared class element has a unique parent.
In one component such as Subscribe:
<style lang="scss">
.subscribe {
.newsletter {
// style for this specific version of newsletter
}
}
</style>
In another component where ".somethingelse" should be the specific type of component:
<style lang="scss">
.somethingelse {
.newsletter {
// style for this other specific version of newsletter
}
}
</style>
More info about global specificity:
https://developer.mozilla.org/en-US/docs/Web/CSS/Specificity

Resources