Samples using WinJS and Polymer
JavaScript CSS
Switch branches/tags
Nothing to show
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Failed to load latest commit information.


Web Components is a set of specs which let web developers leverage their HTML, CSS and JavaScript knowledge to build widgets that can be reused easily and reliably.

Polymer ( is a library that uses the latest web technologies to let you create custom HTML elements. Its philosophy is "everything is an element". It divides its custom elements into two categories: UI elements (e.g. core-list, select) and Non UI elements (e.g. script, style, core-ajax). The lowest layer of Polymer is platform.js: a collection of libraries (or “polyfills”) for new web technologies that haven’t shipped yet across all browsers. Platform makes it possible for developers to use these standards today across all modern browsers. As these technologies are implemented in browsers, the polyfills will shrink and you’ll gain the benefits of native implementations. Platform.js automatically detects native support and switches to the fast path when available. Elements seamlessly start relying on the native stuff–and get faster in the process.

The purpose of this repo is to experiment creating web components based on existing WinJS controls, compare the development experience, and identify gaps in the current state of the specs.

We start by building a <winjs-pivot> web component. Refer to ( for the source code of the existing WinJS Pivot control. The Pivot control creates a tab control that displays multiple items, e.g:

Installation Steps

Clone a copy of the master winjs-polymer-samples git repo:

git clone

Change to the winjs-polymer-samples directory:

cd winjs-polymer-samples

Install the grunt command-line interface globally:

npm install -g grunt-cli

Note: You may need to use sudo (for OSX, *nix, BSD etc) or run your command shell as Administrator (for Windows) to install Grunt globally.

Grunt dependencies are installed separately in each cloned git repo. Install the dependencies with:

npm install

Install bower

npm install -g bower

Note: You may need to use sudo (for OSX, *nix, BSD etc) or run your command shell as Administrator (for Windows) to install Bower globally.

Update bower dependencies for the current project

bower update

Run the following and the WinJS JavaScript and CSS files will be put in the bin directory:

grunt test


![winjs-pivot] (

Demo Details

Using <winjs-pivot>

<!-- HTML imports in <head> -->
<link rel="import" href="elements/winjs-pivot.html"/>
<link rel="import" href="elements/winjs-pivot-item.html"/>
<link rel="import" href="bower_components/core-list/core-list.html">

<!-- using <winjs-pivot> in the <body> -->
<winjs-pivot title="INBOX - EMAIL" selectedIndex="0">
  <winjs-pivot-item header="all">
    <!-- core-list is polymer's virtualized list web component -->
    <core-list id="list" data="{{data}}" height="100">
      <template repeat>
        <div class="item">
          <div class="message">
            <div class="from">{{name}}</div>
            <div class="subject win-pivot-slide1">{{subject}}</div>
            <div class="details win-pivot-slide2">{{details}}</div>

  <winjs-pivot-item header="unread">
    unread content here

  <winjs-pivot-item header="flagged">
    flagged content here

  <winjs-pivot-item header="urgent">
      <!-- WinJS.UI.ListView is WinJS's virtualized list control -->
    <div class="urgentLV" data-win-control="WinJS.UI.ListView" data-win-options="{ itemDataSource: Urgent.dataSource, layout: { type: WinJS.UI.ListLayout }, itemTemplate: select('.mailItemTemplate'), selectionMode: 'none' }"></div>


This particular markup creates a Pivot with 4 tabs simulating a mail app:

  • all: which displays messages in a <core-list> web components (polymer's virtualized list)
  • unread
  • flagged
  • urgent: which display messages in a WinJS.UI.ListView

DOM visualization (in Chrome Canary)

<winjs-pivot> becomes a shadow host when Polymer calls internally createShadowRoot. Since the element has shadow DOM, its children are not rendered; the content of the shadow DOM is rendered instead.

![winjs-pivot shadow-dom] (

every <winjs-pivot-item> has its own shadow root, and depending on what it contains (e.g. <core-list> web component), it could have nested shadow roots.

![winjs-pivot shadow-dom2] (

<winjs-pivot> definition with Polymer

<link rel="import" href="../bower_components/polymer/polymer.html">

<polymer-element name="winjs-pivot" constructor="Pivot">
    <link rel="stylesheet" href="winjs-pivot.css">

    <div class="win-pivot-title">{{title}}</div>
    <div class="win-pivot-headers"></div>
    <div class="win-pivot-viewport" role="group">
      <div class="win-pivot-surface">
  <script src="winjs-pivot.js"></script>

The content of the <template> element is parsed by the parser, but it is inert: scripts aren't processed, images aren't downloaded, and so on. The <template> element is not rendered.

The element allows selecting nodes from the Light DOM and render them at predefined locations in the Shadow DOM.

Distributed nodes are elements that render at the insertion point ( element), in the main sample, it would be all the <win-pivot-item>.

winjs-pivot.js is defined as:

(function() {


  // Tab control which displays an item of content.
  Polymer('winjs-pivot', {

    created: function() {
      console.log("created winjs-pivot");

    ready: function() {
      console.log("ready winjs-pivot");

      this.setAttribute('role', 'tablist');
      // Initialization code here

    attached: function() {
      console.log("attached winjs-pivot");

    domReady: function() {
      console.log("domReady winjs-pivot");

    detached: function() {
      console.log("detached winjs-pivot");

    attributeChanged: function(attrName, oldVal, newVal) {
      console.log("attributeChanged winjs-pivot: " + attrName, 'old: ' + oldVal, 'new:', newVal);

    // Gets or sets the index of the item in view.
    get selectedIndex() {
      if (this._items.length === 0) {
          return -1;

      if (+this._pendingIndexOnScreen === this._pendingIndexOnScreen) {
          return this._pendingIndexOnScreen;

      return this._currentIndexOnScreen;

    set selectedIndex(value) {
      if (value >= 0 && value < this._items.length) {
          if (this._pendingRefresh) {
              this._pendingIndexOnScreen = value;
          } else {
              this._navMode = this._navMode || navigationModes.api;


<winjs-pivot-item> is defined as

<link rel="import" href="../bower_components/polymer/polymer.html">

<polymer-element name="winjs-pivot-item" constructor="PivotItem" attributes="header">
    <link rel="stylesheet" href="winjs-pivot-item.css">

    <div class="win-pivot-item-content">
  <script src="winjs-pivot-item.js"></script>

Notice that the distributed nodes are elements that render at the insertion point ( element), in the main sample, it would be <core-list> inside the first <win-pivot-item>.

Styling Summary (style encapsulation)

  • One of the core features of Shadow DOM is the shadow boundary. It has a lot of nice properties, but one of the best is that it provides style encapsulation for free. Other styles rules defined on the page that target elements (e.g h3) don't bleed into my content. That's because selectors don't cross the shadow boundary.

  • The :host allows you to select and style the element hosting a shadow tree

  • If an element has at least one shadow tree, the ::shadow pseudo-element matches the shadow root itself.

    #myEl::shadow span {
      color: red;

    styles all of the spans within its shadow tree. Supported with querySelector:

    document.querySelector('x-tabs::shadow x-panel::shadow #foo');

    so that you don't have to

  • The /deep/ combinator is similar to ::shadow, but more powerful. It completely ignores all shadow boundaries and crosses into any number of shadow trees (useful in the world of Custom Elements where it's common to have multiple levels of Shadow DOM)

    x-tabs /deep/ x-panel {

    select all elements that are descendants of

  • /deep/ is also useful to style native elements (make the input[type="range"] inside the control's pink, instead of rolling your own.

  • Distributed nodes (elements that render at an insertion point (a <content> element)) retain styles from the main document. They are still logically in the light dom and don't move. They just render elsewhere

  • Distributed nodes are children of the host element, so how can we target them from within the Shadow DOM? The answer is the CSS ::content pseudo element. It's a way to target Light DOM nodes that pass through an insertion point. For example: ::content > h3 styles any h3 tags that pass through an insertion point.

  • Eric Bidelman answered something I was wondering: Do the ::shadow pseudo-element and /deep/ combinator defeat the purpose of style encapsulation? Out of the box, Shadow DOM prevents accidental styling from outsiders but it never promises to be a bullet proof vest. Developers should be allowed to intentionally style inner parts of your Shadow tree...if they know what they're doing. Having more control is also good for flexibility, theming, and the re-usability of your elements.

Issues Discussed


Recommended Reading