diff --git a/src/cdk/stepper/stepper.ts b/src/cdk/stepper/stepper.ts
index 31242d557228..b765d6c2f262 100644
--- a/src/cdk/stepper/stepper.ts
+++ b/src/cdk/stepper/stepper.ts
@@ -20,7 +20,8 @@ import {
   Component,
   ContentChild,
   ViewChild,
-  TemplateRef
+  TemplateRef,
+  ViewEncapsulation
 } from '@angular/core';
 import {LEFT_ARROW, RIGHT_ARROW, ENTER, SPACE} from '@angular/cdk/keycodes';
 import {CdkStepLabel} from './step-label';
@@ -53,7 +54,8 @@ export class StepperSelectionEvent {
 
 @Component({
   selector: 'cdk-step',
-  templateUrl: 'step.html'
+  templateUrl: 'step.html',
+  encapsulation: ViewEncapsulation.None
 })
 export class CdkStep {
   /** Template for step label if it exists. */
@@ -77,6 +79,35 @@ export class CdkStep {
   @Input()
   label: string;
 
+  @Input()
+  get editable() { return this._editable; }
+  set editable(value: any) {
+    this._editable = coerceBooleanProperty(value);
+  }
+  private _editable = true;
+
+  /** Whether the completion of step is optional or not. */
+  @Input()
+  get optional() { return this._optional; }
+  set optional(value: any) {
+    this._optional = coerceBooleanProperty(value);
+  }
+  private _optional = false;
+
+  /** Return whether step is completed or not. */
+  @Input()
+  get completed() {
+    return this._customCompleted == null ? this._defaultCompleted : this._customCompleted;
+  }
+  set completed(value: any) {
+    this._customCompleted = coerceBooleanProperty(value);
+  }
+  private _customCompleted: boolean | null = null;
+
+  private get _defaultCompleted() {
+    return this._stepControl ? this._stepControl.valid && this.interacted : this.interacted;
+  }
+
   constructor(private _stepper: CdkStepper) { }
 
   /** Selects this step component. */
@@ -109,7 +140,8 @@ export class CdkStepper {
   @Input()
   get selectedIndex() { return this._selectedIndex; }
   set selectedIndex(index: number) {
-    if (this._anyControlsInvalid(index)) {
+    if (this._anyControlsInvalid(index)
+        || index < this._selectedIndex && !this._steps.toArray()[index].editable) {
       // remove focus from clicked step header if the step is not able to be selected
       this._stepHeader.toArray()[index].nativeElement.blur();
     } else if (this._selectedIndex != index) {
@@ -134,7 +166,7 @@ export class CdkStepper {
   _focusIndex: number = 0;
 
   /** Used to track unique ID for each stepper component. */
-  private _groupId: number;
+  _groupId: number;
 
   constructor() {
     this._groupId = nextId++;
@@ -172,6 +204,16 @@ export class CdkStepper {
     }
   }
 
+  /** Returns the type of icon to be displayed. */
+  _getIndicatorType(index: number): 'number' | 'edit' | 'done' {
+    const step = this._steps.toArray()[index];
+    if (!step.completed || this._selectedIndex == index) {
+      return 'number';
+    } else {
+      return step.editable ? 'edit' : 'done';
+    }
+  }
+
   private _emitStepperSelectionEvent(newIndex: number): void {
     const stepsArray = this._steps.toArray();
     this.selectionChange.emit({
diff --git a/src/demo-app/stepper/stepper-demo.html b/src/demo-app/stepper/stepper-demo.html
index 70dd6ab6b81b..dbbc6b501ed1 100644
--- a/src/demo-app/stepper/stepper-demo.html
+++ b/src/demo-app/stepper/stepper-demo.html
@@ -19,13 +19,13 @@ <h3>Linear Vertical Stepper Demo using a single form</h3>
       </div>
     </md-step>
 
-    <md-step formGroupName="1" [stepControl]="formArray.get([1])">
+    <md-step formGroupName="1" [stepControl]="formArray.get([1])" optional>
       <ng-template mdStepLabel>
-        <div>Fill out your phone number</div>
+        <div>Fill out your email address</div>
       </ng-template>
       <md-input-container>
-        <input mdInput placeholder="Phone number" formControlName="phoneFormCtrl">
-        <md-error>This field is required</md-error>
+        <input mdInput placeholder="Email address" formControlName="emailFormCtrl">
+        <md-error>The input is invalid.</md-error>
       </md-input-container>
       <div>
         <button md-button mdStepperPrevious type="button">Back</button>
@@ -62,12 +62,12 @@ <h3>Linear Horizontal Stepper Demo using a different form for each step</h3>
     </form>
   </md-step>
 
-  <md-step [stepControl]="phoneFormGroup">
-    <form [formGroup]="phoneFormGroup">
+  <md-step [stepControl]="emailFormGroup" optional>
+    <form [formGroup]="emailFormGroup">
       <ng-template mdStepLabel>Fill out your phone number</ng-template>
       <md-form-field>
-        <input mdInput placeholder="Phone number" formControlName="phoneCtrl" required>
-        <md-error>This field is required</md-error>
+        <input mdInput placeholder="Email address" formControlName="emailCtrl">
+        <md-error>The input is invalid</md-error>
       </md-form-field>
       <div>
         <button md-button mdStepperPrevious>Back</button>
@@ -88,30 +88,28 @@ <h3>Linear Horizontal Stepper Demo using a different form for each step</h3>
 </md-horizontal-stepper>
 
 <h3>Vertical Stepper Demo</h3>
+<md-checkbox [(ngModel)]="isNonEditable">Make steps non-editable</md-checkbox>
 <md-vertical-stepper>
-  <md-step>
+  <md-step [editable]="!isNonEditable">
     <ng-template mdStepLabel>Fill out your name</ng-template>
     <md-form-field>
       <input mdInput placeholder="First Name">
-      <md-error>This field is required</md-error>
     </md-form-field>
 
     <md-form-field>
       <input mdInput placeholder="Last Name">
-      <md-error>This field is required</md-error>
     </md-form-field>
     <div>
       <button md-button mdStepperNext type="button">Next</button>
     </div>
   </md-step>
 
-  <md-step>
+  <md-step [editable]="!isNonEditable">
     <ng-template mdStepLabel>
       <div>Fill out your phone number</div>
     </ng-template>
     <md-form-field>
       <input mdInput placeholder="Phone number">
-      <md-error>This field is required</md-error>
     </md-form-field>
     <div>
       <button md-button mdStepperPrevious type="button">Back</button>
@@ -119,13 +117,12 @@ <h3>Vertical Stepper Demo</h3>
     </div>
   </md-step>
 
-  <md-step>
+  <md-step [editable]="!isNonEditable">
     <ng-template mdStepLabel>
       <div>Fill out your address</div>
     </ng-template>
     <md-form-field>
       <input mdInput placeholder="Address">
-      <md-error>This field is required</md-error>
     </md-form-field>
     <div>
       <button md-button mdStepperPrevious type="button">Back</button>
@@ -148,12 +145,10 @@ <h3>Horizontal Stepper Demo</h3>
     <ng-template mdStepLabel>Fill out your name</ng-template>
     <md-form-field>
       <input mdInput placeholder="First Name">
-      <md-error>This field is required</md-error>
     </md-form-field>
 
     <md-form-field>
       <input mdInput placeholder="Last Name">
-      <md-error>This field is required</md-error>
     </md-form-field>
     <div>
       <button md-button mdStepperNext type="button">Next</button>
@@ -161,12 +156,9 @@ <h3>Horizontal Stepper Demo</h3>
   </md-step>
 
   <md-step>
-    <ng-template mdStepLabel>
-      <div>Fill out your phone number</div>
-    </ng-template>
+    <ng-template mdStepLabel>Fill out your phone number</ng-template>
     <md-form-field>
       <input mdInput placeholder="Phone number">
-      <md-error>This field is required</md-error>
     </md-form-field>
     <div>
       <button md-button mdStepperPrevious type="button">Back</button>
@@ -175,12 +167,9 @@ <h3>Horizontal Stepper Demo</h3>
   </md-step>
 
   <md-step>
-    <ng-template mdStepLabel>
-      <div>Fill out your address</div>
-    </ng-template>
+    <ng-template mdStepLabel>Fill out your address</ng-template>
     <md-form-field>
       <input mdInput placeholder="Address">
-      <md-error>This field is required</md-error>
     </md-form-field>
     <div>
       <button md-button mdStepperPrevious type="button">Back</button>
diff --git a/src/demo-app/stepper/stepper-demo.ts b/src/demo-app/stepper/stepper-demo.ts
index c4f546f0dfed..2c7ed6f7ce92 100644
--- a/src/demo-app/stepper/stepper-demo.ts
+++ b/src/demo-app/stepper/stepper-demo.ts
@@ -1,6 +1,8 @@
 import {Component} from '@angular/core';
 import {FormBuilder, FormGroup, Validators} from '@angular/forms';
 
+const EMAIL_REGEX = /^[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/;
+
 @Component({
   moduleId: module.id,
   selector: 'stepper-demo',
@@ -10,9 +12,10 @@ import {FormBuilder, FormGroup, Validators} from '@angular/forms';
 export class StepperDemo {
   formGroup: FormGroup;
   isNonLinear = false;
+  isNonEditable = false;
 
   nameFormGroup: FormGroup;
-  phoneFormGroup: FormGroup;
+  emailFormGroup: FormGroup;
 
   steps = [
     {label: 'Confirm your name', content: 'Last name, First name.'},
@@ -34,8 +37,8 @@ export class StepperDemo {
           lastNameFormCtrl: ['', Validators.required],
         }),
         this._formBuilder.group({
-          phoneFormCtrl: [''],
-        })
+          emailFormCtrl: ['', Validators.pattern(EMAIL_REGEX)]
+        }),
       ])
     });
 
@@ -44,8 +47,8 @@ export class StepperDemo {
       lastNameCtrl: ['', Validators.required],
     });
 
-    this.phoneFormGroup = this._formBuilder.group({
-      phoneCtrl: ['', Validators.required]
+    this.emailFormGroup = this._formBuilder.group({
+      emailCtrl: ['', Validators.pattern(EMAIL_REGEX)]
     });
   }
 }
diff --git a/src/lib/stepper/_stepper-theme.scss b/src/lib/stepper/_stepper-theme.scss
index d61eb4715996..ed5042d3b031 100644
--- a/src/lib/stepper/_stepper-theme.scss
+++ b/src/lib/stepper/_stepper-theme.scss
@@ -7,30 +7,29 @@
   $background: map-get($theme, background);
   $primary: map-get($theme, primary);
 
-  .mat-horizontal-stepper-header, .mat-vertical-stepper-header {
-
+  .mat-step-header {
     &:focus,
     &:hover {
       background-color: mat-color($background, hover);
     }
 
-    .mat-stepper-label {
+    .mat-step-label-active {
       color: mat-color($foreground, text);
     }
 
-    .mat-stepper-index {
+    .mat-step-label-inactive,
+    .mat-step-optional {
+      color: mat-color($foreground, disabled-text);
+    }
+
+    .mat-step-icon {
       background-color: mat-color($primary);
       color: mat-color($primary, default-contrast);
     }
 
-    &[aria-selected='false'] {
-      .mat-stepper-label {
-        color: mat-color($foreground, disabled-text);
-      }
-
-      .mat-stepper-index {
-        background-color: mat-color($foreground, disabled-text);
-      }
+    .mat-step-icon-not-touched {
+      background-color: mat-color($foreground, disabled-text);
+      color: mat-color($primary, default-contrast);
     }
   }
 
diff --git a/src/lib/stepper/index.ts b/src/lib/stepper/index.ts
index de1802347449..416ee5d2295a 100644
--- a/src/lib/stepper/index.ts
+++ b/src/lib/stepper/index.ts
@@ -17,13 +17,22 @@ import {CdkStepperModule} from '@angular/cdk/stepper';
 import {MdCommonModule} from '../core';
 import {MdStepLabel} from './step-label';
 import {MdStepperNext, MdStepperPrevious} from './stepper-button';
+import {MdIconModule} from '../icon/index';
+import {MdStepHeader} from './step-header';
 
 @NgModule({
-  imports: [MdCommonModule, CommonModule, PortalModule, MdButtonModule, CdkStepperModule],
+  imports: [
+    MdCommonModule,
+    CommonModule,
+    PortalModule,
+    MdButtonModule,
+    CdkStepperModule,
+    MdIconModule
+  ],
   exports: [MdCommonModule, MdHorizontalStepper, MdVerticalStepper, MdStep, MdStepLabel, MdStepper,
-    MdStepperNext, MdStepperPrevious],
+    MdStepperNext, MdStepperPrevious, MdStepHeader],
   declarations: [MdHorizontalStepper, MdVerticalStepper, MdStep, MdStepLabel, MdStepper,
-    MdStepperNext, MdStepperPrevious],
+    MdStepperNext, MdStepperPrevious, MdStepHeader],
 })
 export class MdStepperModule {}
 
@@ -32,3 +41,4 @@ export * from './stepper-vertical';
 export * from './step-label';
 export * from './stepper';
 export * from './stepper-button';
+export * from './step-header';
diff --git a/src/lib/stepper/step-header.html b/src/lib/stepper/step-header.html
new file mode 100644
index 000000000000..3f2310862f6f
--- /dev/null
+++ b/src/lib/stepper/step-header.html
@@ -0,0 +1,17 @@
+<div [class.mat-step-icon]="icon != 'number' || selected"
+     [class.mat-step-icon-not-touched]="icon == 'number' && !selected">
+  <span *ngIf="icon == 'number'">{{index + 1}}</span>
+  <md-icon *ngIf="icon == 'edit'">create</md-icon>
+  <md-icon *ngIf="icon == 'done'">done</md-icon>
+</div>
+<div [class.mat-step-label-active]="active"
+     [class.mat-step-label-inactive]="!active">
+  <!-- If there is a label template, use it. -->
+  <ng-container *ngIf="_templateLabel" [ngTemplateOutlet]="label.template">
+  </ng-container>
+  <!-- It there is no label template, fall back to the text label. -->
+  <div class="mat-step-text-label" *ngIf="_stringLabel">{{label}}</div>
+
+  <div class="mat-step-optional" *ngIf="optional">Optional</div>
+</div>
+
diff --git a/src/lib/stepper/step-header.scss b/src/lib/stepper/step-header.scss
new file mode 100644
index 000000000000..94496b4adcfc
--- /dev/null
+++ b/src/lib/stepper/step-header.scss
@@ -0,0 +1,46 @@
+$mat-stepper-label-header-height: 24px !default;
+$mat-stepper-label-min-width: 50px !default;
+$mat-stepper-side-gap: 24px !default;
+$mat-vertical-stepper-content-margin: 36px !default;
+$mat-stepper-line-gap: 8px !default;
+$mat-step-optional-font-size: 12px;
+$mat-step-header-icon-size: 16px !default;
+
+:host {
+  display: flex;
+}
+
+.mat-step-optional {
+  font-size: $mat-step-optional-font-size;
+}
+
+.mat-step-icon,
+.mat-step-icon-not-touched {
+  border-radius: 50%;
+  height: $mat-stepper-label-header-height;
+  width: $mat-stepper-label-header-height;
+  align-items: center;
+  justify-content: center;
+  display: flex;
+}
+
+.mat-step-icon .mat-icon {
+  font-size: $mat-step-header-icon-size;
+  height: $mat-step-header-icon-size;
+  width: $mat-step-header-icon-size;
+}
+
+.mat-step-label-active,
+.mat-step-label-inactive {
+  display: inline-block;
+  white-space: nowrap;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  min-width: $mat-stepper-label-min-width;
+  vertical-align: middle;
+}
+
+.mat-step-text-label {
+  text-overflow: ellipsis;
+  overflow: hidden;
+}
diff --git a/src/lib/stepper/step-header.ts b/src/lib/stepper/step-header.ts
new file mode 100644
index 000000000000..14901227f698
--- /dev/null
+++ b/src/lib/stepper/step-header.ts
@@ -0,0 +1,73 @@
+/**
+ * @license
+ * Copyright Google Inc. All Rights Reserved.
+ *
+ * Use of this source code is governed by an MIT-style license that can be
+ * found in the LICENSE file at https://angular.io/license
+ */
+
+import {Component, Input, ViewEncapsulation} from '@angular/core';
+import {coerceBooleanProperty, coerceNumberProperty} from '@angular/cdk/coercion';
+import {MdStepLabel} from './step-label';
+
+@Component({
+  selector: 'md-step-header, mat-step-header',
+  templateUrl: 'step-header.html',
+  styleUrls: ['step-header.css'],
+  host: {
+    'class': 'mat-step-header',
+    'role': 'tab',
+  },
+  encapsulation: ViewEncapsulation.None
+})
+export class MdStepHeader {
+  /** Icon for the given step. */
+  @Input()
+  icon: string;
+
+  /** Label of the given step. */
+  @Input()
+  label: MdStepLabel | string;
+
+  /** Index of the given step. */
+  @Input()
+  get index() { return this._index; }
+  set index(value: any) {
+    this._index = coerceNumberProperty(value);
+  }
+  private _index: number;
+
+  /** Whether the given step is selected. */
+  @Input()
+  get selected() { return this._selected; }
+  set selected(value: any) {
+    this._selected = coerceBooleanProperty(value);
+  }
+  private _selected: boolean;
+
+  /** Whether the given step label is active. */
+  @Input()
+  get active() { return this._active; }
+  set active(value: any) {
+    this._active = coerceBooleanProperty(value);
+  }
+  private _active: boolean;
+
+  /** Whether the given step is optional. */
+  @Input()
+  get optional() { return this._optional; }
+  set optional(value: any) {
+    this._optional = coerceBooleanProperty(value);
+  }
+  private _optional: boolean;
+
+  /** Returns string label of given step if it is a text label. */
+  get _stringLabel(): string | null {
+    return this.label instanceof MdStepLabel ? null : this.label;
+  }
+
+  /** Returns MdStepLabel if the label of given step is a template label. */
+  get _templateLabel(): MdStepLabel | null {
+    return this.label instanceof MdStepLabel ? this.label : null;
+  }
+}
diff --git a/src/lib/stepper/stepper-horizontal.html b/src/lib/stepper/stepper-horizontal.html
index 6472a981ff9b..5e2d2ea80fa7 100644
--- a/src/lib/stepper/stepper-horizontal.html
+++ b/src/lib/stepper/stepper-horizontal.html
@@ -1,29 +1,23 @@
 <div class="mat-horizontal-stepper-header-container">
   <ng-container *ngFor="let step of _steps; let i = index; let isLast = last">
-    <div #stepHeader class="mat-horizontal-stepper-header"
-         role="tab"
-         [id]="_getStepLabelId(i)"
-         [attr.aria-controls]="_getStepContentId(i)"
-         [attr.aria-selected]="selectedIndex == i"
-         [tabIndex]="_focusIndex == i ? 0 : -1"
-         (click)="step.select()"
-         (keydown)="_onKeydown($event)">
-      <div class="mat-stepper-index">
-        {{i + 1}}
-      </div>
-
-      <div class="mat-stepper-label">
-        <!-- If there is a label template, use it. -->
-        <ng-container *ngIf="step.stepLabel" [ngTemplateOutlet]="step.stepLabel.template">
-        </ng-container>
-        <!-- It there is no label template, fall back to the text label. -->
-        <div *ngIf="!step.stepLabel">{{step.label}}</div>
-      </div>
-    </div>
-
+    <md-step-header  class="mat-horizontal-stepper-header"
+                     (click)="step.select()"
+                     (keydown)="_onKeydown($event)"
+                     [tabIndex]="_focusIndex == i ? 0 : -1"
+                     [id]="_getStepLabelId(i)"
+                     [attr.aria-controls]="_getStepContentId(i)"
+                     [attr.aria-selected]="selectedIndex == i"
+                     [index]="i"
+                     [icon]="_getIndicatorType(i)"
+                     [label]="step.stepLabel || step.label"
+                     [selected]="selectedIndex == i"
+                     [active]="step.completed || selectedIndex == i"
+                     [optional]="step.optional">
+    </md-step-header>
     <div *ngIf="!isLast" class="mat-stepper-horizontal-line"></div>
   </ng-container>
 </div>
+
 <div class="mat-horizontal-content-container">
   <div *ngFor="let step of _steps; let i = index"
        class="mat-horizontal-stepper-content" role="tabpanel"
diff --git a/src/lib/stepper/stepper-horizontal.ts b/src/lib/stepper/stepper-horizontal.ts
index dbccf06a60c8..776337a8aa5f 100644
--- a/src/lib/stepper/stepper-horizontal.ts
+++ b/src/lib/stepper/stepper-horizontal.ts
@@ -6,7 +6,7 @@
  * found in the LICENSE file at https://angular.io/license
  */
 
-import {Component} from '@angular/core';
+import {Component, ViewEncapsulation} from '@angular/core';
 import {MdStepper} from './stepper';
 import {animate, state, style, transition, trigger} from '@angular/animations';
 
@@ -29,6 +29,7 @@ import {animate, state, style, transition, trigger} from '@angular/animations';
           animate('500ms cubic-bezier(0.35, 0, 0.25, 1)'))
     ])
   ],
-  providers: [{provide: MdStepper, useExisting: MdHorizontalStepper}]
+  providers: [{provide: MdStepper, useExisting: MdHorizontalStepper}],
+  encapsulation: ViewEncapsulation.None
 })
 export class MdHorizontalStepper extends MdStepper { }
diff --git a/src/lib/stepper/stepper-vertical.html b/src/lib/stepper/stepper-vertical.html
index 99dc3a6ac1c1..e32bf82b4f23 100644
--- a/src/lib/stepper/stepper-vertical.html
+++ b/src/lib/stepper/stepper-vertical.html
@@ -1,24 +1,19 @@
 <div class="mat-step" *ngFor="let step of _steps; let i = index; let isLast = last">
-  <div #stepHeader class="mat-vertical-stepper-header" role="tab"
-       [id]="_getStepLabelId(i)"
-       [attr.aria-controls]="_getStepContentId(i)"
-       [attr.aria-selected]="selectedIndex == i"
-       [tabIndex]="_focusIndex == i ? 0 : -1"
-       (click)="step.select()"
-       (keydown)="_onKeydown($event)">
-    <div class="mat-stepper-index">
-      {{i + 1}}
-    </div>
-
-    <div class="mat-stepper-label">
-      <!-- If there is a label template, use it. -->
-      <ng-container *ngIf="step.stepLabel"[ngTemplateOutlet]="step.stepLabel.template">
-      </ng-container>
-      <!-- It there is no label template, fall back to the text label. -->
-      <div *ngIf="!step.stepLabel">{{step.label}}</div>
-    </div>
+  <md-step-header  class="mat-vertical-stepper-header"
+                   (click)="step.select()"
+                   (keydown)="_onKeydown($event)"
+                   [tabIndex]="_focusIndex == i ? 0 : -1"
+                   [id]="_getStepLabelId(i)"
+                   [attr.aria-controls]="_getStepContentId(i)"
+                   [attr.aria-selected]="selectedIndex == i"
+                   [index]="i"
+                   [icon]="_getIndicatorType(i)"
+                   [label]="step.stepLabel || step.label"
+                   [selected]="selectedIndex == i"
+                   [active]="step.completed || selectedIndex == i"
+                   [optional]="step.optional">
+  </md-step-header>
 
-  </div>
   <div class="mat-vertical-content-container" [class.mat-stepper-vertical-line]="!isLast">
     <div class="mat-vertical-stepper-content" role="tabpanel"
          [@stepTransition]="_getAnimationDirection(i)"
diff --git a/src/lib/stepper/stepper-vertical.ts b/src/lib/stepper/stepper-vertical.ts
index b72349db888c..9f85984e74ef 100644
--- a/src/lib/stepper/stepper-vertical.ts
+++ b/src/lib/stepper/stepper-vertical.ts
@@ -6,7 +6,7 @@
  * found in the LICENSE file at https://angular.io/license
  */
 
-import {Component} from '@angular/core';
+import {Component, ViewEncapsulation} from '@angular/core';
 import {MdStepper} from './stepper';
 import {animate, state, style, transition, trigger} from '@angular/animations';
 
@@ -28,6 +28,7 @@ import {animate, state, style, transition, trigger} from '@angular/animations';
         transition('* <=> current', animate('225ms cubic-bezier(0.4, 0.0, 0.2, 1)'))
       ])
   ],
-  providers: [{provide: MdStepper, useExisting: MdVerticalStepper}]
+  providers: [{provide: MdStepper, useExisting: MdVerticalStepper}],
+  encapsulation: ViewEncapsulation.None
 })
 export class MdVerticalStepper extends MdStepper { }
diff --git a/src/lib/stepper/stepper.scss b/src/lib/stepper/stepper.scss
index c91bf3dc4c88..8f443c1f5d63 100644
--- a/src/lib/stepper/stepper.scss
+++ b/src/lib/stepper/stepper.scss
@@ -2,7 +2,6 @@
 
 $mat-horizontal-stepper-header-height: 72px !default;
 $mat-stepper-label-header-height: 24px !default;
-$mat-stepper-label-min-width: 50px !default;
 $mat-stepper-side-gap: 24px !default;
 $mat-vertical-stepper-content-margin: 36px !default;
 $mat-stepper-line-width: 1px !default;
@@ -12,21 +11,9 @@ $mat-stepper-line-gap: 8px !default;
   display: block;
 }
 
-.mat-stepper-label {
-  display: inline-flex;
-  white-space: nowrap;
+.mat-step-header {
   overflow: hidden;
-  // TODO(jwshin): text-overflow does not work as expected.
-  text-overflow: ellipsis;
-  min-width: $mat-stepper-label-min-width;
-}
-
-.mat-stepper-index {
-  border-radius: 50%;
-  height: $mat-stepper-label-header-height;
-  width: $mat-stepper-label-header-height;
-  text-align: center;
-  line-height: $mat-stepper-label-header-height;
+  outline: none;
 }
 
 .mat-horizontal-stepper-header-container {
@@ -35,17 +22,25 @@ $mat-stepper-line-gap: 8px !default;
   align-items: center;
 }
 
+.mat-stepper-horizontal-line {
+  border-top-width: $mat-stepper-line-width;
+  border-top-style: solid;
+  flex: auto;
+  height: 0;
+  margin: 0 $mat-stepper-line-gap - $mat-stepper-side-gap;
+  min-width: $mat-stepper-line-gap + $mat-stepper-side-gap;
+}
+
 .mat-horizontal-stepper-header {
-  display: inline-flex;
-  line-height: $mat-horizontal-stepper-header-height;
+  display: flex;
+  height: $mat-horizontal-stepper-header-height;
   overflow: hidden;
   align-items: center;
-  outline: none;
   padding: 0 $mat-stepper-side-gap;
 
-  .mat-stepper-index {
+  .mat-step-icon,
+  .mat-step-icon-not-touched {
     margin-right: $mat-stepper-line-gap;
-    display: inline-block;
     flex: none;
   }
 }
@@ -54,22 +49,14 @@ $mat-stepper-line-gap: 8px !default;
   display: flex;
   align-items: center;
   padding: $mat-stepper-side-gap;
-  outline: none;
+  max-height: $mat-stepper-label-header-height;
 
-  .mat-stepper-index {
+  .mat-step-icon,
+  .mat-step-icon-not-touched {
     margin-right: $mat-vertical-stepper-content-margin - $mat-stepper-side-gap;
   }
 }
 
-.mat-stepper-horizontal-line {
-  border-top-width: $mat-stepper-line-width;
-  border-top-style: solid;
-  flex: auto;
-  height: 0;
-  margin: 0 $mat-stepper-line-gap - $mat-stepper-side-gap;
-  min-width: $mat-stepper-line-gap + $mat-stepper-side-gap;
-}
-
 .mat-horizontal-stepper-content {
   overflow: hidden;
 
diff --git a/src/lib/stepper/stepper.spec.ts b/src/lib/stepper/stepper.spec.ts
index 7e266674638a..ca3025b606fb 100644
--- a/src/lib/stepper/stepper.spec.ts
+++ b/src/lib/stepper/stepper.spec.ts
@@ -11,6 +11,8 @@ import {dispatchKeyboardEvent} from '@angular/cdk/testing';
 import {ENTER, LEFT_ARROW, RIGHT_ARROW, SPACE} from '@angular/cdk/keycodes';
 import {MdStepper} from './stepper';
 
+const EMAIL_REGEX = /^[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/;
+
 describe('MdHorizontalStepper', () => {
   beforeEach(async(() => {
     TestBed.configureTestingModule({
@@ -42,7 +44,7 @@ describe('MdHorizontalStepper', () => {
 
     it('should change selected index on header click', () => {
       let stepHeaders = fixture.debugElement.queryAll(By.css('.mat-horizontal-stepper-header'));
-      checkSelectionChangeOnHeaderClick(stepperComponent, fixture, stepHeaders);
+      assertSelectionChangeOnHeaderClick(stepperComponent, fixture, stepHeaders);
     });
 
     it('should set the "tablist" role on stepper', () => {
@@ -52,34 +54,44 @@ describe('MdHorizontalStepper', () => {
 
     it('should set aria-expanded of content correctly', () => {
       let stepContents = fixture.debugElement.queryAll(By.css(`.mat-horizontal-stepper-content`));
-      checkExpandedContent(stepperComponent, fixture, stepContents);
+      assertCorrectAriaExpandedAttribute(stepperComponent, fixture, stepContents);
     });
 
     it('should display the correct label', () => {
-      checkCorrectLabel(stepperComponent, fixture);
+      assertCorrectStepLabel(stepperComponent, fixture);
     });
 
     it('should go to next available step when the next button is clicked', () => {
-      checkNextStepperButton(stepperComponent, fixture);
+      assertNextStepperButtonClick(stepperComponent, fixture);
     });
 
     it('should go to previous available step when the previous button is clicked', () => {
-      checkPreviousStepperButton(stepperComponent, fixture);
+      assertPreviousStepperButtonClick(stepperComponent, fixture);
     });
 
     it('should set the correct step position for animation', () => {
-      checkStepPosition(stepperComponent, fixture);
+      assertCorrectStepPosition(stepperComponent, fixture);
     });
 
     it('should support keyboard events to move and select focus', () => {
       let stepHeaders = fixture.debugElement.queryAll(By.css('.mat-horizontal-stepper-header'));
-      checkKeyboardEvent(stepperComponent, fixture, stepHeaders);
+      assertCorrectKeyboardInteraction(stepperComponent, fixture, stepHeaders);
     });
 
     it('should not set focus on header of selected step if header is not clicked', () => {
-      let stepHeaderEl = fixture.debugElement
-          .queryAll(By.css('.mat-horizontal-stepper-header'))[1].nativeElement;
-      checkStepHeaderFocusNotCalled(stepHeaderEl, stepperComponent, fixture);
+      assertStepHeaderFocusNotCalled(stepperComponent, fixture);
+    });
+
+    it('should only be able to return to a previous step if it is editable', () => {
+      assertEditableStepChange(stepperComponent, fixture);
+    });
+
+    it('should set create icon if step is editable and completed', () => {
+      assertCorrectStepIcon(stepperComponent, fixture, true, 'edit');
+    });
+
+    it('should set done icon if step is not editable and is completed', () => {
+      assertCorrectStepIcon(stepperComponent, fixture, false, 'done');
     });
   });
 
@@ -109,13 +121,15 @@ describe('MdHorizontalStepper', () => {
 
       let stepHeaderEl = fixture.debugElement
           .queryAll(By.css('.mat-horizontal-stepper-header'))[1].nativeElement;
-      checkLinearStepperValidity(stepHeaderEl, stepperComponent, testComponent, fixture);
+      assertLinearStepperValidity(stepHeaderEl, stepperComponent, testComponent, fixture);
     });
 
     it('should not focus step header upon click if it is not able to be selected', () => {
-      let stepHeaderEl = fixture.debugElement
-          .queryAll(By.css('.mat-horizontal-stepper-header'))[1].nativeElement;
-      checkStepHeaderBlur(stepHeaderEl, fixture);
+      assertStepHeaderBlurred(fixture);
+    });
+
+    it('should be able to move to next step even when invalid if current step is optional', () => {
+      assertOptionalStepValidity(stepperComponent, testComponent, fixture);
     });
   });
 });
@@ -151,7 +165,7 @@ describe('MdVerticalStepper', () => {
 
     it('should change selected index on header click', () => {
       let stepHeaders = fixture.debugElement.queryAll(By.css('.mat-vertical-stepper-header'));
-      checkSelectionChangeOnHeaderClick(stepperComponent, fixture, stepHeaders);
+      assertSelectionChangeOnHeaderClick(stepperComponent, fixture, stepHeaders);
 
     });
 
@@ -162,34 +176,44 @@ describe('MdVerticalStepper', () => {
 
     it('should set aria-expanded of content correctly', () => {
       let stepContents = fixture.debugElement.queryAll(By.css(`.mat-vertical-stepper-content`));
-      checkExpandedContent(stepperComponent, fixture, stepContents);
+      assertCorrectAriaExpandedAttribute(stepperComponent, fixture, stepContents);
     });
 
     it('should display the correct label', () => {
-      checkCorrectLabel(stepperComponent, fixture);
+      assertCorrectStepLabel(stepperComponent, fixture);
     });
 
     it('should go to next available step when the next button is clicked', () => {
-      checkNextStepperButton(stepperComponent, fixture);
+      assertNextStepperButtonClick(stepperComponent, fixture);
     });
 
     it('should go to previous available step when the previous button is clicked', () => {
-      checkPreviousStepperButton(stepperComponent, fixture);
+      assertPreviousStepperButtonClick(stepperComponent, fixture);
     });
 
     it('should set the correct step position for animation', () => {
-      checkStepPosition(stepperComponent, fixture);
+      assertCorrectStepPosition(stepperComponent, fixture);
     });
 
     it('should support keyboard events to move and select focus', () => {
       let stepHeaders = fixture.debugElement.queryAll(By.css('.mat-vertical-stepper-header'));
-      checkKeyboardEvent(stepperComponent, fixture, stepHeaders);
+      assertCorrectKeyboardInteraction(stepperComponent, fixture, stepHeaders);
     });
 
     it('should not set focus on header of selected step if header is not clicked', () => {
-      let stepHeaderEl = fixture.debugElement
-          .queryAll(By.css('.mat-vertical-stepper-header'))[1].nativeElement;
-      checkStepHeaderFocusNotCalled(stepHeaderEl, stepperComponent, fixture);
+      assertStepHeaderFocusNotCalled(stepperComponent, fixture);
+    });
+
+    it('should only be able to return to a previous step if it is editable', () => {
+      assertEditableStepChange(stepperComponent, fixture);
+    });
+
+    it('should set create icon if step is editable and completed', () => {
+      assertCorrectStepIcon(stepperComponent, fixture, true, 'edit');
+    });
+
+    it('should set done icon if step is not editable and is completed', () => {
+      assertCorrectStepIcon(stepperComponent, fixture, false, 'done');
     });
   });
 
@@ -220,18 +244,21 @@ describe('MdVerticalStepper', () => {
       let stepHeaderEl = fixture.debugElement
           .queryAll(By.css('.mat-vertical-stepper-header'))[1].nativeElement;
 
-      checkLinearStepperValidity(stepHeaderEl, stepperComponent, testComponent, fixture);
+      assertLinearStepperValidity(stepHeaderEl, stepperComponent, testComponent, fixture);
     });
 
     it('should not focus step header upon click if it is not able to be selected', () => {
-      let stepHeaderEl = fixture.debugElement
-          .queryAll(By.css('.mat-vertical-stepper-header'))[1].nativeElement;
-      checkStepHeaderBlur(stepHeaderEl, fixture);
+      assertStepHeaderBlurred(fixture);
+    });
+
+    it('should be able to move to next step even when invalid if current step is optional', () => {
+      assertOptionalStepValidity(stepperComponent, testComponent, fixture);
     });
   });
 });
 
-function checkSelectionChangeOnHeaderClick(stepperComponent: MdStepper,
+/** Asserts that `selectedIndex` updates correctly when header of another step is clicked. */
+function assertSelectionChangeOnHeaderClick(stepperComponent: MdStepper,
                                            fixture: ComponentFixture<any>,
                                            stepHeaders: DebugElement[]) {
   expect(stepperComponent.selectedIndex).toBe(0);
@@ -251,7 +278,8 @@ function checkSelectionChangeOnHeaderClick(stepperComponent: MdStepper,
   expect(stepperComponent.selectedIndex).toBe(2);
 }
 
-function checkExpandedContent(stepperComponent: MdStepper,
+/** Asserts that 'aria-expanded' attribute is correct for expanded content of step. */
+function assertCorrectAriaExpandedAttribute(stepperComponent: MdStepper,
                               fixture: ComponentFixture<any>,
                               stepContents: DebugElement[]) {
   let firstStepContentEl = stepContents[0].nativeElement;
@@ -265,7 +293,8 @@ function checkExpandedContent(stepperComponent: MdStepper,
   expect(secondStepContentEl.getAttribute('aria-expanded')).toBe('true');
 }
 
-function checkCorrectLabel(stepperComponent: MdStepper, fixture: ComponentFixture<any>) {
+/** Asserts that step has correct label. */
+function assertCorrectStepLabel(stepperComponent: MdStepper, fixture: ComponentFixture<any>) {
   let selectedLabel = fixture.nativeElement.querySelector('[aria-selected="true"]');
   expect(selectedLabel.textContent).toMatch('Step 1');
 
@@ -282,7 +311,8 @@ function checkCorrectLabel(stepperComponent: MdStepper, fixture: ComponentFixtur
   expect(selectedLabel.textContent).toMatch('New Label');
 }
 
-function checkNextStepperButton(stepperComponent: MdStepper, fixture: ComponentFixture<any>) {
+/** Asserts that clicking on MdStepperNext button updates `selectedIndex` correctly. */
+function assertNextStepperButtonClick(stepperComponent: MdStepper, fixture: ComponentFixture<any>) {
   expect(stepperComponent.selectedIndex).toBe(0);
 
   let nextButtonNativeEl = fixture.debugElement
@@ -307,7 +337,9 @@ function checkNextStepperButton(stepperComponent: MdStepper, fixture: ComponentF
   expect(stepperComponent.selectedIndex).toBe(2);
 }
 
-function checkPreviousStepperButton(stepperComponent: MdStepper, fixture: ComponentFixture<any>) {
+/** Asserts that clicking on MdStepperPrevious button updates `selectedIndex` correctly. */
+function assertPreviousStepperButtonClick(stepperComponent: MdStepper,
+                                          fixture: ComponentFixture<any>) {
   expect(stepperComponent.selectedIndex).toBe(0);
 
   stepperComponent.selectedIndex = 2;
@@ -333,7 +365,8 @@ function checkPreviousStepperButton(stepperComponent: MdStepper, fixture: Compon
   expect(stepperComponent.selectedIndex).toBe(0);
 }
 
-function checkStepPosition(stepperComponent: MdStepper, fixture: ComponentFixture<any>) {
+/** Asserts that step position is correct for animation. */
+function assertCorrectStepPosition(stepperComponent: MdStepper, fixture: ComponentFixture<any>) {
   expect(stepperComponent._getAnimationDirection(0)).toBe('current');
   expect(stepperComponent._getAnimationDirection(1)).toBe('next');
   expect(stepperComponent._getAnimationDirection(2)).toBe('next');
@@ -353,7 +386,8 @@ function checkStepPosition(stepperComponent: MdStepper, fixture: ComponentFixtur
   expect(stepperComponent._getAnimationDirection(2)).toBe('current');
 }
 
-function checkKeyboardEvent(stepperComponent: MdStepper,
+/** Asserts that keyboard interaction works correctly. */
+function assertCorrectKeyboardInteraction(stepperComponent: MdStepper,
                             fixture: ComponentFixture<any>,
                             stepHeaders: DebugElement[]) {
   expect(stepperComponent._focusIndex).toBe(0);
@@ -411,9 +445,11 @@ function checkKeyboardEvent(stepperComponent: MdStepper,
           'Expected index of selected step to change to index of focused step after SPACE event.');
 }
 
-function checkStepHeaderFocusNotCalled(stepHeaderEl: HTMLElement,
-                                       stepperComponent: MdStepper,
-                                       fixture: ComponentFixture<any>) {
+/** Asserts that step selection change using stepper buttons does not focus step header. */
+function assertStepHeaderFocusNotCalled(stepperComponent: MdStepper,
+                                        fixture: ComponentFixture<any>) {
+  let stepHeaderEl = fixture.debugElement
+      .queryAll(By.css('md-step-header'))[1].nativeElement;
   let nextButtonNativeEl = fixture.debugElement
       .queryAll(By.directive(MdStepperNext))[0].nativeElement;
   spyOn(stepHeaderEl, 'focus');
@@ -424,7 +460,10 @@ function checkStepHeaderFocusNotCalled(stepHeaderEl: HTMLElement,
   expect(stepHeaderEl.focus).not.toHaveBeenCalled();
 }
 
-function checkLinearStepperValidity(stepHeaderEl: HTMLElement,
+/**
+ * Asserts that linear stepper does not allow step selection change if current step is not valid.
+ */
+function assertLinearStepperValidity(stepHeaderEl: HTMLElement,
                                     stepperComponent: MdStepper,
                                     testComponent:
                                         LinearMdHorizontalStepperApp | LinearMdVerticalStepperApp,
@@ -449,7 +488,10 @@ function checkLinearStepperValidity(stepHeaderEl: HTMLElement,
   expect(stepperComponent.selectedIndex).toBe(1);
 }
 
-function checkStepHeaderBlur(stepHeaderEl: HTMLElement, fixture: ComponentFixture<any>) {
+/** Asserts that step header focus is blurred if the step cannot be selected upon header click. */
+function assertStepHeaderBlurred(fixture: ComponentFixture<any>) {
+  let stepHeaderEl = fixture.debugElement
+      .queryAll(By.css('md-step-header'))[1].nativeElement;
   spyOn(stepHeaderEl, 'blur');
   stepHeaderEl.click();
   fixture.detectChanges();
@@ -457,6 +499,81 @@ function checkStepHeaderBlur(stepHeaderEl: HTMLElement, fixture: ComponentFixtur
   expect(stepHeaderEl.blur).toHaveBeenCalled();
 }
 
+/** Asserts that it is only possible to go back to a previous step if the step is editable. */
+function assertEditableStepChange(stepperComponent: MdStepper,
+                           fixture: ComponentFixture<any>) {
+  stepperComponent.selectedIndex = 1;
+  stepperComponent._steps.toArray()[0].editable = false;
+  let previousButtonNativeEl = fixture.debugElement
+      .queryAll(By.directive(MdStepperPrevious))[1].nativeElement;
+  previousButtonNativeEl.click();
+  fixture.detectChanges();
+
+  expect(stepperComponent.selectedIndex).toBe(1);
+
+  stepperComponent._steps.toArray()[0].editable = true;
+  previousButtonNativeEl.click();
+  fixture.detectChanges();
+
+  expect(stepperComponent.selectedIndex).toBe(0);
+}
+
+/**
+ * Asserts that it is possible to skip an optional step in linear stepper if there is no input
+ * or the input is valid.
+ */
+function assertOptionalStepValidity(stepperComponent: MdStepper,
+                           testComponent: LinearMdHorizontalStepperApp | LinearMdVerticalStepperApp,
+                           fixture: ComponentFixture<any>) {
+  testComponent.oneGroup.get('oneCtrl')!.setValue('input');
+  testComponent.twoGroup.get('twoCtrl')!.setValue('input');
+  stepperComponent.selectedIndex = 2;
+  fixture.detectChanges();
+
+  expect(stepperComponent.selectedIndex).toBe(2);
+  expect(testComponent.threeGroup.get('threeCtrl')!.valid).toBe(true);
+
+  let nextButtonNativeEl = fixture.debugElement
+      .queryAll(By.directive(MdStepperNext))[2].nativeElement;
+  nextButtonNativeEl.click();
+  fixture.detectChanges();
+
+  expect(stepperComponent.selectedIndex)
+      .toBe(3, 'Expected selectedIndex to change when optional step input is empty.');
+
+  stepperComponent.selectedIndex = 2;
+  testComponent.threeGroup.get('threeCtrl')!.setValue('input');
+  nextButtonNativeEl.click();
+  fixture.detectChanges();
+
+  expect(testComponent.threeGroup.get('threeCtrl')!.valid).toBe(false);
+  expect(stepperComponent.selectedIndex)
+      .toBe(2, 'Expected selectedIndex to remain unchanged when optional step input is invalid.');
+
+  testComponent.threeGroup.get('threeCtrl')!.setValue('123@gmail.com');
+  nextButtonNativeEl.click();
+  fixture.detectChanges();
+
+  expect(testComponent.threeGroup.get('threeCtrl')!.valid).toBe(true);
+  expect(stepperComponent.selectedIndex)
+      .toBe(3, 'Expected selectedIndex to change when optional step input is valid.');
+}
+
+/** Asserts that step header set the correct icon depending on the state of step. */
+function assertCorrectStepIcon(stepperComponent: MdStepper,
+                               fixture: ComponentFixture<any>,
+                               isEditable: boolean,
+                               icon: String) {
+  let nextButtonNativeEl = fixture.debugElement
+      .queryAll(By.directive(MdStepperNext))[0].nativeElement;
+  expect(stepperComponent._getIndicatorType(0)).toBe('number');
+  stepperComponent._steps.toArray()[0].editable = isEditable;
+  nextButtonNativeEl.click();
+  fixture.detectChanges();
+
+  expect(stepperComponent._getIndicatorType(0)).toBe(icon);
+}
+
 @Component({
   template: `
     <md-horizontal-stepper>
@@ -517,12 +634,28 @@ class SimpleMdHorizontalStepperApp {
           </div>
         </form>
       </md-step>
+      <md-step [stepControl]="threeGroup" optional>
+        <form [formGroup]="threeGroup">
+          <ng-template mdStepLabel>Step two</ng-template>
+          <md-form-field>
+            <input mdInput formControlName="threeCtrl">
+          </md-form-field>
+          <div>
+            <button md-button mdStepperPrevious>Back</button>
+            <button md-button mdStepperNext>Next</button>
+          </div>
+        </form>
+      </md-step>
+      <md-step>
+        Done
+      </md-step>
     </md-horizontal-stepper>
   `
 })
 class LinearMdHorizontalStepperApp {
   oneGroup: FormGroup;
   twoGroup: FormGroup;
+  threeGroup: FormGroup;
 
   ngOnInit() {
     this.oneGroup = new FormGroup({
@@ -531,6 +664,9 @@ class LinearMdHorizontalStepperApp {
     this.twoGroup = new FormGroup({
       twoCtrl: new FormControl('', Validators.required)
     });
+    this.threeGroup = new FormGroup({
+      threeCtrl: new FormControl('', Validators.pattern(EMAIL_REGEX))
+    });
   }
 }
 
@@ -594,12 +730,28 @@ class SimpleMdVerticalStepperApp {
           </div>
         </form>
       </md-step>
+      <md-step [stepControl]="threeGroup" optional>
+        <form [formGroup]="threeGroup">
+          <ng-template mdStepLabel>Step two</ng-template>
+          <md-form-field>
+            <input mdInput formControlName="threeCtrl">
+          </md-form-field>
+          <div>
+            <button md-button mdStepperPrevious>Back</button>
+            <button md-button mdStepperNext>Next</button>
+          </div>
+        </form>
+      </md-step>
+      <md-step>
+        Done
+      </md-step>
     </md-vertical-stepper>
   `
 })
 class LinearMdVerticalStepperApp {
   oneGroup: FormGroup;
   twoGroup: FormGroup;
+  threeGroup: FormGroup;
 
   ngOnInit() {
     this.oneGroup = new FormGroup({
@@ -608,5 +760,8 @@ class LinearMdVerticalStepperApp {
     this.twoGroup = new FormGroup({
       twoCtrl: new FormControl('', Validators.required)
     });
+    this.threeGroup = new FormGroup({
+      threeCtrl: new FormControl('', Validators.pattern(EMAIL_REGEX))
+    });
   }
 }
diff --git a/src/lib/stepper/stepper.ts b/src/lib/stepper/stepper.ts
index 5a0dd890fa89..9492a4a32a8d 100644
--- a/src/lib/stepper/stepper.ts
+++ b/src/lib/stepper/stepper.ts
@@ -19,8 +19,9 @@ import {
   Optional,
   QueryList,
   SkipSelf,
-  ViewChildren
-}from '@angular/core';
+  ViewChildren,
+  ViewEncapsulation
+} from '@angular/core';
 import {MdStepLabel} from './step-label';
 import {
   defaultErrorStateMatcher,
@@ -29,12 +30,14 @@ import {
   ErrorStateMatcher
 } from '../core/error/error-options';
 import {FormControl, FormGroupDirective, NgForm} from '@angular/forms';
+import {MdStepHeader} from './step-header';
 
 @Component({
   moduleId: module.id,
   selector: 'md-step, mat-step',
   templateUrl: 'step.html',
-  providers: [{provide: MD_ERROR_GLOBAL_OPTIONS, useExisting: MdStep}]
+  providers: [{provide: MD_ERROR_GLOBAL_OPTIONS, useExisting: MdStep}],
+  encapsulation: ViewEncapsulation.None
 })
 export class MdStep extends CdkStep implements ErrorOptions {
   /** Content for step label given by <ng-template matStepLabel> or <ng-template mdStepLabel>. */
@@ -66,7 +69,7 @@ export class MdStep extends CdkStep implements ErrorOptions {
 
 export class MdStepper extends CdkStepper implements ErrorOptions {
   /** The list of step headers of the steps in the stepper. */
-  @ViewChildren('stepHeader') _stepHeader: QueryList<ElementRef>;
+  @ViewChildren(MdStepHeader, {read: ElementRef}) _stepHeader: QueryList<ElementRef>;
 
   /** Steps that the stepper holds. */
   @ContentChildren(MdStep) _steps: QueryList<MdStep>;