Override style of first div of mat-form-field - css

How can I override the CSS on the first div generated by the <mat-form-field>?
Background
I have a bunch of fields in an <mat-card> and the last field is causing too much padding at the bottom of the card.
<mat-form-field id="this-feels-hacky-and-wrong"...>
<!-- I want to remove the padding-bottom from this element -->
<div class="mat-input-wrapper mat-form-field-wrapper">
...
</div>
</mat-form-field>
In my stlye.css I put
#this-feels-hacky-and-wrong div{
padding-bottom: 0;
}
and that works but there must be a better/right way to do this, no?
I would prefer a solution that is in my component.scss file.

Try using this ::ng-deep
html
<mat-form-field class="this-feels-better"...>
component.scss
:host ::ng-deep .this-feels-better div.mat-input-wrapper.mat-form-field-wrapper{
padding-bottom: 0;
}
Alternative
If you really don't want to use ng-deep because it'll be removed in the future, and you really want to have your css rule in your component.scss file, then you can change the component's view encapsulation to None
import {ViewEncapsulation} from '#angular/core';
#Component({
//...
encapsulation: ViewEncapsulation.None
})
However, in that case you need to make sure that you prefix all your CSS rules in that component with some kind of selector unique to the component, to prevent the rules leaking to other components.
.uniqueClassOnComponent .this-feels-better div.mat-input-wrapper.mat-form-field-wrapper{
padding-bottom: 0;
}
//Other generic rules
.uniqueClassOnComponent span { ... }

Using ::ng-deep or /deep/ was not a solution for me, as it's being deprecated soon.
ViewEncapsulation.None wasn't a good solution for me either, because it tends to break several other component styles when used.
You can add a custom style (such as no-padding) to your global style sheet.
//example:
mat-form-field.no-padding .mat-form-field-wrapper {
padding: 0;
}
Then apply it to your Material UI Form Fields as needed.
//example:
<mat-form-field class="no-padding">
<mat-label>First Name</mat-label>
<input matInput placeholder="First Name">
</mat-form-field>

With CSS you can use the n-th term. As long as the div you are trying to target has the same parent div as the others, you can reference the first and last child div like such:
.my-div-class:first-child {}
and
.my-div-class:last-child {}
So further this, the nth-term can then be used to target any child like this:
.my-div.class:nth-child(2) {}
This would target the second child.
For some further clarification to your question, this is what your CSS should look like:
mat-form-field:last-child div {
padding-bottom: 0;
}

Update: See pistol-pete's answer above. It's the same, essentially.
For those coming in later, There's another way to do this. Do not use ng-deep, in its various forms, as it's deprecated. Breaking encapsulation is not recommended and goes against Angular's design practice. The solution is global CSS, but with a specific selector.
mat-form-field is part of Angular Material. It has its own encapsulation. Global styles, not component styles, can override this. However, you may not want to override all of these elements, maybe just one or two. I solved this by using a unique class to isolate the elements I wanted to change, then adding .mat-form-field-wrapper to target the element with the padding. Here's an example:
<mat-form-field class="pb-0">
<mat-label>Not so hacky</mat-label>
<imput matInput formControlName="notHacky" />
</mat-form-field>
Then with the pb-0 class, or whatever you want to call it, target in a global CSS space. In Angular, that's styles.css. With Sass, you'll likely have other global files you're importing into the styles file. Add this:
/* sass */
mat-form-field {
&.pb-0 {
.mat-form-field-wrapper {
padding-bottom: 0;
}
}
}
/* plain css */
mat-form-field.pb-0 .mat-form-field-wrapper
{
padding-bottom: 0;
}
You can repeat this for any component from any framework since global styles will be applied everywhere. I would recommend creating a unique file to hold all of these overrides.

Try this one.
HTML
<div style="width: 100%" class="time-period-area">
<mat-form-field class="no-padding" appearance="outline">
<mat-label>Time Period</mat-label>
<input matInput style="height: 0; width: 100%" value=" " />
<div class="mat-input-wrapper mat-form-field-wrapper">
...
</div>
</mat-form-field>
</div>
css
.time-period-area {
mat-form-field.no-padding {
width: 100%;
padding: 0;
}
}

Related

Use pseudo-classes with imported stylesheet as CSS module

I use React in combination with TypeScript, I have a separate CSS file for each component. I import my CSS file as follows:
import styles from 'pages/login/Login.module.css'
Assigning a className looks like that:
<input className={styles.username} ref={nameRef} type="text" placeholder="USERNAME"/>
Is there an option to use pseudo-classes?
<input className={styles.username:active} ref={nameRef} type="text" placeholder="USERNAME"/>
This approach obviously does not work, it is just to make it clearer what I want to achieve.
Once you apply a className, all pseudo-selectors defined in the stylesheet for that CSS class should automatically apply as well.
Because css modules works by adding classes to your elements you can easily add pseudo class selectors.
/* component/text.css */
.text {
color: #777;
font-weight: 24px;
}
.text:hover {
color: #f60;
}
/* component/text.jsx */
import styles from './text.css';
<p className={ styles.text }>Text with hover</p>
Unfortunately, we cannot apply them one by one.

How do I change the Background Color of the middle of mat-expansion-panel (when expanded)?

<mat-expansion-panel>
<mat-expansion-panel-header>
<mat-panel-title>
This is the expansion title
</mat-panel-title>
<mat-panel-description>
This is a summary of the content
</mat-panel-description>
</mat-expansion-panel-header>
<p>This is the primary content of the panel.</p>
</mat-expansion-panel>
How do I change the mat-panel-description area (and only the mat-panel-description area so it is a different background color (e.g. red)?
I've tried various styles but cannot seem to get the entire background of that particular background to change (while keeping the header and footer their original colors).
Using chrome dev tools, adding a background-color to the following .mat-expansion-panel-body works, e.g.:
.mat-expansion-panel-body {
background-color: red;
}
But this does not work when I try to put this in my stylesheet (or even via inline style).
Note: This also works when I put this in my global stylesheet (styles.css) -- but I want to avoid doing this if possible.
Here is a screenshot where I have a red circle indicating the area I want to change the background-color for.
Thanks
in Angular use /deep/ to change styles for material component to force the style down with Emulated encapsulation without changing the encapsulation, so try this:
/deep/ .mat-expansion-panel-body {
background-color: red;
}
or ::ng-deep since /deep/ is deprecated
::ng-deep .mat-expansion-panel-body {
background-color: red;
}
add encapsulation property into your #Component decorator
encapsulation: ViewEncapsulation.None
and then you can simply add styles in your css file.
.mat-expansion-panel-body {
background-color: red;
}
Hope this helps!!
I ended up solving this using global styles, but using a component-specific class as a parent (to prevent overriding all occurrences of .mat-expansion-panel-body)
I chose this method because I didn't want to use deprecated ng-deep or mess with my component's encapsulation.
expansion-overview-example.component.html
<div class="expansion-overview-example">
<p>This is the primary content of the panel.</p>
</div>
styles.css
.expansion-overview-example .mat-expansion-panel-body {
background-color: red;
}

PrimeNg styleClass not change style of p-panel

I am using primeNg. My .html and .scss file like below. But I can't give any styleClass to p-panel. What is wrong in my code?
.html file
<p-panel header="Student Info" styleClass="test">
<div class="ui-g">
<div class="ui-g-12 ui-md-3">
<div class="ui-g-12">
<b>Student</b>
</div>
</div>
</p-panel>
.scss file
.test {
margin-top: 50px;
}
To apply a CSS style to a child component, use ::ng-deep. See this stackblitz for a demo.
:host ::ng-deep .test {
margin-top: 50px;
}
According to Angular documentation:
Component styles normally apply only to the HTML in the component's
own template.
Use the /deep/ shadow-piercing descendant combinator to force a style
down through the child component tree into all the child component
views. The /deep/ combinator works to any depth of nested components,
and it applies to both the view children and content children of the
component.
Use /deep/, >>> and ::ng-deep only with emulated view encapsulation.
Emulated is the default and most commonly used view encapsulation.
...
::ng-deep should be preferred for a broader compatibility with the
tools.
An alternative solution is to set the view encapsulation of the component to ViewEncapsulation.None.
Another alternative is to define the style globally in styles.css, as shown for the second panel in the stackblitz.
One more solution is to use the parent element of <p-panel> element. So for,
<div class="panel-container">
<p-panel>
..
</p-panel>
</div>
We could simply have a class as:
.panel-container div.ui-panel-titlebar {
..
}
and/or
.panel-container div.ui-panel-content {
..
}
BTW to fix #HasanOzdemir's problem we could simply add a little padding to the top of parent element ;-)

`>*` selector not working from parent to child component

I have the following Angular component
<div id="content-div">
<router-outlet></router-outlet>
</div>
I place different components within the div above. My requirement is that all such components should have the css rule height:100%. Instead of duplicating this code, I tried to use >* css but that isn't working.
#content-div >* {
height:100%;
}
Does >* doesn't work across components? How could I make a child component take css rule from its parent?
By default Angular encapsulates CSS. If you want to disabled this you need to do so in your component setup
#Component({
selector: 'app-child-component',
template: `<div class="parent-class">Child Component</div>`,
encapsulation: ViewEncapsulation.None // Use to disable CSS Encapsulation for this component
})
You need to do it in the next way:
:host /deep/ > * {
height: 100%;
}
But it's very unlikely you should do * it's very heavy and slow selector, it's better to limit yourself to something more specific, like the class on container or at least some tag.
Had to change the approach. Instead of using >*, used css-grid. It worked.
#content-div{
height:100%;
display:flex;
align-items: center;
justify-content: center;
}

CSS inline styling ignores hover effect

I was just playing around with CSS and noticed an interesting scenario for which I couldn't really find an explanation. Maybe some of you have the answer for this.
I have a div element with an inline styling
<div id="text-sample" style="overflow:hidden;">This is a sample text to test the CSS behavior of inline styling</div>
My CSS
#text-sample {
width:200px;
white-space: nowrap;
}
#text-sample:hover {
overflow:visible
}
Here the hover effect is not applying. That is, the overflow: visible rule is not taking.
Note: Moving the overflow:hidden from inline style will fix the issue.
I'm looking for the reason why hover effect is not applying. Can anyone explain this scenario?
All else being equal, inline styles take precedence over styles applied via stylesheet rules. In your case, when hovering, the overflow: visible is invoked via the stylesheet rule, but that cannot override the inline style. If necessary, you could try !important.
#text-sample {
width: 200px;
white-space: nowrap;
}
#text-sample:hover {
overflow: visible !important;
}
<div id="text-sample" style="overflow:hidden;">
This is a sample text to test the CSS behavior of inline styling
</div>
But it would be easier simply to specify overflow: hidden in the #text-sample stylesheet rule, instead of giving it inline.
Your inline style will always override your external CSS.
You can use !important in :hover
#text-sample {
width:200px;
white-space: nowrap;
}
#text-sample:hover {
overflow:visible!important;
}
Inline styles take precedence over style sheets. There are two ways to change that: using JavaScript or using !important in the style sheet.
#text-sample:hover {
overflow:visible !important;
}
In CSS, there's something called Specificity. Simply said, something like
#id { color: red; }
would take precedence over something like
.blue { color: red; }
when having something like <div id="id" class="blue">. See example below.
This is because an ID selector (#) is interpreted as more important than a class. In the same manner, an equally specific selector with a later declaration (later in the file) takes precedence and the more specific your selector gets, the more important it is.
For your example: An inline-style takes precedence over anything written in a CSS file (unless using !important). I believe the :hover does not change anything on that fact.
For the detailed rules look my link above.
div {
width:200px;
white-space: nowrap;
}
#text-sample:hover,
#sample-2:hover {
overflow:visible;
}
#sample-2 {
overflow: hidden;
}
#foo {
color: red;
}
.blue {
color: blue;
}
<div id="text-sample" style="overflow:hidden;">This is a sample text to test the CSS behavior of inline styling</div>
<div id="sample-2">This is a sample text to test the CSS behavior of inline styling</div>
<div id="foo" class="blue">foo</div>
EDIT
As mentioned in comments, Specificity does not apply to inline styles. Nevertheless, inline styles are taking precedence over anything in a CSS declarations in files. However, as soon as you move the rule into the same CSS file (as you mentioned will work), the :hover is more important than the other rule since it is more specific in the moment you're hovering.

Resources