polyfiddle

Polymer boilerplate for jsFiddle

HTML

<script>
 Polymer = {
   dom: 'shadow',
   lazyRegister: true,
   suppressBindingNotifications: true,
   suppressTemplateNotifications: true,
   useNativeCSSProperties: true,
};
</script>
<base href="https://rawcdn.githack.com/StartPolymer/cdn/v1.11.0/components/">
<link href="polymer/polymer.html" rel="import">

<dom-module id="cr-toggle">
  <template>
    <style>
      :host {
        cursor: pointer;
        display: block;
        outline: none;
      }

      :host([disabled]) {
        cursor: initial;
        pointer-events: none;
      }

      #container {
        position: relative;
        width: 34px;
        z-index: 0;
      }

      #bar {
        background-color: black;
        border-radius: 8px;
        height: 12px;
        left: 3px;
        opacity: 0.4;
        position: absolute;
        top: 2px;
        transition: background-color linear 80ms;
        width: 28px;
        z-index: 0;
      }

      :host([checked]) #bar {
        background-color: blue;
      }

      :host([disabled]) #bar {
        background-color: black;
        opacity: 0.12;
      }

      #button {
        background-color: lightgray;
        border-radius: 50%;
        box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.4);
        height: 16px;
        position: relative;
        transition: transform linear 80ms, background-color linear 80ms;
        width: 16px;
        z-index: 1;
      }

      :host([checked]) #button {
        background-color: red;
        transform: translate3d(18px, 0, 0);
      }

      :host-context([dir=rtl]):host([checked]) #button {
        transform: translate3d(-18px, 0, 0);
      }

      :host([disabled]) #button {
        background-color: #bdbdbd;
        opacity: 1;
      }
    </style>
    <div id="container">
     <div id="bar"></div>
     <div id="button"></div>
    </div>
  </template>
</dom-module>                                                                         

Use-case requirements
<ul>
 <li>Switch can be toggled by...

CSS

body {
  font-family: sans-serif;
  margin: 0;
}

.clickable-row {
  display: flex;
  margin: 20px;
  padding: 5px;
  background-color: yellow;
  cursor: pointer;
  user-select: none;
}

.clickable-row span {
  flex: 1;
}

JavaScript

Polymer({
  is: 'cr-toggle',

  properties: {
    checked: {
      type: Boolean,
      value: false,
      reflectToAttribute: true,
      observer: 'checkedChanged_',
      notify: true,
    },

    disabled: {
      type: Boolean,
      value: false,
      reflectToAttribute: true,
      observer: 'disabledChanged_',
    },
  },

  hostAttributes: {
    'aria-disabled': 'false',
    'aria-pressed': 'false',
    role: 'button',
    tabindex: 0,
  },

  listeners: {
    'pointerdown': 'onPointerDown_',
    'pointerup': 'onPointerUp_',
    'click': 'onTap_',
    'keypress': 'onKeyPress_',
  },

  /** @private {?Function} */
  boundPointerMove_: null,

  /**
   * Number of pixels required to move to consider the pointermove event as
   * intentional.
   * @type {number}
   */
  MOVE_THRESHOLD_PX: 5,

  /**
   * Whether the state of the toggle has already taken into account by
   * |pointeremove| handlers. Used in the 'tap' handler.
   * @private {boolean}
   */
  handledInPointerMove_: false,

  /** @private {number} */
  lastPointerUpTime_: 0,

  /** @override */
  attached: function() {
    let direction = this.matches(':host-context([dir=rtl]) cr-toggle') ? -1 : 1;

    this.boundPointerMove_ = (e) => {
      // Prevent unwanted text selection to occur while moving the pointer, this
      // is important.
      e.preventDefault();

      let diff = e.clientX - this.pointerDownX_;
      if (Math.abs(diff) < this.MOVE_THRESHOLD_PX)
        return;

      this.handledInPointerMove_ = true;

      let shouldToggle = (diff * direction < 0 && this.checked) ||
          (diff * direction > 0 && !this.checked);
      if (shouldToggle)
        this.toggleState_();
    };
  },

  /** @private */
  checkedChanged_: function() {
    this.setAttribute('aria-pressed', this.checked ? 'true' : 'false');
  },

  /** @private */
  disabledChanged_: function() {
    this.setAttribute('tabindex', this.disabled ? -1 : 0);
    this.setAttribute('aria-disabled', this.disabled ?...