diff --git a/components-chromium/paper-tooltip/paper-tooltip.js b/components-chromium/paper-tooltip/paper-tooltip.js
index 853eee1990258..1b36aa4c38b3f 100644
--- a/components-chromium/paper-tooltip/paper-tooltip.js
+++ b/components-chromium/paper-tooltip/paper-tooltip.js
@@ -42,12 +42,14 @@ Custom property | Description | Default
 `--paper-tooltip-background` | The background color of the tooltip | `#616161`
 `--paper-tooltip-opacity` | The opacity of the tooltip | `0.9`
 `--paper-tooltip-text-color` | The text color of the tooltip | `white`
-`--paper-tooltip` | Mixin applied to the tooltip | `{}`
 `--paper-tooltip-delay-in` | Delay before tooltip starts to fade in | `500`
 `--paper-tooltip-delay-out` | Delay before tooltip starts to fade out | `0`
 `--paper-tooltip-duration-in` | Timing for animation when showing tooltip | `500`
 `--paper-tooltip-duration-out` | Timing for animation when hiding tooltip | `0`
-`--paper-tooltip-animation` | Mixin applied to the tooltip animation | `{}`
+
+Also prefer using the exposed CSS part as follows where possible
+paper-tooltip::part(tooltip) {...}
+
 @group Paper Elements
 @element paper-tooltip
 @demo demo/index.html
@@ -67,14 +69,12 @@ Polymer({
       #tooltip {
         display: block;
         outline: none;
-        @apply --paper-font-common-base;
         font-size: 10px;
         line-height: 1;
         background-color: var(--paper-tooltip-background, #616161);
         color: var(--paper-tooltip-text-color, white);
         padding: 8px;
         border-radius: 2px;
-        @apply --paper-tooltip;
       }
 
       @keyframes keyFrameScaleUp {
@@ -149,7 +149,6 @@ Polymer({
         animation-timing-function: ease-in;
         animation-duration: var(--paper-tooltip-duration-in, 500ms);
         animation-fill-mode: forwards;
-        @apply --paper-tooltip-animation;
       }
 
       .fade-out-animation {
@@ -160,7 +159,6 @@ Polymer({
         animation-timing-function: ease-in;
         animation-duration: var(--paper-tooltip-duration-out, 500ms);
         animation-fill-mode: forwards;
-        @apply --paper-tooltip-animation;
       }
 
       .scale-up-animation {
@@ -172,7 +170,6 @@ Polymer({
         animation-timing-function: ease-in;
         animation-duration: var(--paper-tooltip-duration-in, 500ms);
         animation-fill-mode: forwards;
-        @apply --paper-tooltip-animation;
       }
 
       .scale-down-animation {
@@ -184,7 +181,6 @@ Polymer({
         animation-timing-function: ease-in;
         animation-duration: var(--paper-tooltip-duration-out, 500ms);
         animation-fill-mode: forwards;
-        @apply --paper-tooltip-animation;
       }
 
       .slide-down-animation {
@@ -196,7 +192,6 @@ Polymer({
         animation-timing-function: cubic-bezier(0.0, 0.0, 0.2, 1);
         animation-duration: var(--paper-tooltip-duration-out, 500ms);
         animation-fill-mode: forwards;
-        @apply --paper-tooltip-animation;
       }
 
       .slide-down-animation-out {
@@ -208,7 +203,6 @@ Polymer({
         animation-timing-function: cubic-bezier(0.4, 0.0, 1, 1);
         animation-duration: var(--paper-tooltip-duration-out, 500ms);
         animation-fill-mode: forwards;
-        @apply --paper-tooltip-animation;
       }
 
       .cancel-animation {
@@ -222,7 +216,7 @@ Polymer({
       }
     </style>
 
-    <div id="tooltip" class="hidden">
+    <div id="tooltip" class="hidden" part="tooltip">
       <slot></slot>
     </div>
 `,
@@ -282,8 +276,8 @@ Polymer({
      */
     animationExit: {type: String, value: ''},
     /**
-     * This property is deprecated.  Use --paper-tooltip-animation to change the
-     * animation. The entry and exit animations that will be played when showing
+     * This property is deprecated.
+     * The entry and exit animations that will be played when showing
      * and hiding the tooltip. If you want to override this, you must ensure
      * that your animationConfig has the exact format below.
      * @deprecated since version
@@ -311,12 +305,16 @@ Polymer({
 
   /**
    * Returns the target element that this tooltip is anchored to. It is
-   * either the element given by the `for` attribute, or the immediate parent
-   * of the tooltip.
+   * either the element given by the `for` attribute, the element manually
+   * specified through the `target` attribute, or the immediate parent of
+   * the tooltip.
    *
    * @type {Node}
    */
   get target() {
+    if (this._manualTarget)
+      return this._manualTarget;
+
     var parentNode = dom(this).parentNode;
     // If the parentNode is a document fragment, then we need to use the host.
     var ownerRoot = dom(this).getOwnerRoot();
@@ -331,6 +329,15 @@ Polymer({
     return target;
   },
 
+  /**
+   * Sets the target element that this tooltip will be anchored to.
+   * @param {Node} target
+   */
+  set target(target) {
+    this._manualTarget = target;
+    this._findTarget();
+  },
+
   /**
    * @return {void}
    */
@@ -429,13 +436,16 @@ Polymer({
    * @return {void}
    */
   updatePosition: function() {
-    if (!this._target || !this.offsetParent)
+    if (!this._target)
+      return;
+    var offsetParent = this._composedOffsetParent();
+    if (!offsetParent)
       return;
     var offset = this.offset;
     // If a marginTop has been provided by the user (pre 1.0.3), use it.
     if (this.marginTop != 14 && this.offset == 14)
       offset = this.marginTop;
-    var parentRect = this.offsetParent.getBoundingClientRect();
+    var parentRect = offsetParent.getBoundingClientRect();
     var targetRect = this._target.getBoundingClientRect();
     var thisRect = this.getBoundingClientRect();
     var horizontalCenterOffset = (targetRect.width - thisRect.width) / 2;
@@ -581,5 +591,45 @@ Polymer({
     }
     this.unlisten(this.$.tooltip, 'animationend', '_onAnimationEnd');
     this.unlisten(this, 'mouseenter', 'hide');
+  },
+
+  /**
+   * Polyfills the old offsetParent behavior from before the spec was changed:
+   * https://github.com/w3c/csswg-drafts/issues/159
+   */
+  _composedOffsetParent: function() {
+    // Do an initial walk to check for display:none ancestors.
+    for (let ancestor = this; ancestor; ancestor = flatTreeParent(ancestor)) {
+      if (!(ancestor instanceof Element))
+        continue;
+      if (getComputedStyle(ancestor).display === 'none')
+        return null;
+    }
+
+    for (let ancestor = flatTreeParent(this); ancestor; ancestor = flatTreeParent(ancestor)) {
+      if (!(ancestor instanceof Element))
+        continue;
+      const style = getComputedStyle(ancestor);
+      if (style.display === 'contents') {
+        // display:contents nodes aren't in the layout tree so they should be skipped.
+        continue;
+      }
+      if (style.position !== 'static') {
+        return ancestor;
+      }
+      if (ancestor.tagName === 'BODY')
+        return ancestor;
+    }
+    return null;
+
+    function flatTreeParent(element) {
+      if (element.assignedSlot) {
+        return element.assignedSlot;
+      }
+      if (element.parentNode instanceof ShadowRoot) {
+        return element.parentNode.host;
+      }
+      return element.parentNode;
+    }
   }
 });
