30
votes

I can't seem to wrap my head around having this container in an md card.

In my material cards, I have this:

<div class="mat-card-header-text"> </div>

I've seen other people notice it. It causes a 40px space on the left of my title. No CSS seems to affect it either.

I am using Angular 4.x and Material2.

10

10 Answers

49
votes

This extra div is actually quite annoying. As it turns out though, you can use shadow-piercing to apply a style to it without changing the CSS emulation mode. You can do this using the ::ng-deep combinator as such:

::ng-deep .mat-card-header-text {
  /* CSS styles go here */
  margin: 0px; // for example to remove the margin
}

You can also just replace the whole header with your own CSS classes instead:

<mat-card>
  <div class="your-header">
    <div class="your-title">
      Your title here
    </div>
  </div>
  <mat-card-content>
    Stuff goes here
  </mat-card-content>
</mat-card>
15
votes

Per their recent recommendation https://angular.io/guide/component-styles, this worked for me:

 :host /deep/ .mat-card-header-text {
     margin: 0;
 }
9
votes

I fixed it by providing negative left margin to md-card-title

<md-card-title style="margin-left:-8px;">
8
votes

This behavior is the result of Angular 2/4's view encapsulation, which in Emulated mode will only inject (via style elements) component styles that match elements actually in your view template.

So if you try to override a .mat-* style like so:

.mat-card-header-text {
  height: auto;
  margin: 0;
}

but your HTML looks like this:

<md-card-header>
  <md-icon md-card-avatar>face</md-icon>
  <md-card-title>{{user.name}}</md-card-title>
  <md-card-subtitle>{{user.status | userStatus}}</md-card-subtitle>
</md-card-header>

then the .mat-card-header-text rule won't be injected into the DOM, since the injector doesn't see such an element in your template.

The simplest workaround is to directly include the div.mat-card-header-text element in your template:

<md-card-header>
  <md-icon md-card-avatar>face</md-icon>
  <div class="mat-card-header-text">
    <md-card-title>{{user.name}}</md-card-title>
    <md-card-subtitle>{{user.status | userStatus}}</md-card-subtitle>
  </div>
</md-card-header>

Edit: as you pointed out, this generates an extra empty div.mat-card-header-text, so it's not an ideal solution. The only way to fix that is if you create your own card component based on md-card (possibly using component inheritence), but at that point you'd just modify the component's CSS directly.

Otherwise, you can switch the view encapsulation mode for your component to None:

import { ViewEncapsulation } from '@angular/core';

@Component({
  moduleId: module.id,
  selector: 'user-card',
  templateUrl: 'user-card.component.html',
  styleUrls: ['user-card.component.css'],
  encapsulation: ViewEncapsulation.None
})
...

Though if you do that, the :host selector will no longer work, so you'll need to replace it with the selector you specified in the @Component decorator:

user-card {
   ...
}
1
votes

Give a negative left-margin to the mat-card-title solve the issue.

.mat-card-title {
   margin-left: -16px;
 }
0
votes

Fixed it using the following css and html:

md-card-title > span {
    background-color: #fff;
    background-color: rgba(255, 255, 255, 0.5);
    position: absolute;
    margin-top: -81px;
    margin-left: -24px;
    font-size: 20px;
    padding: 10px;
}

<div class="main" mat-padding fxLayout="row" fxLayoutWrap="no-wrap" fxLayoutAlign="center start" fxLayoutGap="15px">
    <md-card class="mat-elevation-z2" mat-whiteframe="8" (click)="goToArticle(article)" *ngFor="let article of articleStore.articles() | async">
        <img md-card-image src="{{ article.getCover() }}">
        <md-card-title fxFlex>
            <span>{{ article.title }}</span>
        </md-card-title>
        <md-card-content>
            <p>{{ article.description }}</p>
        </md-card-content>  
    </md-card>
</div>

Using <md-card-header></md-card-header>gives some odd spacing issues. Not sure if this is a bug or not.

0
votes

If you want to support both a version with image in the header and without, this is one possible solution. The idea is to toggle a class which fixes the margin when there is no image available (set through the link input in the example). This way to card looks fine with and without an image.

Html:

<mat-card>
  <mat-card-header [ngClass]="{'fix-margin': !link}">
    <mat-card-title>{{content}}</mat-card-title>
    <mat-card-subtitle>{{content}}</mat-card-subtitle>
    <img *ngIf="link" mat-card-avatar [src]="link">
  </mat-card-header>
  <mat-card-content>
    {{content}}
  </mat-card-content>
  <mat-card-actions>
    <button mat-button>SHOW</button>
  </mat-card-actions>
</mat-card>

Css

.fix-margin {
  margin: 0 -8px;
}

Ts

export class component {
  @Input() content;
  @Input() link;    
}
0
votes

I Know is an old question, but still today is an issue. The way I solved was overriding it like this in the ts file:

ngAfterViewInit() {
    document.getElementsByClassName('mat-card-header-text')[0].setAttribute('style', 'margin: 0 0');
  }
0
votes
mat-card-header{
  justify-content: center;
}
-3
votes

make a class for the mat-card container and add

border-collapse:collapse;

worked wonders for me.